Flutter 之 AppBar 这样的骚操作你知道吗?

好久不见了,这阵子在忙公司的项目,加班比较严重,这周终于抽了点时间来学习一下新技术。由于去年九月份在上海参加过 Google 举办的 Google develop days, 受益颇多,特别在其目前正在大力热推的 Flutter 框架。相比于目前热门的跨平台框架 React Native,Flutter在 UI 绘制以及性能方便不遑多让。因此,这款 app 打算基于 Dart 语言,并采用 Flutter 框架来完成。

花了大概几天时间熟悉了下 Dart 语法和 Flutter 基本组成控件,就开始摸索着做一个练手项目。AppBar 想必大家都用过,当其处于启动页面时是隐藏 leading 的,而处于其他页面时左边默认携带返回按钮。目前,我们的需求是:首页的 AppBar 最左边为用户头像,点击用户头像可以自动打开左侧抽屉栏。

最终效果图如下所示:

效果图

初学者一开始总是痛苦的,还好,解决问题的途径“万变不离其宗”。我们先来查看一下 Flutter 官方文档 发现,要使用 AppBar来操作页面,决定其左边点击事件的属性就是 leading

AppBar({
Key key,
Widget leading,
bool automaticallyImplyLeading: true,
Widget title,
List<Widget> actions,
Widget flexibleSpace,
PreferredSizeWidget bottom,
double elevation,
Color backgroundColor,
Brightness brightness,
IconThemeData iconTheme,
TextTheme textTheme,
bool primary: true,
bool centerTitle,
double titleSpacing: NavigationToolbar.kMiddleSpacing,
double toolbarOpacity: 1.0,
double bottomOpacity: 1.0
})

可以看到,我们 AppBar 中的 leading 属性是一个 Widget 类型,那么它接收的参数范围就很广了,换句话说,我们可以直接将一个按钮传递给它,并在 onPressed 方法中处理左边侧滑栏的开启和关闭状态。👌,有思路了咱们久开始试验一下:

 final GlobalKey<ScaffoldState> _scaffoldKey = new                                                  GlobalKey();
 
 @override
  Widget build(BuildContext context) {
    return new DefaultTabController(
        length: _pages.length,
        child: new Scaffold(
          key: _scaffoldKey,
          appBar: new AppBar(
            /// 第一种方式
            /// 通过可监听点击的IconButton传入widget,
            /// 并在onPressed中处理drawer开启,借助于GlobalKey
            leading: new IconButton(
                icon: new Container(
                  padding: EdgeInsets.all(3.0),
                  child: new CircleAvatar(
                      radius: 30.0,
                      backgroundImage: AssetImage("assets/images/moosphon_logo.jpeg")
                  ),
                ),
              onPressed: (){
                _scaffoldKey.currentState.openDrawer();
              },
            ),
            centerTitle: true,
            title: new TabBar(
                isScrollable: true,
                labelPadding: EdgeInsets.all(14),
                indicatorSize: TabBarIndicatorSize.label,
                indicatorColor: Colors.white,
                tabs: _titles.map((String title) =>
                new Tab(text: title)).toList()
            ),
            actions: <Widget>[
              new IconButton(
                  icon: new Icon(Icons.search),
                  tooltip: '搜索',
                  onPressed: (){
                    NavigatorUtil.intentToPage(context, new SearchPage(), pageName: "SearchPage");
                  }
              )
            ],
          ),
          body: new TabBindingView(),
          drawer: new Drawer(
            child: new HomeLeftDrawerPage(),
          ),
        )
    );
  }

为了不影响篇幅,这里我只贴了关键代码,至于 Drawer 用法相信大家没什么问题,这里我们给 AppBar 的leading 参数传了一个 IconButton 控件,里面我还实现了圆形头像。这样一来,我们的用户头像就可以被点击了响应了,接下来我们需要处理头像点击事件与 Drawer 的联动性了。这里我们通过设置 GlobalKey 来让 Scaffold 容器获取到其内部的 Drawer 组件,进而控制它的开闭,这样,我们就已经可以通过点击自定义的用户头像来开启左边侧滑栏啦😄。

细心的小伙伴一定会发现,上面的代码中我注释这只是第一种实现方式,难道还有第二种实现方式?哈哈,没错,的确还有第二种思路。其实,通过查看 AppBar 的源码,我们可以发现:AppBar内部已经自动实现了 AppBar 的 leadingDrawer 抽屉栏的关联:

Widget leading = widget.leading;
    if (leading == null && widget.automaticallyImplyLeading) {
      if (hasDrawer) {
        leading = IconButton(
          icon: const Icon(Icons.menu),
          onPressed: _handleDrawerButton,
          tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
        );
      } else {
        if (canPop)
          leading = useCloseButton ? const CloseButton() : const BackButton();
      }
    }
    if (leading != null) {
      leading = ConstrainedBox(
        constraints: const BoxConstraints.tightFor(width: _kLeadingWidth),
        child: leading,
      );
    }

由于代码较多,这里我只放上了 build 方法中关于 leading 部分的关键代码,通过分析我们可以发现:

  • leading 如果 为空并且 automaticallyImplyLeading 属性为true,那么就会自动推断出 leadingWidget 的类型和用途,另外如果当前 Scaffold 中存在 Drawer ,则会自动创建一个 IconButton 作为leading 使用,同时它的点击事件中处理了与 Drawer 抽屉栏的关联事件,无需开发者再处理。也就是说,在这种情况下,如果我们自定义了 leading (例如当前我们给它传的是用户的头像 Widget ),那么 leading 就无法自动关联 Drawer ,也就是说关联 Drawer 的这部分代码需要开发者自行实现。
  • 如果 leading 不为空呢?并且 automaticallyImplyLeading 开关关闭,那么 leading 的空间就会被 title 给占据。

说了这么多,最终的结论是:如果我们想要自定义 leading ,那么目前官方源码中 AppBar 中 leading 自动关联 Drawer 的处理我们没办法使用。如果我们非要用呢?那就只能修改源码啦:

/// 注意,前方高能来袭,以下为修改的部分code

    Widget leading = widget.leading;
    if (/*leading == null && */widget.automaticallyImplyLeading) {
      if (hasDrawer) {
        leading = IconButton(
          icon: /*const Icon(Icons.menu)*/ leading ?? Icon(Icons.home), // 如果leading指定了widget那么
          onPressed: _handleDrawerButton,
          tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
        );
      } else {
        if (canPop)
          leading = useCloseButton ? const CloseButton() : const BackButton();
      }
    }
    if (leading != null) {
      leading = ConstrainedBox(
        constraints: const BoxConstraints.tightFor(width: _kLeadingWidth),
        child: leading,
      );
    }

铛铛铛铛~这就改好啦,改动的地方是不是很少?哈哈😄,其实只要把 leading 为空的限制条件去掉,并且可以传入我们自己定义的 Widget 就好啦!

这样一来,我们就不用手动去处理 leadingDrawer 的关联事件,只需要交给系统帮我们完成就可以啦:

@override
  Widget build(BuildContext context) {
    return new DefaultTabController(
        length: _pages.length,
        child: new Scaffold(
          key: _scaffoldKey,
          appBar: new DrawerAutoBindingAppBar(
            /// 第二种方式
            /// 通过修改[AppBar]源码来将普通widget作为icon传给IconButton
            /// 源码中已处理onPressed关联drawer事件,无需额外处理,详情见[DrawerAutoBindingAppBar]
            leading: new Container(
                padding: EdgeInsets.all(3.0),
                child: new CircleAvatar(
                    radius: 30.0,
                    backgroundImage: AssetImage("assets/images/moosphon_logo.jpeg")
                )
            ),
            centerTitle: true,
            title: new TabBar(
                isScrollable: true,
                labelPadding: EdgeInsets.all(14),
                indicatorSize: TabBarIndicatorSize.label,
                indicatorColor: Colors.white,
                tabs: _titles.map((String title) =>
                new Tab(text: title)).toList()
            ),
            actions: <Widget>[
              new IconButton(
                  icon: new Icon(Icons.search),
                  tooltip: '搜索',
                  onPressed: (){
                    NavigatorUtil.intentToPage(context, new SearchPage(), pageName: "SearchPage");
                  }
              )
            ],
          ),
          body: new TabBindingView(),
          drawer: new Drawer(
            child: new HomeLeftDrawerPage(),
          ),
        )
    );
  }

代码就不多做解释了,效果是和之前一样的,好啦,这样我们就更加了解 AppBar 的用法以及 leading 的背后“小彩蛋”啦😁。

感谢大家的阅读,以后我将给大家带来更多好玩实用的应用型开发技巧,如果大家有任何建议,欢迎留言👏。

QQ群: 601924443 一起玩技术~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容