Flutter之旅(二)—Material风格的界面结构:AppBar、TabBar、Drawer及BottomNavigationBar

2,374 阅读5分钟

Flutter之旅(一)-Flutter项目架构、HelloWord及ListView

AppBar

pubspec.yaml文件的dependencies节点下添加fluttertoast库以用来演示使用

# dependencies节点下主要引用第三方库
dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  fluttertoast: ^8.2.1

左侧添加按钮交互

appBar添加左侧菜单按钮的action是通过leading小部件,并通过IconButton添加按钮图标和点击onPressed方法,如下:

class ViewStructure extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey[300],
      appBar: AppBar(
        leading: IconButton( //通过leading小部件添加菜单按钮
          icon: const Icon(Icons.menu),
          tooltip: 'menu', //修饰说明
          onPressed: () => Fluttertoast.showToast(msg: "menu"),
        ),
        title: const Text('Flutter界面结构'),
        elevation: 0.0, // 去除appbar下的阴影
      ),
      body: null,
    );
  }
}

Simulator Screen Shot - iPhone 14 Pro - 2023-03-29 at 01.07.01.png

appBar右侧添加按钮交互

appBar右侧添加交互我们可以通过actions来实现,在title属性下面添加如下:

class ViewStructure extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey[300],
      appBar: AppBar(
        leading: IconButton( //通过leading小部件添加菜单按钮
          icon: const Icon(Icons.menu),
          tooltip: 'menu', //修饰说明
          onPressed: () => Fluttertoast.showToast(msg: "menu"),
        ),
        title: const Text('Material风格界面结构'),
        actions: <Widget>[ //右边通过actions设置一系列动作
          IconButton(
            icon: const Icon(Icons.search),
            tooltip: 'Search',
            onPressed: () => Fluttertoast.showToast(msg: "search"),
          )
        ],
        elevation: 0.0, // 去除appbar下的阴影
      ),
      body: null,
    );
  }
}

actions属性中可以继续添加IconButton添加多个交互事件:

image.png

TabBar、TabView及TabController

TabBar就是Tab(选项卡或标签)组成的导航栏,如果熟悉Android开发的同学可以理解它就是对应着Android开发中的TabLayout,在Flutter中要达到标签导航切换的功能需要TabbarTabViewTabController系一起来实现。

TabController

我们基于上一小节的代码添加一个tab标签选择切换对应ui的功能,首先需要添加TabController,这里使用框架自带的DefaultTabController,并通过length属性设置为3个tab,如下:

image.png 这里可以见到Scaffold小部件被DefaultTabController包裹。

TabBar

appBar中的通过bottom属性在title下添加TabBar小部件,TabBar中可以通过tabs设置标签的内容,并对标签的效果,如选中颜色或指示器进行调整:

bottom: const TabBar(
  //TabBar
  unselectedLabelColor: Colors.black38,
  //未被选择的标签的颜色
  indicatorColor: Colors.yellow,
  //标签选择指示器颜色
  indicatorSize: TabBarIndicatorSize.label,
  //设置指示器长度和tab长度一样
  indicatorWeight: 2.0,
  //指示器的高度
  tabs: <Widget>[
    //设置每个tab的icon或文本
    Tab(icon: Icon(Icons.car_crash)),
    Tab(icon: Icon(Icons.cabin)),
    Tab(icon: Icon(Icons.flag)),
  ],
),

TabView

TabView就是设置标签选项对应的视图内容,类似Android开发中TabLayout切换选中的Fragment,那么TabBarView设置的多个对应Widget相当于一个个Fragment,我们在视图的body中显示,如下:

body: const TabBarView(
  //标签对应的视图
  children: <Widget>[
    //每个标签对应view的内容
    Icon(Icons.car_crash, size: 136.0, color: Colors.lightBlue),
    Icon(Icons.cabin, size: 136.0, color: Colors.yellow),
    Icon(Icons.flag, size: 136.0, color: Colors.red),
  ],
),

整体效果如下:

屏幕录制2023-04-04 00.08.38.gif

Drawer

Android开发中也有Material风格的DrawerLayout,它就是一个侧边栏抽屉的效果,主要是用于侧边栏菜单等,而flutterdrawer效果和DrawerLayout是一样的,我们可以在body下添加一个drawer的属性(如果是endDrawer就代表是在右边显示),然后使用Container小部件添加抽屉侧边栏(drawer)的背景色及添加一个文本,代码如下:

drawer: Container(
  color: Colors.lightBlue,
  padding: const EdgeInsets.all(16.0), //设置Container小部件整体的内边距
  child: Column(
    mainAxisAlignment: MainAxisAlignment.center,
    //设置主轴对齐,可以理解为Column部件中的wiget都整体居中
    children: const <Widget>[
      Text(
        'drawer显示',
        style: TextStyle(color: Colors.white),
      )
    ],
  ),
),

效果如下:

屏幕录制2023-04-04 23.53.38.gif

使用Drawer部件实现抽屉侧边栏

我们把上面的Container小部件换成Drawer,子Widget使用一个ListView来处理:

drawer: Drawer(
  child: ListView(
    padding: EdgeInsets.zero,
    children: <Widget>[],
    ),
 )

<Widget>[]中首先添加一个UserAccountsDrawerHeader添加一个用户信息的View,并设置背景图:

UserAccountsDrawerHeader(
  accountName: const Text('柒叁'),
  accountEmail: const Text('123455678@gmail.com'),
  currentAccountPicture: const CircleAvatar(
    backgroundImage: NetworkImage(
        'https://p3-passport.byteimg.com/img/user-avatar/aede74f1a2d5008f871a8c1b95b2a3ce~180x180.awebp'),
  ),
  decoration: BoxDecoration( //设置UserAccountsDrawerHeader的背景图片
      color: Colors.yellow[200],
      image: DecorationImage(
          fit: BoxFit.cover, //填充满整个区域
          colorFilter: ColorFilter.mode(Colors.lightBlue.withOpacity(0.6),
              BlendMode.srcOver),  //颜色的滤镜
          image: const NetworkImage(
              'https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/04d31e46137f4981b7aaa6f6c83f1cc1~tplv-k3u1fbpfcp-watermark.image?'))),
),

如果需要方便自定义一点可以直接使用DrawerHeader,如下:

DrawerHeader(
  decoration: BoxDecoration(
    //设置header的背景色
    color: Colors.grey[200],
  ),
  child: Column(
    children: <Widget>[
      // const Icon(Icons.people,color: Colors.lightBlue,size: 48),
      ClipOval( //设置圆形切割
        child: Image.network(
          'https://p3-passport.byteimg.com/img/user-avatar/aede74f1a2d5008f871a8c1b95b2a3ce~180x180.awebp',
          width: 80,
          height: 80,
          fit: BoxFit.cover,
        ),
      ),
      const Text('柒叁'),
    ],
  ),
),

UserAccountsDrawerHeader之下通过ListTitle添加三个item,如下:

ListTile(
  title: const Text('Rate us', textAlign: TextAlign.start),
  leading:
      const Icon(Icons.star, color: Colors.lightBlue, size: 32),
  onTap: () => Navigator.pop(context), //点击item关闭抽屉
  // trailing: , 在标题右边添加图片trailing
),
ListTile(
  title: const Text('Share', textAlign: TextAlign.start),
  leading: const Icon(Icons.share,
      color: Colors.lightBlue, size: 32),
  onTap: () => Navigator.pop(context),
  // trailing: ,
),
ListTile(
  title: const Text('Setting', textAlign: TextAlign.start),
  leading: const Icon(Icons.settings,
      color: Colors.lightBlue, size: 32),
  onTap: () => Navigator.pop(context),
  // trailing: ,
),

当我们使用使用Drawer时会自动添加左边的菜单按钮的,并自动点击打开抽屉,我们先把AppBar中设置的leading给注释掉,呈现如下效果:

屏幕录制2023-04-08 16.20.25.gif

BottomNavigationBar

bottomNavigationBar顾名思义就是底部的导航栏,通过BottomNavigationBar小部件的items属性添加一系列的BottomNavigationBarItem得到底部导航栏:

bottomNavigationBar: BottomNavigationBar(
  items: const [
    BottomNavigationBarItem(
      icon: Icon(Icons.home),
      label: "首页",
    ),
    BottomNavigationBarItem(
      icon: Icon(Icons.shop),
      label: "商城",
    ),
    BottomNavigationBarItem(
      icon: Icon(Icons.people),
      label: "个人中心",
    ),
  ],
),

效果如下:

image.png

BottomNavigationBar的还可以设置以下几个属性:

currentIndex: 1,  //设置当前的选中的tab
type: BottomNavigationBarType.fixed,//设置是刚好屏幕内填充
fixedColor: Colors.red,  //设置选中的颜色

一般来说currentIndex这个是动态变化的,所以我们需要使用变量来设置,也就是处理底部导航栏切换选中的状态,这里需要使用到setState方法,setState方法需要使用到StatefulWidget,所以我们需要自定义一个Widget继承StatefulWidget

class BottomNavigationBarDemo extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return BottomNavigationBarDemoState();
  }
}

BottomNavigationBarDemoState继承State

class BottomNavigationBarDemoState extends State<BottomNavigationBarDemo> {
  int _currentIndex = 0;

  //监听状态变化
  void _onTapHandler(int index) {
    setState(() {
      _currentIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return BottomNavigationBar(
      currentIndex: _currentIndex,  //设置当前的选中的tab
      type: BottomNavigationBarType.fixed,//设置是刚好屏幕内填充
      fixedColor: Colors.red,  //设置选中的颜色
      onTap: _onTapHandler,
      // 切换状态的属性
      items: const [
        BottomNavigationBarItem(
          icon: Icon(Icons.home),
          label: "首页",
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.shop),
          label: "商城",
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.person),
          label: "个人中心",
        ),
      ],
    );
  }
}

对于底部导航栏切换状态的变化处理需要做以下几点:

  1. 声明变量_currentIndex;
  2. _currentIndex赋值给BottomNavigationBarcurrentIndex属性;
  3. 声明_onTapHandler方法更新变量_currentIndex的值;
  4. BottomNavigationBaronTap属性添加_onTapHandler方法的调用。

本文介绍的示例代码:view_structure.dart