Flutter-使用NestedScrollView实践 202

2023-06-18  本文已影响0人  勇往直前888

需求简介

订单列表

如图所示,这是一个订单列表,红框中的消息,tab标题,工具栏三部分比较占空间,在上划的时候,隐藏比较好。

简单分析

看原来的代码,实现的思路是:

这种写法算是主流做法,红框部分一直存在,不能隐藏。
现在要改成红框部分也能滑动,能隐藏,该怎么修改?

NestedScrollView

(1) 红框部分是需要滑动的

(2)内容部分在ListView中,本身也是能滑动的

(3)并且还有TabBarView,需要进行内容的切换

以上三条,用NestedScrollView来实现非常适合。

企业微信截图_0f268e84-3451-46a0-a7cb-86f41a7de061.png
class NestedScrollView extends StatefulWidget {
  const NestedScrollView({
    ...
    //通用属性已省略
    // header, sliver构造器
    required this.headerSliverBuilder,
    // 可以接受任意的滚动组件
    required this.body,
    this.dragStartBehavior = DragStartBehavior.start,
    this.floatHeaderSlivers = false,
  })

header部分要求是Sliver家族的;但是body部分可以用普通的;这个有点奇怪;

方案1:放入头部

直接先上代码,由于业务代码较多,这里用了注释表达意图。

@override
  Widget build(BuildContext context) {
    return GetBuilder<SliverOrderLogic>(
      assignId: true,
      builder: (logic) {
        return Scaffold(
          body: _buildBody(),
        );
      },
    );
  }

  /// 页面结构,放在一个CustomScrollView中
  Widget _buildBody() {
    return NestedScrollView(
      headerSliverBuilder: (context, innerBoxIsScrolled) {
        return [
          SliverOverlapAbsorber(
            handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
            sliver: SliverAppBar(
              backgroundColor: PandaColorConfig().colf9f9f9,
              elevation: 0,
              centerTitle: true,
              iconTheme: IconThemeData(color: PandaColorConfig().col333333),
              title: _buildTitle(),
              actions: _buildActions(),
              expandedHeight: logic.isShowMessage() ? 185.h : 165.h,
              flexibleSpace: FlexibleSpaceBar(
                background: Column(
                  children: [
                    /// 标题栏占位
                    SizedBox(
                      height: 85.5.h,
                    ),

                    /// 消息
                    _buildMessage(),

                    /// tab标题
                    _buildTabBar(),

                    /// 工具栏
                    _buildToolBar(),
                  ],
                ),
              ),
              pinned: true,
              floating: true,
              snap: true,
            ),
          ),
        ];
      },
      body: _buildTabContent(),
    );
  }
/// 标题栏占位
    SizedBox(
          height: 85.5.h,
    ),

实现方式二:header列表

  /// 页面结构,放在一个CustomScrollView中
  Widget _buildBody() {
    return NestedScrollView(
      floatHeaderSlivers: true, // 这个参数能够让头部优先滑动,类似淘宝上滑引出菜单
      headerSliverBuilder: (context, innerBoxIsScrolled) {
        return [
          _buildAppBar(),
          SliverToBoxAdapter(child: _buildMessage()),
          SliverToBoxAdapter(child: _buildTabBar()),
          SliverToBoxAdapter(child: _buildToolBar()),
        ];
      },
      body: _buildTabContent(),
    );
  }

  /// 头部
  Widget _buildAppBar() {
    return SliverAppBar(
      backgroundColor: PandaColorConfig().colf9f9f9,
      elevation: 0,
      centerTitle: true,
      iconTheme: IconThemeData(color: PandaColorConfig().col333333),
      title: _buildTitle(),
      actions: _buildActions(),
      pinned: true,
    );
  }

这样简单改造一下,就能得到头部和body部分的ListView同时滑动的效果。但是这里还有一点不满足要求:下滑的时候要求头部先出来,然后body部分继续滑动

后来想想,这种滑动效果,和floatHeaderSlivers的字面意思确实很像。只是一开始不知道,绕了很多圈子。

注意事项

CustomScrollView(
  //controller: logic.contentController, // 这里不能指定Controller,否则header将不能滑动
  physics: physics,
  slivers: [
    SliverList(
        delegate: SliverChildBuilderDelegate(
      (context, index) => _orderCell(logic.rows[index]),
      childCount: logic.rows.length,
    )),
  ],
)
  /// 页面结构,放在一个CustomScrollView中
  Widget _buildBody() {
    return NestedScrollView(
      /// 这里可以加Controller,对于交互行为没有影响
      /// 但是如果这里加个滑动监听,会影响性能
      /// 这里只是加了个打印offset的操作,都会有卡顿的感觉
      //controller: logic.headerController,
      floatHeaderSlivers: true, // 这个参数能够让头部优先滑动,类似淘宝上滑引出菜单
      headerSliverBuilder: (context, innerBoxIsScrolled) {
        return [
          _buildAppBar(),
          SliverToBoxAdapter(child: _buildMessage()),
          SliverToBoxAdapter(child: _buildTabBar()),
          SliverToBoxAdapter(child: _buildToolBar()),
        ];
      },
      body: _buildTabContent(),
    );
  }
CustomScrollView(
  physics: physics,
  slivers: [
    SliverList(
        delegate: SliverChildBuilderDelegate(
      (context, index) => _orderCell(logic.rows[index]),
      childCount: logic.rows.length,
    )),
  ],
)

替换前:

ListView(
  physics: physics,
  children:
      get.rows.map((e) => _orderCell(get, e)).toList(),
)

这样替换的成本很低,但是性能提升明显。

编码的形式真是太恶心了

参考文章

不一样角度带你了解 Flutter 中的滑动列表实现
Flutter学习 可滚动Widget 下
Flutter 入门指北(Part 8)之 Sliver 组件、NestedScrollView
Flutter 之 嵌套可滚动组件 (二十五)

上一篇下一篇

猜你喜欢

热点阅读