Flutter 透明漸變 AppBar


最近要用 Flutter 重構一個 Native 頁面,效果如下:

隨着頁面滑動,圓形按鈕逐漸消失,返回按鈕逐漸呈現,同時AppBar的透明度在整個過程中,是隨着滑動距離線性變化的,而按鈕的變化分為兩段:圓形按鈕逐漸消失,返回按鈕逐漸呈現,整個過程可逆。

接下來介紹實現過程。

1.整體結構設計

通過觀察可知,listView 可以在 AppBar 底部滑動,常規的 Scaffold widget 無法滿足這個需求,而 Stack widget 可以實現組件的疊加,在這里通過 Stack 作為頁面的 root widget。通過監聽scrollView 的滑動距離,實時計算 appBar 和 按鈕 的透明度。

///首先聲明 全局變量
  AppBarWidget appBar;
  ScrollController scrollController; //scrollView的控制器
  PositionedBtnWidget roundLeftBtn; //圓形返回按鈕
  PositionedBtnWidget rectLeftBtn;  //方形返回按鈕

在初始化方法里,給全局變量賦值:

 @override
  void initState() {
    super.initState();
    appBar = AppBarWidget();
    scrollController = ScrollController();
    roundLeftBtn = PositionedBtnWidget();
    rectLeftBtn = PositionedBtnWidget();
  }

整體UI結構使用 Scaffold 作為主框架,body部分則是 Stack ,底部 TabBar使用 Scaffold 自帶屬性自定義搭建。為了適配 iPhoneX 底部,需要計算 安全區域高度。 MediaQuery.of(context).padding.bottom;CustomScrollView 的controller 繼承自 ChangeNotifier,可監聽其位置變化。

///示意代碼
Scaffold(
        body: Stack(
          children: <Widget>[
            ///監聽滾動
            NotificationListener(
              onNotification: (notification) {
                if (notification is ScrollUpdateNotification &&
                    notification.depth == 0) {
                  ///滑動通知
           scrollViewDidScrolled(notification.metrics.pixels);
                }
                ///通知不再上傳
                return true;
              },
              child: CustomScrollView(),
            appBar,
            rectLeftBtn,
            roundLeftBtn,
          ],
        ),
        bottomNavigationBar: Container(
            color: Colors.orange,
            height: bottomBarHeight,
            child: Center(
              child: Text('bottom bar'),
            )));

2.其他部件的搭建

因為要實現透明度效果,這里使用 Opacity widegt 來實現。控制 opacity 透明度的值,可實現透明度的變化。注意:在這里發現,Stack內的兩個組件,如果發生重疊,位於頂部的widget最先響應點擊事件。

///示例
Opacity(
      opacity: opacity,
      child: Container(
        height: appBarHeight,
        child: AppBar(
          title: Text('app bar'),
          backgroundColor: Colors.deepOrange,
        ),
      ),
    );

在 Stack 內部,變動部件位置需要用到 Positioned widegt, 點擊事件通過 IconButton 來實現

Positioned(
      top: btnTop,
      right: right,
      left: left,
      child: Opacity(
        opacity: btnOpacity,
        child: IconButton(
          icon: Image.asset(image),
          onPressed: () {
            if (widget != null && widget.actionFunction != null) {
              widget.actionFunction();
            }
          },
        ),
      ),
    );

3.透明度計算

通過監聽 scrollview的滑動距離,計算各個部件的透明度。
在這里 把完全透明到不透明 需要滑動的距離定為 80(單位邏輯像素 logical pixels
而按鈕 的變化分為兩段,每段滑動距離為整體的一半,也就是40邏輯像素。
具體計算方式如下:

 double maxOffset = 80.0;

  scrollViewDidScrolled(double offSet) {
    //print('scroll offset ' + offSet.toString());

    ///appbar 透明度
    double appBarOpacity = offSet / maxOffset;
    double halfPace = maxOffset / 2.0;

    ///圓形按鈕透明度
    double roundOpacity = (halfPace - offSet) / halfPace;

    ///方形按鈕透明度
    double rectOpacity = (offSet - halfPace) / halfPace;

    if (appBarOpacity < 0) {
      appBarOpacity = 0.0;
    } else if (appBarOpacity > 1) {
      appBarOpacity = 1.0;
    }

    if (roundOpacity < 0) {
      roundOpacity = 0.0;
    } else if (roundOpacity > 1) {
      roundOpacity = 1;
    }

    if (rectOpacity < 0) {
      rectOpacity = 0.0;
    } else if (rectOpacity > 1) {
      rectOpacity = 1.0;
    }
    //print('roundOpacity $roundOpacity rectOpacity $rectOpacity');

    ///更新透明度
    if (appBar != null && appBar.updateAppBarOpacity != null) {
      appBar.updateAppBarOpacity(appBarOpacity);
    }

    if (roundLeftBtn != null && roundLeftBtn.updateOpacity != null) {
      roundLeftBtn.updateOpacity(roundOpacity);
    }
    if (rectLeftBtn != null && rectLeftBtn.updateOpacity != null) {
      rectLeftBtn.updateOpacity(rectOpacity);
    }
  }

代碼地址
Demo


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM