Flutter第七章(Drawer 侧边栏,UserAccountsDrawerHeader,DrawerHeader以及案例 )

版权声明:本文为作者原创书籍。转载请注明作者和出处,未经授权,严禁私自转载,侵权必究!!!
情感语录:情不自禁念出的那个名字,梦里经常出现的那个她还在你的身边吗?无论在与不在都要学会懂得更加珍惜!

欢迎来到本章节,上一章节我们讲了底部导航菜单和顶部导航菜单场景的使用,知识点回顾 戳这里 Flutter基础第六章

本章节将讲解另一个场景:左右侧边栏,侧边栏的使用场景还是非常多的,比如:QQ,京东等。本章知识点将延用第六章中实例进行讲解,因为架子已经搭好了嘛,充分利用起来O(∩_∩)O。

本章简要:

1、Drawer 侧边栏

2、 UserAccountsDrawerHeader 头部模板样式的配置

3、 DrawerHeader 定制侧边栏头部样式

4、 侧边栏路由实现跳转

5、侧边栏 开/关 监听以及手动 开/关 侧边栏

一、侧边栏Drawer

在 Scaffold 组件里面配置drawer属性 可以定义左侧边栏,配置 endDrawer 可以定义右侧边栏。侧边栏默认是隐藏的,我们可以通过手指滑动或者点击按钮显示侧边栏。

我们将路由先修改到第六章的 Tabs 下,作为第一个启动界面。然后 在 HomePage 中添加侧边栏:

  import 'package:flutter/material.dart';
  
  class HomePage extends StatefulWidget {
    HomePage({Key key}) : super(key: key);
  
    _HomePageState createState() => _HomePageState();
  }
  
  class _HomePageState extends State {
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        backgroundColor: Colors.blue,
        body: Container(
          child: Center(
              child: Text(
            "主页",
          )),
        ),
        drawer: Drawer(
  
          child: Container(
            //加个背景
            color: Colors.deepPurpleAccent,
  
            child: ListView(
              children: [
  
                ListTile(title: Text('QQ会员', style: TextStyle(color: Colors.blue, fontSize: 22)),
                  //CircleAvatar 一般用于设置圆形头像
                  leading:  CircleAvatar(child:  Icon(Icons.question_answer),),
                  onTap: () {
                    Navigator.pop(context);
                  },),
                ListTile(title:  Text('我的相册', style: TextStyle(color: Colors.blue, fontSize: 22)),
                  leading:  CircleAvatar(
                      child: Icon(Icons.people)
                  ),
                  onTap: () {
                    Navigator.pop(context);
                  },),
                ListTile(title: Text('我的文件', style: TextStyle(color: Colors.blue, fontSize: 22)),
                  leading:  CircleAvatar(
                    child:  Icon(Icons.list),),
                  onTap: () {
                    Navigator.pop(context);
                  },),
              ],
            ),
          )
        ),
  
        endDrawer: Drawer(
          child: Text(
            '右侧侧边栏',
            style: TextStyle(color: Colors.blue, fontSize: 22),
          ),
        ),
      );
    }
  }

分别配置了 drawerendDrawer 左右侧边栏,并在左侧边栏加了一个菜单列表的样式,点击菜单可以退出侧边栏,退出侧边栏同退出页面一样使用 Navigator.pop(context); 即可。

drawer.gif

二、 UserAccountsDrawerHeader 头部模板样式的配置

UserAccountsDrawerHeader组件可以方便在侧边栏配置一个头部样式,使用这个往往是样式比较固定的情况下可以选择,构造函数如下:

 const UserAccountsDrawerHeader({
    Key key,
    this.decoration,
    this.margin = const EdgeInsets.only(bottom: 8.0),
    this.currentAccountPicture,
    this.otherAccountsPictures,
    @required this.accountName,
    @required this.accountEmail,
    this.onDetailsPressed,
  }) : super(key: key);
常用属性
    属性                       描述

    decoration                 设置顶部背景颜色

    accountName                账户名称

    accountEmail               账户邮箱

    currentAccountPicture      用户头像

    otherAccountsPictures      用来设置当前账户其他账户头像

    margin                     外边距

下面来利用 UserAccountsDrawerHeader 给侧边栏顶部配置下样式。

  import 'package:flutter/material.dart';
  
  class HomePage extends StatefulWidget {
    HomePage({Key key}) : super(key: key);
  
    _HomePageState createState() => _HomePageState();
  }
  
  class _HomePageState extends State {
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        backgroundColor: Colors.blue,
        body: Container(
          child: Center(
              child: Text(
            "主页",
          )),
        ),
        drawer: Drawer(
  
          child: Container(
          //加个背景
          color: Colors.deepPurpleAccent,
  
          child: ListView(
            children: [
              UserAccountsDrawerHeader(
                accountName: Text("红红",style: TextStyle(color: Colors.lightBlue, fontSize: 18)),
                accountEmail: Text("[email protected]",style: TextStyle(color: Colors.lightBlue, fontSize: 18)),
                //账户头像
                currentAccountPicture: CircleAvatar(
                  backgroundImage:
                      NetworkImage("https://upload.jianshu.io/users/upload_avatars/3030564/2789e9ea-9856-456f-be2a-e00ed5992c26.png?imageMogr2/auto-orient/strip|imageView2/1/w/120/h/120"),
                ),
  
                //配置背景
                decoration: BoxDecoration(
                    color: Colors.yellow,
                    image: DecorationImage(
                        image: NetworkImage(
                            "https://gss1.bdstatic.com/-vo3dSag_xI4khGkpoWK1HF6hhy/baike/awhcrop%3D200%2C200/sign=a13391550fe939014048d06d098324da/4b90f603738da977e4f8af5ebe51f8198718e3f4.jpg"),
                        fit: BoxFit.cover)),
  
                //配置其他
                otherAccountsPictures: [
                  Text("其他内容",style: TextStyle(color: Colors.lightBlue, fontSize: 10))
                ],
              ),
  
              ListTile(
                title: Text('QQ会员',
                    style: TextStyle(color: Colors.blue, fontSize: 18)),
                //CircleAvatar 一般用于设置圆形头像
                leading: CircleAvatar(
                  child: Icon(Icons.question_answer),
                ),
                onTap: () {
                  Navigator.pop(context);
                },
              ),
              ListTile(
                title: Text('我的相册',
                    style: TextStyle(color: Colors.blue, fontSize: 18)),
                leading: CircleAvatar(child: Icon(Icons.people)),
                onTap: () {
                  Navigator.pop(context);
                },
              ),
              ListTile(
                title: Text('我的文件',
                    style: TextStyle(color: Colors.blue, fontSize: 18)),
                leading: CircleAvatar(
                  child: Icon(Icons.list),
                ),
                onTap: () {
                  Navigator.pop(context);
                },
              ),
            ],
          ),
        )),
        endDrawer: Drawer(
          child: Text(
            '右侧侧边栏',
            style: TextStyle(color: Colors.blue, fontSize: 22),
          ),
        ),
      );
    }
  }

实在找不到好看的图片,随便找了一张女神的图片O(∩_∩)O。通过 UserAccountsDrawerHeader 确实能比较快速构建出一个带有顶部侧边栏样式的效果,otherAccountsPictures 属性也尽可能的让我们去配置更多自定义的样式,但使用起来还是比较模板化,效果如下:

UserAccountsDrawerHeader.gif

三、 DrawerHeader 定制侧边栏头部样式

上面使用了 UserAccountsDrawerHeader 去配置了侧边栏头部样式,效果也非常不错,但使用起来比较还是比较固定化,下面我们来通过 DrawerHeader 去定制自己的头部样式:

DrawerHeader 常用属性
    属性                       描述

    decoration                 设置顶部背景颜色
    
    child                      配置子元素
    
    padding                    内边距
    
    margin                     外边距

DrawerHeader 中可以方便的使用 child 属性去定制自己样式,下面我们来简单修改下上面的案例。

  import 'package:flutter/material.dart';
  
  class HomePage extends StatefulWidget {
    HomePage({Key key}) : super(key: key);
  
    _HomePageState createState() => _HomePageState();
  }
  
  class _HomePageState extends State {
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        backgroundColor: Colors.blue,
        body: Container(
          child: Center(
              child: Text(
            "主页",
          )),
        ),
  
        drawer: Drawer(
            child: Container(
          //加个背景
          color: Colors.deepPurpleAccent,
  
          child: ListView(
            children: [
              
              DrawerHeader(
                
                  // 更多自定义样式就在这里 DIY 吧
                  child: Column(
                    children: [
                      Row(
                        children: [
                          Icon(
                            Icons.build,
                            color: Colors.deepOrange,
                          ),
                          SizedBox(width: 10),
                          Text("红红",
                              style: TextStyle(
                                  color: Colors.deepOrange, fontSize: 22)),
                        ],
                      ),
                      Row(
                        children: [
                          Icon(
                            Icons.select_all,
                            color: Colors.deepOrange,
                          ),
                          SizedBox(width: 10),
                          Text("桐桐",
                              style: TextStyle(color: Colors.blue, fontSize: 18)),
                        ],
                      ),
                    ],
                  ),
                  
                  //设置背景
                  decoration: BoxDecoration(
                      color: Colors.yellow,
                      image: DecorationImage(
                          image: NetworkImage(
                              "https://gss1.bdstatic.com/-vo3dSag_xI4khGkpoWK1HF6hhy/baike/awhcrop%3D200%2C200/sign=a13391550fe939014048d06d098324da/4b90f603738da977e4f8af5ebe51f8198718e3f4.jpg"),
                          fit: BoxFit.cover))),
              ListTile(
                title: Text('QQ会员',
                    style: TextStyle(color: Colors.blue, fontSize: 18)),
                //CircleAvatar 一般用于设置圆形头像
                leading: CircleAvatar(
                  child: Icon(Icons.question_answer),
                ),
                onTap: () {
                  Navigator.pop(context);
                },
              ),
              ListTile(
                title: Text('我的相册',
                    style: TextStyle(color: Colors.blue, fontSize: 18)),
                leading: CircleAvatar(child: Icon(Icons.people)),
                onTap: () {
                  Navigator.pop(context);
                },
              ),
              ListTile(
                title: Text('我的文件',
                    style: TextStyle(color: Colors.blue, fontSize: 18)),
                leading: CircleAvatar(
                  child: Icon(Icons.list),
                ),
                onTap: () {
                  Navigator.pop(context);
                },
              ),
            ],
          ),
        )),
        endDrawer: Drawer(
          child: Text(
            '右侧侧边栏',
            style: TextStyle(color: Colors.blue, fontSize: 22),
          ),
        ),
      );
    }
  }

我这里写的比较简单,你可以根据你想要的样式在 child 属性中进行 DIY ,效果如下:

DrawerHeader.gif

四、 侧边栏的路由跳转

侧边栏往往都是作为菜单按钮显示,并不是单纯的只显示些信息就OK 了,如果我想点击侧边栏上的按钮进行页面跳转并能够自动关闭侧边栏那该怎么办呢?

前面章节我们讲到 通过路由可以实现页面跳转,通过路由也可以关闭页面。其实你可以理解为侧边栏也是一个单独的页面,那我们同样使用跳转和关闭页面的方式来进行关闭侧边栏和跳转:

   onTap: (){

      Navigator.of(context).pop();

      Navigator.pushNamed(context, '/目标页');

   }

开发中可能会遇到需要监听侧边栏开启和关闭的状态回调事件,你可能会发现源码中有 DrawerController 且 有 drawerCallback回调函数可以接收状态,看起来似乎能满足这点需求啊,于是你开心的写出了类似如下代码:

 drawer: DrawerController(
    child: Drawer(
       child: Text( '侧边栏', style: TextStyle(color: Colors.blue, fontSize: 22),
    ),
  ),
    drawerCallback: (isOpen) {
      print('打开状态:$isOpen');
    },
  ),

当点击AppBar中左边的按钮时发现,弹出了一个蒙版,然而 Drawer 侧边栏并没有显示出来,尼玛啊,还是高兴太早了,这个问题该怎么去解决呢? 请继续往下看O(∩_∩)O

五、 侧边栏 开 & 关 监听

怎么去解决第四点中遇到的问题呢? 首先我们还是去先看下 Drawer 中的源码是怎么回事,点击 drawer 属性追溯到 Scaffold 中去看Scaffold 到底是怎么定义Drawer 的,Scaffold 类还是有几千行,下面我们来着重看下面两个方法:

  void _buildDrawer(List children, TextDirection textDirection) {
    if (widget.drawer != null) {
      assert(hasDrawer);                   // 标记1
      _addIfNonNull(                       // 标记 2
        children,
        DrawerController(                  //标记3
          key: _drawerKey,
          alignment: DrawerAlignment.start,        //标记4
          child: widget.drawer,
          drawerCallback: _drawerOpenedCallback,
          dragStartBehavior: widget.drawerDragStartBehavior,
          scrimColor: widget.drawerScrimColor,
        ),
        _ScaffoldSlot.drawer,
        // remove the side padding from the side we're not touching
        removeLeftPadding: textDirection == TextDirection.rtl,
        removeTopPadding: false,
        removeRightPadding: textDirection == TextDirection.ltr,
        removeBottomPadding: false,
      );
    }
  }
  
  void _buildEndDrawer(List children, TextDirection textDirection) {
    if (widget.endDrawer != null) {
      assert(hasEndDrawer);
      _addIfNonNull(
        children,
        DrawerController(
          key: _endDrawerKey,
          alignment: DrawerAlignment.end,
          child: widget.endDrawer,
          drawerCallback: _endDrawerOpenedCallback,
          dragStartBehavior: widget.drawerDragStartBehavior,
          scrimColor: widget.drawerScrimColor,
        ),
        _ScaffoldSlot.endDrawer,
        // remove the side padding from the side we're not touching
        removeLeftPadding: textDirection == TextDirection.ltr,
        removeTopPadding: false,
        removeRightPadding: textDirection == TextDirection.rtl,
        removeBottomPadding: false,
      );
    }
  }

从名字上就能猜出 _buildDrawer_buildEndDrawer 两个方法就是分别创建 左侧边栏和右侧边栏的关键方法,实现方式都是一直的,关键点 DrawerAlignment.start / end 实现了左边和右边的区分。原理都一样,那我们来分析下左边侧边栏就可以了:

首先在_buildDrawer 中判断 drawer是否为空(标记1),这个肯定能满足不为空,除非你并没设置侧边栏或者赋值了一个 null,这里先过,然后在不为空的情况下_addIfNonNull方法将它添加到 children 里面(标记2),继续往下看,DrawerController,哎哟我去这玩意在这里出现了,也就是说 Scaffold 中帮我们自动添加了一个 DrawerController ,而且 _drawerOpenedCallback还是定义为私有的,晕啊!! 现在问题发现了,也就是说在第四点中讲到的再通过 DrawerController 去实现监听自然是不可行了,你再添加 一个 DrawerController 就会导致 需要滑动两次才能出现我们的侧边栏。

此路不通,只能另想它法了,苦恼思索中............要是我们的 Drawer 能像 Android 中的 Activity 一样具有生命周期那该多好啊。哎呀呀,生命周期??? 哈哈灵感来了,前面章节中我们讲过了 StatelessWidgetStatefulWidget 的区别知道,StatefulWidget 是具有生命周期的。从源码中知道 Drawer 是继承的 StatelessWidget ,生命周期就 只有build,而 StatefulWidget 则不然,从创建到销毁皆有回调,忘了的同学请回顾前面的知识点:Flutter基础第四章

有了上面的想法,我们来将 Drawer 进行依葫芦画瓢,首先 新建一个 AutoDrawer 类,并继承 StatefulWidget,并在生命周期中(创建/销毁)时处理回调,其他可以直接原封不动抄下来。

  import 'package:flutter/foundation.dart';

  class AutoDrawer extends StatefulWidget {
  
    final double elevation;
    final Widget child;
    final String semanticLabel;
    //定义显示宽度百分百比 默认60%
    final double widthPercent;
    final DrawerCallback callback;
     AutoDrawer({
      Key key,
      this.elevation = 16.0,
      this.child,
      this.semanticLabel,
      this.widthPercent = 0.6,
      this.callback,
    })  :super(key: key);
    @override
    _AutoDrawerState createState() => _AutoDrawerState();
  }
  
  class _AutoDrawerState extends State {
  
    @override
    void initState() {
      if(widget.callback!=null){
        //打开侧边栏 触发
        widget.callback(true);
      }
      super.initState();
    }
    @override
    void dispose() {
      if(widget.callback!=null){
        // 关闭侧边栏触发
        widget.callback(false);
      }
      super.dispose();
    }
  
    @override
    Widget build(BuildContext context) {
      assert(debugCheckHasMaterialLocalizations(context));
      String label = widget.semanticLabel;
      switch (defaultTargetPlatform) {
        case TargetPlatform.iOS:
          label = widget.semanticLabel;
          break;
        case TargetPlatform.android:
        case TargetPlatform.fuchsia:
          label = widget.semanticLabel ?? MaterialLocalizations.of(context)?.drawerLabel;
      }
      final double _width = MediaQuery.of(context).size.width * widget.widthPercent;
      return Semantics(
        scopesRoute: true,
        namesRoute: true,
        explicitChildNodes: true,
        label: label,
        child: ConstrainedBox(
          constraints: BoxConstraints.expand(width: _width),
          child: Material(
            elevation: widget.elevation,
            child: widget.child,
          ),
        ),
      );
    }
  }

