Flutter领航系列篇02

上一回 Flutter领航系列篇01 我们对Flutter进行了基本的介绍。
学了基础控件Scaffold和Text,还学了一个无状态组件StatelessWidget,通过这几个组件构建了一个应用的基本界面。
这一回我们接着上一回完善侧边抽屉界面,把导航加上,顺便把导航玩儿出一点新花样来。

1. 有/无状态控件:

我们先简单讲下上回留下的控件状态话题,让大伙儿对控件状态有个大致的印象。
在Flutter中控件按状态划分,可以分为两类:

  • StatelessWidget,表示无状态的控件。
    1. 表示纯展示型的控件
    2. 无状态内部必须有一个build函数,用来渲染UI
    3. 每一个项目必须有一个MaterialApp
  • StatefulWidget,表示有状态的控件
    1. 打个比方,如果需要私有数据,例如:
      请求返回的数据,那就需要声明有状态的控件

按照这个分法,大伙儿可以看出来咱们目前代码继承的是无状态控件。
好,咱们先热热身,敲个小代码:

actions: <Widget>[
  IconButton(
    icon: Icon(Icons.search),
    onPressed: () {}
  )
],

把这段代码,加入AppBar()。
按下R键刷新一下,可以看到界面的右上角出现了一个搜索小图标,挺有意思吧。

2. 抽屉界面:

接下来,咱们把上回的抽屉界面(drawer)完善一下。
这里我们需要讲细一点:

  1. listView控件:往drawer()里加入listView控件
  2. children属性:listview里有一个children属性,是一个控件数组,布局元素就放在这个数组里边儿。
  3. UserAccountsDrawerHeader控件:
    • 这里我们可以选择往children控件数组里边儿放入UserAccountsDrawerHeader()控件。
    • UserAccountsDrawerHeader()控件里边儿有两个必填属性:accountEmail、accountName
    • 如果有头像需求,还可以选择加入一个currentAccountPicture属性,如果头像需要是圆形的,那么代码如下:
currentAccountPicture:CircleAvatar(
  backgroundImage: NetworkImage('[https://qcloud.dpfile.com/pc/bMbudG0HNlcgJ9mCH9uT_KG-8Lkov5fWOq0Vw93525g-OrTcCi-L0gXpZGLYEEcFTYGVDmosZWTLal1WbWRW3A.jpg](https://qcloud.dpfile.com/pc/bMbudG0HNlcgJ9mCH9uT_KG-8Lkov5fWOq0Vw93525g-OrTcCi-L0gXpZGLYEEcFTYGVDmosZWTLal1WbWRW3A.jpg)')
),

这里一一解释一下:

  • CircleAvatar:表示圆形头像
  • backgroundImage:背景图
  • NetworkImage:表示是网络图片,这里我在网上随便找了张图

这个时候大家不妨运行试试看,可以看到效果如下:

avatar

目前的背景是蓝色,若是期望再加个背景图的话,可以再加一段儿背景图。
代码如下:

decoration: BoxDecoration(
  image: DecorationImage(
    fix: BoxFix.cover,
    image: NetworkImage('https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3672957379,325248742&fm=26&gp=0.jpg')
  )
),
  • decoration 属性对当前控件的做装饰美化的,他有一个BoxDecoration盒子装饰器,这里边儿的image就是背景图片。
  • fit 属性是控制填充模式的,BoxFix.cover 让图片适配到容器边缘。
    如果不明白,注释掉,对比一下效果就知道了。

好啦,这个时候,我们接着加入ListTitle()控件,代码如下:

ListTile(title: Text('我喜欢的'), trailing: Icon(Icons.favorite)),
ListTile(title: Text('我收藏的'), trailing: Icon(Icons.fastfood)),
Divider(),
ListTile(title: Text('个人信息'), trailing: Icon(Icons.face))
  • ListTile 控件就是每一项,trailing看英文意思也知道是后面的项,icon组件就不用说了吧。
  • Divider 看英文意思也知道是分割线啦。
3. 小Tips:
  1. 大家可以看到左上角时间6:35那个地方,是留白的。
    如果不想留白的话,可以往child: ListView()里边儿放一段儿
    padding:EdgeInsets.all(0),
    这里padding的参数不能是数值,不能像前端写css一样直接padding:0
  2. 加了背景图之后,可以看到字是黑色的,效果不太明显。可以在text组件里调整颜色,例如:
var color = TextStyle(color: Colors.white);
UserAccountsDrawerHeader(accountEmail: Text('data@data.com', style: color),accountName: Text('飞狐', style: color),
侧边栏

整体代码如下:

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var color = TextStyle(color: Colors.white);
    var avatar = 'https://qcloud.dpfile.com/pc/bMbudG0HNlcgJ9mCH9uT_KG-8Lkov5fWOq0Vw93525g-OrTcCi-L0gXpZGLYEEcFTYGVDmosZWTLal1WbWRW3A.jpg';
    var bgImage = 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3672957379,325248742&fm=26&gp=0.jpg';
    return MaterialApp(
      title: '南帝',
      theme: ThemeData(primarySwatch: Colors.lightBlue),
      home: Scaffold(
        appBar: AppBar(
          title: Text('北丐'),
          actions: <Widget>[
            IconButton(icon: Icon(Icons.search),onPressed: () {})
          ],
        ),
        body: Center(child: Text('中神通')),
        floatingActionButton: FloatingActionButton(onPressed: () {},child: Text('东邪')),
        drawer: Drawer(
          child: ListView(
            padding: EdgeInsets.all(0),
            children: <Widget>[
              UserAccountsDrawerHeader(accountEmail: Text('data@data.com', style: color),accountName: Text('飞狐', style: color),
              currentAccountPicture: 
                CircleAvatar(backgroundImage: NetworkImage(avatar)),
                decoration: BoxDecoration(
                  image: DecorationImage(
                    fit: BoxFit.cover,
                    image: NetworkImage(bgImage)
                  )
                ),
              ),
              ListTile(title: Text('我喜欢的'), trailing: Icon(Icons.favorite)),
              ListTile(title: Text('我收藏的'), trailing: Icon(Icons.fastfood)),
              Divider(),
              ListTile(title: Text('个人信息'), trailing: Icon(Icons.face))
            ],
          )
        ),
      ),
    );
  }
}

侧边栏就告一段落啦,接下来,我们开始写导航栏

4. 底部导航前奏-代码拆分

底部的导航实现其实挺简单。不过在写导航之前,咱得先改下结构。
目的是把 home 控件拎出来,使其独立成一个类,代码如下:

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '周伯通',
      theme: ThemeData(primarySwatch: Colors.lightBlue),
      home: MyHomePage()
    );
  }
}
class MyHomePage extends StatelessWidget {
  // 省略Scaffold的代码咯...
}

可以看到,主要就两个步骤:

  1. 新写一个 MyHomePage 类
  2. 把 Scaffold 拆分过去
5. 底部导航实现

底部导航的实现有3个动作:

  • 新增底部导航区域控件(bottomNavigationBar),步骤如下:
    1. 位置放置与 drawer 控件同级
    2. 新增 Container 容器
    3. 往 Container 容器里添加 decoration 修饰容器,比如背景色
    4. 高度属性 height
    5. child 属性为 TabBar 组件
  • 新增 DefaultTabController,步骤如下:
    1. 把 Scaffold 控件作为子节点赋值给 child
    2. 同时增加一个 length 属性,该属性值表示导航栏的 item 项长度,如下:

DefaultTabController(length: 5, child: Scaffold())

  • body 属性改为 TabBarView,放置的就是内容层

