flutter

CustomScrollView

2022-07-12  本文已影响0人  乐狐
CustomScrollView.png
Sliver名称 功能 对应滚动组件
SliverList 列表 ListView
SliverFixedExtentList 项目高度固定列表 ListView 指定itemExtent
SliverPrototypeExtentList 根据原型生成高度固定的列表 ListView 指定prototypeItem
SliverAnimatedList 添加/删除列表项时执行动画 AnimatedList
SliverGrid 网格 GridView
SliverFillViewport 子组件可填满屏幕 PageView
Sliver名称 对应RenderBox
SliverAppBar AppBar
SliverPadding Padding
SliverToBoxAdapter 将 RenderBox 适配为 Sliver
SliverPersistentHeader 滑至顶部固定
SliverVisibility、SliverOpacity Visibility、Opacity
SliverFadeTransition FadeTransition
SliverLayoutBuilder LayoutBuilder
import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(
    debugShowCheckedModeBanner: false,
    home: SliverPersistentHeaderWidget(title: "测试"),
  ));
}

class CustomScrollViewWidget extends StatefulWidget {
  const CustomScrollViewWidget({Key? key}) : super(key: key);

  @override
  State<StatefulWidget> createState() => _CustomScrollViewWidgetState();
}

class _CustomScrollViewWidgetState extends State<CustomScrollViewWidget> {
  final List<String> _items = List<String>.generate(100, (i) => "Item1 $i");

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('CustomScrollView'),
      ),
      //两个滚动组件自己独立的 Scrollable、Viewport和Sliver
      body: Column(children: [
        Container(
          height: 80,
          decoration: const BoxDecoration(color: Colors.deepOrange),
          child: GridView.count(
            crossAxisCount: 5,
            childAspectRatio: 1.0,
            children: const [
              Icon(Icons.add),
              Icon(Icons.add),
              Icon(Icons.add),
              Icon(Icons.add),
              Icon(Icons.add),
              Icon(Icons.add),
              Icon(Icons.add),
              Icon(Icons.add),
            ],
          ),
        ),
        Expanded(
          child: ListView.builder(
            itemBuilder: (BuildContext context, int index) {
              return ListTile(title: Text('${_items[index]} abc'));
            },
            itemCount: _items.length,
          ),
        ),
      ]),
    );
  }
}

class CustomScrollView2Widget extends StatefulWidget {
  const CustomScrollView2Widget({Key? key}) : super(key: key);

  @override
  State<StatefulWidget> createState() => _CustomScrollView2WidgetState();
}

class _CustomScrollView2WidgetState extends State<CustomScrollView2Widget> {
  SliverFixedExtentList listSliver = SliverFixedExtentList(
    delegate: SliverChildBuilderDelegate((ctx, index) {
      return ListTile(
        title: Text("$index"),
      );
    }, childCount: 10),
    itemExtent: 56,
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('CustomScrollView'),
      ),
      body: CustomScrollView(
        slivers: [listSliver, listSliver],
      ),
    );
  }
}

class CustomScrollView3Widget extends StatelessWidget {
  const CustomScrollView3Widget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Material(
      child: CustomScrollView(
        slivers: [
          SliverAppBar(
            //滑动到顶端时会固定住
            //pinned: true,
            expandedHeight: 200.0,
            title: const Text('Demo'),
            flexibleSpace: FlexibleSpaceBar(
              background: Image.network(
                "https://upload.jianshu.io/admin_banners/web_images/5055/348f9e194f4062a17f587e2963b7feb0b0a5a982.png?imageMogr2/auto-orient/strip|imageView2/1/w/1250/h/540",
                fit: BoxFit.cover,
              ),
            ),
          ),
          //可单独滚动的组件
          SliverToBoxAdapter(
            child: Container(
              height: 100,
              decoration: const BoxDecoration(color: Colors.deepOrange),
              child: PageView(
                children: const [
                  Text("PageViewA 横向滑动"),
                  Text("PageViewB 横向滑动")
                ],
              ),
            ),
          ),
          SliverPadding(
            padding: const EdgeInsets.all(8.0),
            sliver: SliverGrid(
              //Grid
              gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                mainAxisSpacing: 10.0,
                crossAxisSpacing: 10.0,
                childAspectRatio: 4.0,
              ),
              delegate: SliverChildBuilderDelegate(
                (BuildContext context, int index) {
                  return Container(
                    alignment: Alignment.center,
                    color: Colors.cyan[100 * (index % 9)],
                    child: Text('grid item $index'),
                  );
                },
                childCount: 20,
              ),
            ),
          ),
          SliverFixedExtentList(
            itemExtent: 50.0,
            delegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                return Container(
                  alignment: Alignment.center,
                  color: Colors.lightBlue[100 * (index % 9)],
                  child: Text('list item $index'),
                );
              },
              childCount: 20,
            ),
          ),
        ],
      ),
    );
  }
}

