【Flutter从入门到实战】 ⑨、滚动的Widget-ListView、GridView、SliverWidget、滚动的Widget的滚动监听的方式

Flutter从入门到实战
一共分为23个系列
①(Flutter、Dart环境搭建篇) 共3个内容 已更新
②(Dart语法1 篇) 共4个内容 已更新
③(Dart语法2 篇) 共2个内容 已更新
④(Flutter案例开发篇) 共4个内容 已更新
⑤(Flutter的StatelessWidget 共3个内容 已更新
⑥(Flutter的基础Widget篇) 共2个内容 已更新
⑦(布局Widget篇) 共1个内容 已更新
⑧(Flex、Row、Column以及Flexible、Stack篇) 共1个内容 已更新
⑨(滚动的Widget篇) 共4个内容 已更新
⑩(Dart的Future和网络篇) 共3个内容 已更新
⑪(豆瓣案例-1篇) 共3个内容 已更新
⑫(豆瓣案例-2篇) 共3个内容 已更新

官方文档说明

官方视频教程
Flutter的YouTube视频教程-小部件

请添加图片描述


⑨、滚动的Widget篇

①、ListView

Flutter的List官方文档

1.ListView的创建几种方式

官方文档说明的创建几种方式
请添加图片描述

1. List()
2. List.generate(100, (index) {
    
    
          return Text("Hello world");
        })
3.         

1.1 ListView.generate 以及 ListTile 结合使用 (如果创建100个 那么Flutter会一次性创建100个)

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

  @override
  Widget build(BuildContext context) {
    
    
    return ListView(
      // 滚动方式
      scrollDirection: Axis.horizontal,
      // 每个item的宽度
      itemExtent: 200,
      // 反向属性 true 就0在最右侧 https://api.flutter.dev/flutter/widgets/ScrollView/reverse.html
      // reverse: true,
      // 
      // primary: ,
      children: List.generate(100, (index) {
    
    
        return ListTile(
          leading: Icon(Icons.people),
          trailing: Icon(Icons.delete),
          title: Text("联系人 ${index}"),
          subtitle: Text("联系人 电话号码 185100000123"),
        );
      }),
    );
  }
}

1.1 效果图 - ListView.generate 以及 ListTile 结合使用

请添加图片描述

1.2 ⭐️ListView.builder 需要展示的时候 才去加载

// 创建方式2: ListViewBuilder 比较常用一些
class ListViewBuilderWidget extends StatelessWidget {
    
    
  const ListViewBuilderWidget({
    
    
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    
    
    return ListView.builder(
      // 可能展示100个
      itemCount: 100,
      //
      itemExtent: 60,
      itemBuilder: (BuildContext ctx,int index){
    
    
        return Text("hello world ${index}",style: TextStyle(fontSize: 20));
      },
    );
  }
}

1.3 ListView.separated 分割线

// 分割线 
// 会自适应高度
class ListSeparatedDemoWidget extends StatelessWidget {
    
    
  const ListSeparatedDemoWidget({
    
    
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    
    
    return ListView.separated(
        itemCount: 100,
      itemBuilder: (BuildContext ctx,int index){
    
    
    return Text("hello world ${index} ",style: TextStyle(fontSize: 20));
    },
        // 分割线
      separatorBuilder: (BuildContext ctx,int index) {
    
    
          // Divider 线
        //   height 是占据整个区域有多高
        //   indent 开始的间距
        //   endIndent 尾部间距
        //  thickness 线的整体高度
          return Divider(color: Colors.red,height: 100,indent: 20,endIndent: 30,thickness: 20,);
      },
    );
  }
}

1.3 效果图 - ListView.separated 分割线

请添加图片描述


②、GridView

Flutter的GridView官方文档

1.1 GridView的使用 SliverGridDelegateWithFixedCrossAxisCount

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

  @override
  Widget build(BuildContext context) {
    
    
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 8,vertical: 8),
      child: GridView(
        //
        /**
         * SliverGridDelegateWithFixedCrossAxisCount
         * 在交叉轴上创建具有固定数量的图块的网格布局。
            例如,如果网格是垂直的,则此委托将创建具有固定列数的布局。如果网格是水平的,则此委托将创建具有固定行数的布局。
            此委托创建具有相同大小和间距的图块的网格。
         * */
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            // 交叉轴 返回多少个
            crossAxisCount: 3,
            // 宽度 / 高度 的比例 是1.5
            childAspectRatio: 1.5,
            // 交叉轴的间距
            crossAxisSpacing: 8,
            // 主轴的间距
            mainAxisSpacing: 8
        ),
        children: List.generate(100, (index){
    
    
          return Container(
            color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
          );
        }),
      ),
    );
  }
}

1.2 GridView的使用 SliverGridDelegateWithMaxCrossAxisExtent

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

  @override
  Widget build(BuildContext context) {
    
    
    return Padding(
      padding: EdgeInsets.symmetric(horizontal: 0),
      child: GridView(
        gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
          // maxCrossAxisExtent 一个item最大的宽度
          maxCrossAxisExtent: 400,
            childAspectRatio: 1.5,
            crossAxisSpacing: 8,
            mainAxisSpacing: 8
        ),
        children: List.generate(100, (index){
    
    
          return Container(
            color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
          );
        }),
      ),
    );
  }
}

1.2 效果图 - GridView的使用 SliverGridDelegateWithMaxCrossAxisExtent

在这里插入图片描述

1.3 ⭐️GridView.builder

builder 作用

  1. 只有在屏幕显示的时候 才会去加载builder的方法
// 3. GridView.builder 只有在屏幕显示的时候 才会去加载builder的方法 性能比较好一点
class GridViewBuilderWidget extends StatelessWidget {
    
    
  const GridViewBuilderWidget({
    
    
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    
    
    return GridView.builder(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 3,
            crossAxisSpacing: 8,
            mainAxisSpacing: 8
        ),
        itemBuilder: (BuildContext ctx,int index) {
    
    
          return Container(
            color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
          );
        }
        );
  }
}

1.3 效果图 - GridView.builder

在这里插入图片描述

1.4 瀑布流可以使用第三方

flutter_staggered_grid_view


③、SliverView

Flutter的GridView官方文档
作用是:在同一个页面可以创建多个ListView 和 GridView

3.1 SliverView的使用 以GirdView为例

// SliverView的使用 能包裹 ListView 和 GridView
class SliverViewDemoWidget extends StatelessWidget {
    
    
  const SliverViewDemoWidget({
    
    
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    
    
    return CustomScrollView(
      slivers: [
        SliverGrid(
            delegate: SliverChildBuilderDelegate(
                (BuildContext ctx,int index){
    
    
                  return Container(
                    color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
                  );
                },
                // 返回多少个
              childCount: 100
            ),
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 4,
            crossAxisSpacing: 8,
            mainAxisSpacing: 8,
            childAspectRatio: 1.5
            ),

        )
      ],
    );
  }
}

3.1 效果图-SliverView的使用 以GirdView为例

请添加图片描述

3.2 iOS的SafeArea 和 SliverSafeArea 安全区域的区别

GridView在安全区域的iPhone会超出。如何防止安全区域问题
1.使用SafeArea进行包裹 用户体验不好。滚动在最顶部没有展示超出的范围
2.在滚动到最顶部需要现实展示的UI

3.2.1 使用SafeArea进行包裹 用户体验不好。滚动在最顶部没有展示超出的范围

SafeArea(
        child:xxxWidget
        )

在这里插入图片描述

3.2.1 在滚动到最顶部需要现实展示的UI 使用SliverSafeArea

 SliverSafeArea(
            sliver:xxxxWidget
            )

请添加图片描述

3.3 SliverPadding的使用

我们在使用ListView或者GridView的时候 。当我们添加了内边距之后。

  1. 发现滚动向上的时候 。内边距依然还是有的
  2. 如果处理这个问题
  3. 我们使用SliverListView或者GridView进行替代ListView或者GridView
    并且 使用SliverPadding 可以修复 向上滚动出现的内边距问题

SliverPadding的使用 code

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

  @override
  Widget build(BuildContext context) {
    
    
    return CustomScrollView(
      slivers: [
        SliverSafeArea(
          sliver: SliverPadding(
            padding: EdgeInsets.all(8),
            sliver: SliverGrid(
                delegate: SliverChildBuilderDelegate(
                    (BuildContext ctx,int index){
    
    
                      return Container(
                        color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
                      );
                    },
                    // 返回多少个
                  childCount: 100
                ),
                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 4,
                crossAxisSpacing: 8,
                mainAxisSpacing: 8,
                childAspectRatio: 1.5
                ),

            ),
          ),
        )
      ],
    );
  }
}

3.3 效果图 SliverPadding的使用

在这里插入图片描述

3.4 SliverView的使用 以ListView、GirdView综合使用

类似个人主页的案例

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

  @override
  Widget build(BuildContext context) {
    
    
    return CustomScrollView(
      slivers: [
        // 如果不想系统的Bar在顶部。可以把系统的Bar隐藏。
        // 使用下面Bar。这样滚动是一个整体滚动的 // 能随着内容滚动而滚动
        SliverAppBar(
          // 扩展的内容
          flexibleSpace: FlexibleSpaceBar(
            title: Text("Hello world"),
            background: Image.asset("assets/images/gtr.png",fit:BoxFit.cover),
          ),
          // 扩展高度
          expandedHeight: 200,
          // 不跟随滚动而滚动
          pinned: true,
        ),
        SliverGrid(
          delegate: SliverChildBuilderDelegate(
                  (BuildContext ctx,int index){
    
    
                return Container(
                  color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
                );
              },
              // 返回多少个
              childCount: 10
          ),
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 4,
              crossAxisSpacing: 8,
              mainAxisSpacing: 8,
              childAspectRatio: 1.5
          ),
        ),

        SliverList(
          delegate: SliverChildBuilderDelegate(
              (BuildContext ctx,int index){
    
    
                return ListTile(
                  leading: Icon(Icons.people),
                  trailing: Icon(Icons.delete),
                  title: Text("联系人 ${index}"),
                  subtitle: Text("联系人 电话号码 185100000123"),
                );
              },
            childCount: 20,
          ),
        )
      ],
    );
  }
}