上面具有生命周期的 Drawer 就写完了,下面来实际运用下。

  import 'package:flutter/foundation.dart';
  import 'package:flutter/material.dart';

  class HomePage extends StatefulWidget {
    HomePage({Key key}) : super(key: key);

    HomePageState createState() => HomePageState();
  }

  class HomePageState extends State {
    void handlerDrawerButton() {

    }
    @override
    Widget build(BuildContext context) {

      return Scaffold(
          appBar: AppBar(
            title: Text("呆萌"),
          ),
        backgroundColor: Colors.blue,
        body: Container(
          child: Center(
            child: Text("主页"),
          ),
        ),
       // drawer: LeftDrawerUserAccountsDrawerHeader(),
       // drawer: LeftDrawerHeader(),
        // 使用包装有监听的回调的侧边栏
        drawer: WithListenerDrawer(),
        endDrawer: Drawer(
          child: Text(
            '右侧侧边栏', style: TextStyle(color: Colors.blue, fontSize: 22),
          ),
        ),
      );
    }
  }


  class WithListenerDrawer extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return AutoDrawer(

          // 添加打开和关闭的回调监听
          callback: (value){
            print(value);
          },
          child: Container(
            //加个背景
            color: Colors.deepPurpleAccent,

            child: ListView(
              children: [
                DrawerHeader(
                  // 更多自定义样式就在这里 DIY 吧
                    child: Column(
                      children: [
                        Row(
                          children: [
                            Icon(
                              Icons.build, color: Colors.deepOrange,
                            ),
                            SizedBox(width: 10),
                            Text("红红", style: TextStyle(color: Colors.deepOrange, fontSize: 22)),
                          ],
                        ),
                      ],
                    ),

                    //设置背景
                    decoration: BoxDecoration(
                        color: Colors.yellow,
                        image: DecorationImage(
                            image: NetworkImage(
                                "https://gss1.bdstatic.com/-vo3dSag_xI4khGkpoWK1HF6hhy/baike/awhcrop%3D200%2C200/sign=a13391550fe939014048d06d098324da/4b90f603738da977e4f8af5ebe51f8198718e3f4.jpg"),
                            fit: BoxFit.cover))),

                ListTile(
                  title: Text('QQ会员',
                      style: TextStyle(color: Colors.blue, fontSize: 18)),
                  //CircleAvatar 一般用于设置圆形头像
                  leading: CircleAvatar(
                    child: Icon(Icons.question_answer),
                  ),
                  onTap: () {
                    Navigator.pop(context);
                  },
                ),
                ListTile(
                  title: Text('我的相册',
                      style: TextStyle(color: Colors.blue, fontSize: 18)),
                  leading: CircleAvatar(child: Icon(Icons.people)),
                  onTap: () {
                    Navigator.pop(context);
                  },
                ),
                ListTile(
                  title: Text('我的文件',
                      style: TextStyle(color: Colors.blue, fontSize: 18)),
                  leading: CircleAvatar(
                    child: Icon(Icons.list),
                  ),
                  onTap: () {
                    Navigator.pop(context);
                  },
                ),
              ],
            ),
          )
      );
    }
  }



  // 自定义测侧边栏Widget
  class AutoDrawer extends StatefulWidget {

    final double elevation;
    final Widget child;
    final String semanticLabel;
    //定义显示宽度百分百比 默认60%
    final double widthPercent;
    final DrawerCallback callback;
     AutoDrawer({
      Key key,
      this.elevation = 16.0,
      this.child,
      this.semanticLabel,
      this.widthPercent = 0.6,
      this.callback,
    })  :super(key: key);
    @override
    _AutoDrawerState createState() => _AutoDrawerState();
  }

  class _AutoDrawerState extends State {

    @override
    void initState() {
      if(widget.callback!=null){
        //打开侧边栏 触发
        widget.callback(true);
      }
      super.initState();
    }
    @override
    void dispose() {
      if(widget.callback!=null){
        // 关闭侧边栏触发
        widget.callback(false);
      }
      super.dispose();
    }

    @override
    Widget build(BuildContext context) {
      assert(debugCheckHasMaterialLocalizations(context));
      String label = widget.semanticLabel;
      switch (defaultTargetPlatform) {
        case TargetPlatform.iOS:
          label = widget.semanticLabel;
          break;
        case TargetPlatform.android:
        case TargetPlatform.fuchsia:
          label = widget.semanticLabel ?? MaterialLocalizations.of(context)?.drawerLabel;
      }
      final double _width = MediaQuery.of(context).size.width * widget.widthPercent;
      return Semantics(
        scopesRoute: true,
        namesRoute: true,
        explicitChildNodes: true,
        label: label,
        child: ConstrainedBox(
          constraints: BoxConstraints.expand(width: _width),
          child: Material(
            elevation: widget.elevation,
            child: widget.child,
          ),
        ),
      );
    }
  }

