Flutter自定義漸變色AppBar

AppBar的屬性

 AppBar({
    Key key,
    this.leading,
    this.automaticallyImplyLeading = true,
    this.title,
    this.actions,
    this.flexibleSpace,
    this.bottom,
    this.elevation,
    this.backgroundColor,
    this.brightness,
    this.iconTheme,
    this.textTheme,
    this.primary = true,
    this.centerTitle,
    this.titleSpacing = NavigationToolbar.kMiddleSpacing,
    this.toolbarOpacity = 1.0,
    this.bottomOpacity = 1.0,
  }) : assert(automaticallyImplyLeading != null),
       assert(elevation == null || elevation >= 0.0),
       assert(primary != null),
       assert(titleSpacing != null),
       assert(toolbarOpacity != null),
       assert(bottomOpacity != null),
       preferredSize = Size.fromHeight(kToolbarHeight + (bottom?.preferredSize?.height ?? 0.0)),
       super(key: key);

大體思路就是繼承一個 PreferredSize 類苛让,內(nèi)部通過 Container + decoration 實(shí)現(xiàn)自己需要的效果握爷。(在 Scaffold 類中 appBar 參數(shù)需要一個實(shí)現(xiàn) PreferredSizeWidget 的對象)

文章中的代碼這里貼出來

Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new PreferredSize(
        child: new Container(
          padding: new EdgeInsets.only(
            top: MediaQuery.of(context).padding.top
          ),
          child: new Padding(
            padding: const EdgeInsets.only(
              left: 30.0,
              top: 20.0,
              bottom: 20.0
            ),
            child: new Text(
              'Arnold Parge',
              style: new TextStyle(
                fontSize: 20.0,
                fontWeight: FontWeight.w500,
                color: Colors.white
              ),
            ),
          ),
          decoration: new BoxDecoration(
            gradient: new LinearGradient(
              colors: [
                Colors.red,
                Colors.yellow
              ]
            ),
            boxShadow: [
              new BoxShadow(
                color: Colors.grey[500],
                blurRadius: 20.0,
                spreadRadius: 1.0,
              )
            ]
          ),
        ),
        preferredSize: new Size(
          MediaQuery.of(context).size.width,
          150.0
        ),
      ),
      body: new Center(
        child: new Text('Hello'),
      ),
    );
  }

AppBar內(nèi)部實(shí)現(xiàn)

class AppBar extends StatefulWidget implements PreferredSizeWidget 

Appbar 繼承了 StatefulWidget 實(shí)現(xiàn)了 PreferredSizeWidget ,所以我們直接看它的 State -> _AppBarState 吼鳞。

直接去看 build 方法的返回,從后向前,看 AppBar 是如何實(shí)現(xiàn)的绅这。

 @override
  Widget build(BuildContext context) {
    // 省略部分代碼,后面再看
    ...

    final Brightness brightness = widget.brightness
      ?? appBarTheme.brightness
      ?? themeData.primaryColorBrightness;
    final SystemUiOverlayStyle overlayStyle = brightness == Brightness.dark
      ? SystemUiOverlayStyle.light
      : SystemUiOverlayStyle.dark;

    return Semantics( // 輔助功能相關(guān)
      container: true, 
      child: AnnotatedRegion<SystemUiOverlayStyle>( // 處理主題相關(guān)在辆,狀態(tài)欄文字顏色
        value: overlayStyle,
        child: Material( // Material 控件证薇,處理顏色度苔,陰影等效果
          color: widget.backgroundColor
            ?? appBarTheme.color
            ?? themeData.primaryColor,
          elevation: widget.elevation
            ?? appBarTheme.elevation
            ?? _defaultElevation,
          child: Semantics( // child里面才是真正的內(nèi)容,我們看內(nèi)部的appBar的實(shí)現(xiàn)浑度。
            explicitChildNodes: true,
            child: appBar,
          ),
        ),
      ),
    );
  }

返回了一個控件寇窑,處理了明暗主題,顏色箩张,陰影甩骏,子控件,這里我們不想用這個顏色伏钠,再通過查看 child 能否設(shè)置顏色横漏。

這里的 appBar 是在上面定義的:

Widget appBar = ClipRect( // 用矩形剪輯其子widget
      child: CustomSingleChildLayout( // 通過deleagate 來約束子widget
        delegate: const _ToolbarContainerLayout(), // 這里的布局是一個寬充滿,高度為kkToolbarHeight高度
        child: IconTheme.merge( // 處理IconTheme
          data: appBarIconTheme,// 通過判斷熟掂,處理iconTheme的取值
          child: DefaultTextStyle( // 文字樣式
            style: sideStyle, // 通過判斷傳入的textTheme處理style取值
            child: toolbar,
          ),
        ),
      ),
    );

這里可以看到缎浇,這里就是包裝了一個 toolbar ,我們繼續(xù)看 toolbar :

 // 這里是一個NavigationToolbar赴肚,我們設(shè)置的leading素跺,title在這里使用
    final Widget toolbar = NavigationToolbar(
      leading: leading,
      middle: title,
      trailing: actions,
      centerMiddle: widget._getEffectiveCenterTitle(themeData),
      middleSpacing: widget.titleSpacing,
    );