3.4 效果图 - SliverView的使用 以ListView、GirdView综合使用

请添加图片描述


④、滚动的监听 - 以回到顶部的页面为例

1. 使用controller进行监听滚动

弊端是: 无法监听到 开始滚动和结束滚动,只能监听到滚动

class YHiOSHomePage extends StatefulWidget {
    
    
  @override
  State<YHiOSHomePage> createState() => _YHiOSHomePageState();
}

class _YHiOSHomePageState extends State<YHiOSHomePage> {
    
    
  // 偏移300
  ScrollController _controller = ScrollController(initialScrollOffset: 300);

  bool _isShowFloatingBtn = false;
  /**
   * 两种方式可以监听:
   * Controller:
   * 1.可以设置默认值 offset
   * 2.监听滚动,也可以监听滚动的位置
   * NotificationListener
   * 1.开始滚动和结束滚动
   * */

  @override
  void initState() {
    
    
    // TODO: implement initState
    super.initState();
    _controller.addListener(() {
    
    
      print("监听到滚动.... ${_controller.offset}");
      setState(() {
    
    
        _isShowFloatingBtn = _controller.offset >= 1000;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    
    
    return Scaffold(
      appBar: AppBar(
        title: Text("列表测试_Widget"),
      ),
      floatingActionButton: _isShowFloatingBtn ? FloatingActionButton(
        child: Icon(Icons.arrow_upward),
        onPressed: (){
    
    
          print("点击了回到顶部");
          _controller.animateTo(0, duration: Duration(seconds: 1), curve: Curves.easeIn);
        },
      ) : null,
      body: ListView.builder(
          controller: _controller,
          itemBuilder: (BuildContext cxt,int index){
    
    
            return ListTile(
              leading: Icon(Icons.people),
              title: Text("联系人 ${index}"),
            );
          },
          itemCount: 100,
      ),
    );
  }
}

2. 使用NotificationListener进行监听滚动

可以监听 开始滚动和结束滚动
把滚动Widget 用一个NotificationListener 包裹

class YHiOSHomePage extends StatefulWidget {
    
    
  @override
  State<YHiOSHomePage> createState() => _YHiOSHomePageState();
}

class _YHiOSHomePageState extends State<YHiOSHomePage> {
    
    
  // 偏移300
  ScrollController _controller = ScrollController(initialScrollOffset: 300);

  bool _isShowFloatingBtn = false;
  /**
   * 两种方式可以监听:
   * Controller:
   * 1.可以设置默认值 offset
   * 2.监听滚动,也可以监听滚动的位置
   * NotificationListener
   * 1.开始滚动和结束滚动
   * */

  @override
  void initState() {
    
    
    // TODO: implement initState
    super.initState();
    _controller.addListener(() {
    
    
      // print("_controller 监听到滚动.... ${_controller.offset}");
      setState(() {
    
    
        _isShowFloatingBtn = _controller.offset >= 1000;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    
    
    return Scaffold(
      appBar: AppBar(
        title: Text("列表测试_Widget"),
      ),
      floatingActionButton: _isShowFloatingBtn ? FloatingActionButton(
        child: Icon(Icons.arrow_upward),
        onPressed: (){
    
    
          print("点击了回到顶部");
          _controller.animateTo(0, duration: Duration(seconds: 1), curve: Curves.easeIn);
        },
      ) : null,
      body: NotificationListener(
        onNotification: (ScrollNotification notification) {
    
    
          print("NotificationListener 监听到滚动....");
          if(notification is ScrollStartNotification)
            {
    
    
              print("开始滚动");
            }else if (notification is ScrollUpdateNotification)
            {
    
    
              print("正在滚动 总滚动距离: ${notification.metrics.maxScrollExtent} 当前滚动的位置 : ${notification.metrics.pixels}");
            }else if (notification is ScrollEndNotification)
            {
    
    
              print("结束滚动");
            }
          return true;
        },
        child: ListView.builder(
            controller: _controller,
            itemBuilder: (BuildContext cxt,int index){
    
    
              return ListTile(
                leading: Icon(Icons.people),
                title: Text("联系人 ${index}"),
              );
            },
            itemCount: 100,
        ),
      ),
    );
  }
}

④、效果图 - 滚动的监听 - 以回到顶部的页面为例

请添加图片描述


小贴士

1.设置快捷指令

1.1. 创建dart文件

请添加图片描述

1.2. 创建模板

请添加图片描述

1.2.1 使用快捷指令创建模板

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_42816425/article/details/123502741
今日推荐