【Flutter】实现页面沉浸式主题色配置

3,411 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情

前言

实现自定义TabBar主题色配置实现TabBar主题色定制后,对整体页面做定制化主题色配置更加具有沉浸式效果。因此为了更好体现出主题色配置特点,本次对页面整体做定制化主题色实现做一次介绍。

Video_20220820_045352_279.gif

实现过程

背景沉浸式配置

根据需求描述需要对顶部透出背景做主题切换效果。当频道页面做左右滑动操作时,页面背景也会随之发生变化。原有的框架结构如下所示:

/// 伪代码形式
NestedScrollView(
  headerSliverBuilder:(
    SliverAppBar(
      flexibleSpace:MagicFlexibleSpaceBar()
      bottom: TabBar()
    )
  )
  body:TabBarView(
    CustomScrollView(
      CupertinoSliverRefreshControl()
      SliverFixedExtentList()
    )
    ///同上CustomScrollView
    ....
  )
)

如果需要添加背景色覆盖住全局就必须将NestedScrollView嵌套在布局内部。可采用Stack形式讲主题背景作为底层布局在整体页面下方呈现出来,此外只需要为BackgroundThemes组件提供页面滑动监听即可实现主题背景切换的效果。

/// 伪代码形式
Stack(
  BackgroundThemes[]//主题切换组件
  NestedScrollView(
  headerSliverBuilder:(
    SliverAppBar(
      flexibleSpace:MagicFlexibleSpaceBar()
      bottom: TabBar()
    )
  )
  body:TabBarView(
    CustomScrollView(
      CupertinoSliverRefreshControl()
      SliverFixedExtentList()
    )
    ///同上CustomScrollView
    ....
  )
)
)

通过获取到Tab控制器偏移量得知当前页面下标,通过配置主题背景数组picUrls得到当前背景图,内部使用Stack来实现当前和下一个页面背景切换功能(切换过程带有透明度变化使得切换过程更为自然),切换具体实现如下:

Positioned(
          top: 0.0,
          left: 0.0,
          right: 0.0,
          /// 监听
          child:  AnimatedBuilder(
              animation: tabController1.animation,
              builder: (context, child) {
                // 与之前实现TabBar指示器的方法一样
                double page = 0;
                int realPage = 0;
                page = tabController1.index + tabController1.offset ?? 0;
                realPage =
                    tabController1.index + tabController1.offset?.floor() ?? 0;
                double opacity = 1 - (page - realPage).abs();
                int nextIndex =
                realPage + 1 < colors.length ? realPage + 1 : realPage;
                String thisPic = picUrls[realPage];
                String nextPic = picUrls[
                realPage + 1 < picUrls.length ? realPage + 1 : realPage];
                List<Widget> childs = List();
                //当前页面的背景图片
                if (thisPic != null && thisPic != '') {
                  childs.add(
                    Opacity(
                      opacity: opacity,
                      child: Image.asset(
                        thisPic,
                        fit: BoxFit.fitWidth,
                        alignment: Alignment.topCenter,
                      ),
                    ),
                  );
                }
                //下一个页面的背景图片
                if (nextPic != null && nextPic != '') {
                  childs.add(Opacity(
                    opacity: 1 - opacity,
                    child: Image.asset(
                      picUrls[realPage + 1 < picUrls.length
                          ? realPage + 1
                          : realPage],
                      fit: BoxFit.fitWidth,
                      alignment: Alignment.topCenter,
                    ),
                  ));
                }
                return Stack(
                  children: childs,
                );
              }),
        ),

背景主题实现需要将SliverAppBar以及内部FlexibleSpaceBar的背景色设置为Colors.transparent来透出背景。

683cc492a59d217de4f9b1042dde5642.gif

上滑顶部栏隐藏以及背景透出

实现背景色主题之后继续实现顶部上滑时底部列表不遮盖背景。

0ff6be160c76dcdb6456d4e419024128.gif

为处理默认实现是这样的,当做上滑操作时顶部栏可以正常隐藏但上滑列表会将背景色图片遮盖。这是因为NestedScrollView以及其内部组件实现有关,上滑联动headerSliverBuilder组件是悬浮之上和body会重叠的。若希望实现不重叠的效果就需要只对body做滑动操作,因此需要改动点是将NestedScrollView设置为不可滑动的组件,同时赋值ScrollControllerbodyCustomScrollView设置自身ScrollController并且嵌套NotificationListener来监听滑动,当在body在指定范围内滑动联动带上NestedScrollView滑动。实际上该操作就是将NestedScrollView内部的outScroller释放掉,让开发自行控制body滑动操作以此来避免上下组件重叠。

NestedScrollView(
controller: fatherController,
physics: NeverScrollableScrollPhysics(),
body:TabBarView(
  children:[
    NotificationListener(
      onNotification:(position) {
        // 获取到position 和 fatherController 做比较
        // 根据滑动情况 对fatherController做移动操作
      }
      child: CustomScrollView(
        controller: scrollController,
        physics: NestedClampingScrollPhysics(),
      )
    )
  ]
)
)

4e1ba25becb5649d2c31939c4e941a6c.gif

整体功能展示

🚀具体实现代码看这里🚀

Video_20220825_081753_890.gif