關(guān)于 appBar 內(nèi)部還進(jìn)行一些處理,如處理 bottom 誉券,增加 SafeArea 等處理指厌,這里不做展開了

   if (widget.bottom != null) { // bottom
      appBar = Column(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: <Widget>[
          Flexible(
            child: ConstrainedBox(
              constraints: const BoxConstraints(maxHeight: kToolbarHeight),
              child: appBar,
            ),
          ),
          widget.bottomOpacity == 1.0 ? widget.bottom : Opacity(
            opacity: const Interval(0.25, 1.0, curve: Curves.fastOutSlowIn).transform(widget.bottomOpacity),
            child: widget.bottom,
          ),
        ],
      );
    }

    // The padding applies to the toolbar and tabbar, not the flexible space.
    if (widget.primary) { // SafeArea
      appBar = SafeArea(
        top: true,
        child: appBar,
      );
    }

    appBar = Align( // Alignment.topCenter
      alignment: Alignment.topCenter,
      child: appBar,
    );

    if (widget.flexibleSpace != null) { // flexibleSpace效果
      appBar = Stack(
        fit: StackFit.passthrough,
        children: <Widget>[
          widget.flexibleSpace,
          appBar,
        ],
      );
    }

通過這里我們知道了,其實(shí) AppBar 中踊跟,顏色是在 Material 中設(shè)置的踩验,我們常用的設(shè)置是在 toolbar 中進(jìn)行使用的,所以最簡單的漸變色處理方式就是將 Material 的child 包一層做顏色處理商玫,不去修改現(xiàn)有部分箕憾。
代碼實(shí)現(xiàn)
代碼很簡單,將AppBar的代碼拷貝出來進(jìn)行修改拳昌,這里的類名為GradientAppBar金踪。
在自定義的 GradientAppBar 的構(gòu)造方法中增加漸變顏色的初始值鸵荠,和終止值口渔。

 GradientAppBar({
    ...
    this.gradientStart,
    this.gradientEnd,
  })  : assert(automaticallyImplyLeading != null),
        ...
        super(key: key);
  
  final Color gradientStart;
  final Color gradientEnd;

再將 _AppBarState 類的代碼拷貝出來饺窿,這里的類名是 _GradientAppBarState (記得修改 createState 方法)。

然后在修改對 build 方法 return 中 child 進(jìn)行包裝沈矿,使用傳入的顏色作為漸變色背景上真。

// 添加到build方法最后,return之前羹膳,通過使用decoration實(shí)現(xiàn)顏色的漸變
    if (widget.gradientStart != null && widget.gradientEnd != null) {
      appBar = Container(
        decoration: BoxDecoration(
          gradient: LinearGradient(
              colors: [widget.gradientStart, widget.gradientEnd]),
        ),
        child: appBar,
      );
    }

再進(jìn)行處理 Material 的 顏色

 return Material(
      // 判斷是否使用漸變色
      color: widget.gradientStart != null && widget.gradientEnd != null
          ? Colors.transparent
          : widget.backgroundColor ??
              appBarTheme.color ??
              themeData.primaryColor,
      elevation: widget.elevation ?? appBarTheme.elevation ?? _defaultElevation,
      child: appBar, // 使用包裝后的appBar 
    );

這樣就實(shí)現(xiàn)了漸變效果谷羞。

使用 GradientAppBar ,就是將原來使用 AppBar 替換為 GradientAppBar 溜徙。

return Scaffold(
      appBar: PreferredSize(
        child: GradientAppBar(
          gradientStart: Color(0xFF49A2FC),
          gradientEnd: Color(0xFF2171F5),
          title: Text(widget.title),
          leading: Icon(Icons.ac_unit),
        ),
        preferredSize: Size.fromHeight(400),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末湃缎,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蠢壹,更是在濱河造成了極大的恐慌嗓违,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件图贸,死亡現(xiàn)場離奇詭異蹂季,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)疏日,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進(jìn)店門偿洁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人沟优,你說我怎么就攤上這事涕滋。” “怎么了挠阁?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵宾肺,是天一觀的道長。 經(jīng)常有香客問我侵俗,道長锨用,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任隘谣,我火速辦了婚禮增拥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘寻歧。我一直安慰自己掌栅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布熄求。 她就那樣靜靜地躺著渣玲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪弟晚。 梳的紋絲不亂的頭發(fā)上忘衍,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天,我揣著相機(jī)與錄音卿城,去河邊找鬼枚钓。 笑死,一個胖子當(dāng)著我的面吹牛瑟押,可吹牛的內(nèi)容都是我干的搀捷。 我是一名探鬼主播,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼嫩舟!你這毒婦竟也來了氢烘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤家厌,失蹤者是張志新(化名)和其女友劉穎播玖,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饭于,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蜀踏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了掰吕。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片果覆。...
    茶點(diǎn)故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖殖熟,靈堂內(nèi)的尸體忽然破棺而出局待,到底是詐尸還是另有隱情,我是刑警寧澤吗讶,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布燎猛,位于F島的核電站,受9級特大地震影響照皆,放射性物質(zhì)發(fā)生泄漏重绷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一膜毁、第九天 我趴在偏房一處隱蔽的房頂上張望昭卓。 院中可真熱鬧,春花似錦瘟滨、人聲如沸候醒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽倒淫。三九已至,卻和暖如春败玉,著一層夾襖步出監(jiān)牢的瞬間敌土,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工运翼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留返干,地道東北人。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓血淌,卻偏偏與公主長得像矩欠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內(nèi)容