实现像 Louis Vuitton 应用程序这样的项目列表视图的自定义水平滚动

Implementing custom horizontal scroll of listview of items like Louis Vuitton app

最近下载了Louis Vuitton App. I found a strange type of horizontal scroll of product items in listview. I tried card_swiper包,但是无法通过。我怎样才能实现下面gif中的滚动?

这里的技巧是使用堆栈并且:

  • 使用页面视图显示除第一个元素之外的所有元素
  • 使用左对齐 FractionallySizedBox 显示第一项并随着第一项偏移量增长

我尝试了几次,但结果非常令人满意,我会让你添加袋子,但这里你使用彩色盒子 ;) :

import 'dart:math';

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(home: FunList()));
}

class FunList extends StatefulWidget {
  @override
  State<FunList> createState() => _FunListState();
}

class _FunListState extends State<FunList> {
  /// The colors of the items in the list
  final _itemsColors = List.generate(
    100,
        (index) => Color((Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0),
  );
  
  /// The current page of the page view
  double _page = 0;

  /// The index of the leftmost element of the list to be displayed
  int get _firstItemIndex => _page.toInt();

  /// The offset of the leftmost element of the list to be displayed
  double get _firstItemOffset => _controller.hasClients ? 1 - (_page % 1) : 1;

  /// Controller to get the current position of the page view
  final _controller = PageController(
    viewportFraction: 0.25,
  );

  /// The width of a single item
  late final _itemWidth = MediaQuery.of(context).size.width * _controller.viewportFraction;

  @override
  void initState() {
    super.initState();

    _controller.addListener(() => setState(() {
      _page = _controller.page!;
    }));
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {

    return Center(
      child: Stack(
        children: [
          Positioned.fill(
            child: Align(
              alignment: Alignment.centerLeft,
              child: SizedBox(
                width: _itemWidth,
                child: FractionallySizedBox(
                  widthFactor: _firstItemOffset,
                  heightFactor: _firstItemOffset,
                  child: PageViewItem(color: _itemsColors[_firstItemIndex]),
                ),
              ),
            ),
          ),
          SizedBox(
            height: 200,
            child: PageView.builder(
              padEnds: false,
              controller: _controller,
              itemBuilder: (context, index) {
                return Opacity(
                  opacity: index <= _firstItemIndex ? 0 : 1,
                  child: PageViewItem(color: _itemsColors[index]),
                );
              },
              itemCount: _itemsColors.length,
            ),
          ),
        ],
      ),
    );
  }
}

class PageViewItem extends StatelessWidget {
  final Color color;

  const PageViewItem({
    Key? key,
    required this.color,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.all(10),
      color: color,
    );
  }
}