至此,整个完成,代码如下:

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '周伯通',
      theme: ThemeData(primarySwatch: Colors.lightBlue),
      home: MyHomePage()
    );
  }
}
class MyHomePage extends StatelessWidget {
  var color = TextStyle(color: Colors.white);
  var avatar = 'https://qcloud.dpfile.com/pc/bMbudG0HNlcgJ9mCH9uT_KG-8Lkov5fWOq0Vw93525g-OrTcCi-L0gXpZGLYEEcFTYGVDmosZWTLal1WbWRW3A.jpg';
  var bgImage = 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3672957379,325248742&fm=26&gp=0.jpg';
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 5,
      child: Scaffold(
        appBar: AppBar(
          title: Text('北丐'),
          actions: <Widget>[
            IconButton(icon: Icon(Icons.search),onPressed: () {})
          ],
        ),
        body: TabBarView(
          children: <Widget>[
            Text('金融'),
            Text('医疗'),
            Text('中间'),
            Text('量化'),
            Text('我的')
          ],
        ),
        floatingActionButton: FloatingActionButton(onPressed: () {}, child: Text('东邪')),
        drawer: Drawer(
          child: ListView(
            padding: EdgeInsets.all(0),
            children: <Widget>[
              UserAccountsDrawerHeader(accountEmail: Text('data@data.com', style: color),accountName: Text('飞狐', style: color),
              currentAccountPicture: 
                CircleAvatar(backgroundImage: NetworkImage(avatar)),
                decoration: BoxDecoration(
                  image: DecorationImage(
                    fit: BoxFit.cover,
                    image: NetworkImage(bgImage)
                  )
                ),
              ),
              ListTile(title: Text('我喜欢的'), trailing: Icon(Icons.favorite)),
              ListTile(title: Text('我收藏的'), trailing: Icon(Icons.fastfood)),
              Divider(),
              ListTile(title: Text('个人信息'), trailing: Icon(Icons.face))
            ],
          )
        ),
        bottomNavigationBar: Container(
          decoration: BoxDecoration(color: Colors.lightBlue),
          height: 60,
          child: TabBar(
            labelStyle: TextStyle(height: 0.5, fontSize: 12),
            tabs: <Widget>[
              Tab(icon: Icon(Icons.memory), text: '医疗'),
              Tab(icon: Icon(Icons.money_off), text: '金融'),
              Tab(icon: Icon(Icons.add_circle, size: 52, color: Colors.white)),
              Tab(icon: Icon(Icons.high_quality), text: '量化'),
              Tab(icon: Icon(Icons.my_location), text: '我的')
            ],
          ),
        ),
      ),
    );
  }
}

大家可以运行看看,导航部分效果如下:


导航栏
6. 小彩蛋-底部导航凹凸效果

有些APP,导航栏中间部分是有凹凸效果的,其实也不难实现。
飞狐在这里把部分代码给大家演示一下,具体封装成一个很好的控件就是个细活儿了。
替换 bottomNavigationBar,代码如下:

        bottomNavigationBar: BottomAppBar(
          child: SizedBox(
            height: 60,
            child: Row(
              mainAxisSize: MainAxisSize.max,
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: <Widget>[
                Expanded(child: Tab(icon: Icon(Icons.memory), text: '医疗',), flex: 1),
                Expanded(child: Tab(icon: Icon(Icons.money_off), text: '金融',), flex: 2,),
                Expanded(child: Tab(icon: Icon(Icons.high_quality), text: '量化',), flex: 2),
                Expanded(child: Tab(icon: Icon(Icons.my_location), text: '我的',), flex: 1),
              ],
            ),
          ),
          shape: CircularNotchedRectangle(),
          color: Colors.lightBlueAccent,
        ),

关键点,shape 就是凹陷代码部分。
还没完,接着把 floatingActionButton 部分代码替换如下:

floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: FloatingActionButton(onPressed: () {}, child: Icon(Icons.add)),

关键点,floatingActionButtonLocation 让浮动图标居中至导航栏中部。
好啦,大家刷新看效果吧。

导航栏凹凸效果

这一回我们讲的控件有点多,难度适中,需要小伙伴儿们好好消化一下。
复盘一下,这一回我们实现了区分了组件状态,抽屉界面,导航栏。
下一篇我们会讲到内容列表的实现。

好啦,下回见。

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

推荐阅读更多精彩内容