在 AutoDrawer 生命周期 初始化 / 销毁时 分别通过callback 进行了状态回调。此时我们应该就可以准确的拿到侧边栏状态回调,具体行不行呢?运行看看效果:

drawer监听.gif

控制台也打印出了侧边栏的 开 / 关 状态信息:

    I/flutter ( 4944): true
    I/flutter ( 4944): false
    I/flutter ( 4944): true
    I/flutter ( 4944): false

哈哈,果然实现了,手动call 666 ........

骚等... 这里怎么出现了两个顶部导航栏 (AppBar),还记得我们在 前面章节中对 Tabs 类中的 Scaffold 也添加了一个 AppBar吗?也就是在外层我们添加了一个导航栏,然后又在 HomePage 页面又添加了一个导航栏,所以出现了两个。 下面先将 Tabs 中的 AppBar 去掉,再次运行:

drawer监听2.gif

外部的 AppBar 被移除后,果然只显示了一个导航栏。效果逐渐开始完美,美中不足的是 我想替换左侧顶部按钮图片,然后点击图片也能打开侧边栏。在上面的例子中好像并没有手动去调用打开侧边的方法吧 ? 确实是这样的,我们只是 添加了一个 AppBar ,Flutter就自动给我们加上了一个侧边的开关按钮,且能控制侧边栏。现在我们要替换这个自带的按钮,且要自己手动去触发打开侧边栏,那该怎么办呢?

分析发现其实就只有两步: 1 、 替换顶部左边按钮图片 2、绑定事件,点击按钮图片触发打开侧边栏。

第一步其实很简单,在前面章节中就有讲到,在 AppBar 中配置 leading 配置属性添加一个按钮图片即可。

   appBar: AppBar(
      title: Text("呆萌"),
      leading: IconButton(
          icon: new Icon(Icons.settings),
          // 添加事件
          onPressed: (){
            
            // TODO 
          },
        ),
    ),

第二步中就遇到一个问题,也就是我们要怎么才能打开侧边栏?没办法,确实不知道,只能从源码下手查看 AppBar 是怎样调度的,进入 AppBar 发现有下面这段代码:

   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();
      }
    }

首先 判断了 leading 这个为不为空,在我们没有配置 leading 属性的情况下肯定能进入该逻辑,其次判断是否有添加侧边栏,自然是有添加,然后跟进,发现 onPressed: _handleDrawerButton, 这么一行,也就是点击事件会走 _handleDrawerButton 这里面的逻辑。下面我们跟进看下:

  void _handleDrawerButton() {
    Scaffold.of(context).openDrawer();
  }

  void _handleDrawerButtonEnd() {
    Scaffold.of(context).openEndDrawer();
  }

我去这两个方法这么简单,就一行代码,可以看出 内部是通过 Scaffold.of(context).openDrawer(); 打开左侧边栏,Scaffold.of(context).openEndDrawer(); 是打开右侧边栏。

知道打开侧边的方式了,下面来将我们代码也加上。

  appBar: AppBar(
      title: Text("呆萌"),
      leading: IconButton(
         // 修改左边按钮
          icon: new Icon(Icons.settings),
          onPressed: (){
            Scaffold.of(context).openDrawer();
          },
        ),
    ),

运行:

侧边栏按钮手动.png

按钮成功替换了,但是你会发现此时不管你点击多少次都打不开侧边了,我们也按照内部的实现方式加上了 Scaffold.of(context).openDrawer(); 啊! 为嘛不行呢?

