MENU

Flutter Scrollable笔记

October 1, 2022 • 笔记

滚动组件笔记

滚动列表与动态加载

ListView.builder(
        physics: const BouncingScrollPhysics(), // 末端继续滑动的效果,回弹
        controller: _scrollController, // 控制器
        padding: const EdgeInsets.all(50), // ListView的Padding
        cacheExtent: 200, // 缓冲区像素,一般不用设置
        itemExtent: 100, // 每个item的高度,用于item高度不同使用Scrollbar跳转时的性能优化
        itemBuilder: (context, index) {
          return Container(
            width: double.infinity,
            height: 100,
            color: Colors.blue[(index % 5) * 100],
            alignment: Alignment.center,
            child: Text('$index'),
          );
        },
        itemCount: 900, // 列表item个数,不填就是无限个
      ),

加载下一页

定义一个controller

final _scrollController = ScrollController();

利用offset属性和jumpto方法滑动

onPressed: () {
          final now = _scrollController.offset; // 获取当前列表偏移量
          _scrollController.animateTo(now + 700, // 每次向下滑动700像素单位
              duration: const Duration(milliseconds: 300), curve: Curves.ease);
        },

回到顶部

onTap: () {
            _scrollController.animateTo(
              -20.0, // 如果是负数,会回弹到0.0,更加自然
              duration: const Duration(milliseconds: 300),
              curve: Curves.ease,
            );
          },

显示滚动条

// 在ScrollView外面套一个Scrollbar即可,还可以设置滚动条的位置和风格,ios默认是CupertinoScrollbar,android是RawScrollBar
Scrollbar(
            controller: _scrollController, // 这一层和滚动层都需要设置相同的controller,否则会报错
        child: ListView.builder(
          physics: const BouncingScrollPhysics(),
          controller: _scrollController,
          padding: const EdgeInsets.all(50),
          cacheExtent: 200,
          itemExtent: 100,
          itemBuilder: (context, index) {
            return Container(
              width: double.infinity,
              height: 100,
              color: Colors.blue[(index % 5) * 100],
              alignment: Alignment.center,
              child: Text('$index'),
            );
          },
          itemCount: 900,
        ),
      ),

下拉刷新

 RefreshIndicator(
               strokeWidth: 3.0,
        color: Colors.white,
        backgroundColor: Colors.amber,
        onRefresh: () {
          return Future.delayed(const Duration(seconds: 2));  // 模拟异步操作
        },
        child: ListView.builder(...

通知事件

NotificationListener(
            onNotification: (notification) {
              print(notification);
              return false; // 返回false则事件继续冒泡,true则截停事件冒泡,结果是父级需要用到通知的组件会失效
            },
            child: ListView.builder(...

支持滑动删除的Dismissible

ListView.builder(
        physics: const BouncingScrollPhysics(),
        controller: _scrollController,
        itemBuilder: (context, index) {
          return Dismissible( // 用Dismissible嵌套
            key: UniqueKey(), // key必传
            background: Container( // 滑动底部背景(主,从左到右,startToEnd)
              color: Colors.green,
              alignment: Alignment.centerLeft,
              padding: const EdgeInsets.only(left: 16),
              child: const Icon(
                Icons.phone,
                color: Colors.white,
              ),
            ),
            secondaryBackground: Container( // 和如上属性相反
              color: Colors.amber,
              alignment: Alignment.centerRight,
              padding: const EdgeInsets.only(right: 16),
              child: const Icon(
                Icons.message,
                color: Colors.white,
              ),
            ),
            onDismissed: (direction) { // 滑动消失的方向,左面是start右面是end
              print(direction);
            },
            confirmDismiss: (direction) async { // 是否真的要删除,是异步函数,返回ture删除,false不删除
              await Future.delayed(const Duration(seconds: 1));
              return true;
            },
            onResize: () { // 删除后剩余item重新布局的过程
              print('on resizing');
            },
            resizeDuration: const Duration(seconds: 2), // 重新布局过渡时间
            movementDuration: const Duration(seconds: 5), // 删除滑动或恢复的时间
            dismissThresholds: const { // 设置滑动方向对应的阈值,默认是0.4
              DismissDirection.startToEnd: 0.1,
              DismissDirection.endToStart: 0.8
            },
            child: Container(
              width: double.infinity,
              height: 100,
              color: Colors.blue[(index % 5) * 100],
              alignment: Alignment.center,
              child: Text('$index'),
            ),
          );
        },
        itemCount: 900,
      ),

ListWheelScrollView(选择器效果)

ListWheelScrollView(
        children: List.generate(
          20,
          (index) => Container(
           // color: Colors.amber,
            child: Text('hello$index',style: TextStyle(fontSize: 72),),
            alignment: Alignment.center,
          ),
        ),
        itemExtent: 100, // 单个元素高度
        offAxisFraction: -1.2, // 扭曲偏移
        overAndUnderCenterOpacity: .5, // 未选中的透明度
        useMagnifier: true, // 使用放大镜
        diameterRatio: 1.5, // 调整绕3D圆心转动时的转动半径(可以这么理解)
        physics: FixedExtentScrollPhysics(), // 始终固定到某一项
        onSelectedItemChanged: (value) { // 选择事件
          print('select $value');
        },
      ),

无法设置横向滚动,必须使用的话要在外层套一个RotateView

PageView

PageView(
        pageSnapping: true, // 始终吸附固定,显示某一个整页
        scrollDirection: Axis.horizontal, // 方向
        onPageChanged: (index) {
          print('$index');
        },
        children: List.generate(
          7,
          (index) => Container(
            color: Colors.amber[(index + 1) * 100],
            alignment: Alignment.center,
            child: Text(
              'page$index',
              style: TextStyle(fontSize: 72),
            ),
          ),
        ),
      ),

ReorderableListView(拖拽)

ReorderableListView(
        children: List.generate(
          20,
          (index) => Text(
            'index is $index',
            key: UniqueKey(), // 必须传一个key
            textAlign: TextAlign.center,
          ),
        ),
        onReorder: (oldIndex, newIndex) { // 拖动释放事件
          print('move from $oldIndex to $newIndex');
        },
      ),