class SliverPersistentHeaderWidget extends StatefulWidget {
  final String title;

  const SliverPersistentHeaderWidget({Key? key, required this.title})
      : super(key: key);

  @override
  State<SliverPersistentHeaderWidget> createState() =>
      SliverPersistentHeaderState();
}

class SliverPersistentHeaderState extends State<SliverPersistentHeaderWidget> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Material(
        child: CustomScrollView(
          slivers: [
            buildSliverList(),
            SliverPersistentHeader(
              pinned: true, //滑到顶部固定
              delegate: PersistentHeaderDelegate(
                maxHeight: 60,
                minHeight: 40,
                child: buildHeader(1),
              ),
            ),
            buildSliverList(),
            SliverPersistentHeader(
              pinned: true, //滑到顶部固定
              delegate: PersistentHeaderDelegate.fixedHeight(
                height: 40,
                child: buildHeader(2),
              ),
            ),
            buildSliverList(),
            SliverPersistentHeader(
              pinned: true, //滑到顶部固定
              delegate: PersistentHeaderDelegate.fixedHeight(
                height: 40,
                child: buildHeader(3),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget buildSliverList([int count = 18]) {
    return SliverFixedExtentList(
        delegate: SliverChildBuilderDelegate(
          (context, index) {
            return ListTile(title: Text('$index'));
          },
          childCount: count,
        ),
        itemExtent: 50);
  }

  Widget buildHeader(int i) {
    return Container(
      color: Colors.lightBlue.shade200,
      alignment: Alignment.centerLeft,
      child: Text(" PersistentHeader $i"),
    );
  }
}

typedef SliverHeaderBuilder = Widget Function(
    BuildContext context, double shrinkOffset, bool overlapsContent);

class PersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
  PersistentHeaderDelegate({
    required this.maxHeight,
    this.minHeight = 0,
    required Widget child,
  })  : builder = ((a, b, c) => child),
        assert(minHeight <= maxHeight && minHeight >= 0);

  //最大和最小高度相同
  PersistentHeaderDelegate.fixedHeight({
    required double height,
    required Widget child,
  })  : builder = ((a, b, c) => child),
        maxHeight = height,
        minHeight = height;

  //自定义builder时使用
  PersistentHeaderDelegate.builder({
    required this.maxHeight,
    this.minHeight = 0,
    required this.builder,
  });

  final double maxHeight;
  final double minHeight;
  final SliverHeaderBuilder builder;

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    Widget child = builder(context, shrinkOffset, overlapsContent);
    assert(() {
      if (child.key != null) {
        print('${child.key}: shrink: $shrinkOffset,overlaps:$overlapsContent');
      }
      return true;
    }());
    return SizedBox.expand(child: child);
  }

  @override
  double get maxExtent => maxHeight;

  @override
  double get minExtent => minHeight;

  @override
  bool shouldRebuild(covariant SliverPersistentHeaderDelegate old) {
    return old.maxExtent != maxExtent || old.minExtent != minExtent;
  }
}
上一篇下一篇

猜你喜欢

热点阅读