这里需要和前面章节中弹出 SnackBar 提示框的处理方式去处理下。

 appBar: AppBar(
      title: Text("呆萌"),
      leading:Builder(
        builder: (context) => IconButton(
          icon: new Icon(Icons.settings),
          onPressed: () => Scaffold.of(context).openDrawer(),
        ),
      ),
    ),

加上 Builder处理后确实能打开侧边栏了,但还是美中不足,你发现侧边滑出时并没覆盖 底部 导航栏,并没有像QQ 那样完美的效果。分析下 我们的侧边栏是在 HomePage 页面中添加的,也就是说我们只有在 首页 中能打开侧边栏。而更多的是我们无论是在 首页 、社区、还是个人中心,都希望能滑动或者点击按钮打开,那我们要怎样去改造呢?

其实很简单嘛,就是 HomePage 中局部的侧边栏提升到外部容器中即可解决该问题,那下面来将我们的 Tabs 中也加上侧边栏。

  import 'package:flutter/material.dart';

  //引入准备好的界面
  import 'tabs/CommunityPage.dart';
  import 'tabs/PersionPage.dart';
  import 'tabs/HomePage.dart';



  class Tabs extends StatefulWidget {

    _TabsState createState() => _TabsState();

  }

  class _TabsState extends State {

    //默认选中的索引
    int _currentIndex=0;

    //将页面封装在集合中,方便通过索引取出
    List _pageList=[
      HomePage(),
      CommunityPage(),
      PersionPage(),
    ];


    @override
    Widget build(BuildContext context) {
      return Scaffold(
          body: this._pageList[this._currentIndex],

          //appBar: AppBar(
          //  title: Text("呆萌"),
          // ),


          drawer: WithListenerDrawer(),

          bottomNavigationBar: BottomNavigationBar(

            //配置选中的索引值
            currentIndex: this._currentIndex,

            onTap: (int index){
              //改变状态
                setState(() {
                    this._currentIndex=index;
                });
            },
            //icon的大小
            iconSize:36.0,
            //选中的颜色
            fixedColor:Colors.red,

            selectedFontSize: 18,

            selectedLabelStyle: TextStyle(color: Colors.red),

            // selectedItemColor: Colors.red, 和 fixedColor互斥

            unselectedFontSize: 18,

            unselectedItemColor:Colors.deepPurple ,

            unselectedLabelStyle: TextStyle(color: Colors.deepPurple),

            showUnselectedLabels: true,

            //配置底部tabs为固定效果,即没有点击后的凸出感
            type:BottomNavigationBarType.fixed,

            // BottomNavigationBarItem 包装的底部按钮
            items: [

              BottomNavigationBarItem(

                icon: Icon(Icons.home),
                title: Text("首页")

              ),
               BottomNavigationBarItem(
                icon: Icon(Icons.question_answer),
                title: Text("社区")
              ),

               BottomNavigationBarItem(
                icon: Icon(Icons.people),
                title: Text("个人中心")
              )
            ],
          ),
        );
    }
  }

其实 就这么 drawer: WithListenerDrawer(), 一行,因为做了简单的封装嘛,在开发中尽量的把功能代码抽取出来,可以减少很多冗余代码,后面维护也方便!! 闲话不多说,看看效果吧:

侧滑最终效果.gif

可以看出 QQ 侧边栏那样的效果就实现了,我这里只是把 drawer 提取出去了,其实 AppBar 也应该放到外部去,更多细节就你自己去优化吧。

本篇文章幅度有点偏长了,下面回顾下重点知识点:

1、drawer属性 可以定义左侧边栏,endDrawer 属性可定义右侧边栏。

2、侧边栏的关闭: Navigator.pop(context);

3、怎么实现带有监听效果Drawer 自定义组件

4、 打开左侧边栏 Scaffold.of(context).openDrawer(); ,打开右侧边栏 Scaffold.of(context).openEndDrawer();

5、自定义侧边栏开关按钮 需要 Builder 处理

6、怎样实现QQ 效果的全局侧滑。

好了本章节到此结束,又到了说再见的时候了,如果你喜欢请留下你的小红星,你们的支持才是创作的动力,如有错误,请热心的你留言指正, 谢谢大家观看,下章再会 O(∩_∩)O

实例源码地址: https://github.com/zhengzaihong/flutter_learn

你可能感兴趣的:(Flutter第七章(Drawer 侧边栏,UserAccountsDrawerHeader,DrawerHeader以及案例 ))