写点什么

从 0 开始写一个基于 Flutter 的开源中国客户端(5),带你全面理解 View 的绘制流程

用户头像
Android架构
关注
发布于: 刚刚

新建项目

在 AndroidStudio 中,通过File -> New -> New Flutter Project...创建一个新的 Flutter 工程。

使用 MaterialApp 和 Scaffold 组件构建首页

在新创建的 Flutter 工程中,删除lib/main.dart中的代码,并编写下面的代码:


import 'package:flutter/material.dart';


void main() {runApp(new MyApp());}


// MyApp 是一个有状态的组件,因为页面标题,页面内容和页面底部 Tab 都会改变 class MyApp extends StatefulWidget {@overrideState<StatefulWidget> createState() => new MyOSCClientState();}


class MyOSCClientState extends State<MyApp> {@overrideWidget build(BuildContext context) {return new MaterialApp(theme: new ThemeData(// 设置页面的主题色 primaryColor: const Color(0xFF63CA6C)),home: new Scaffold(appBar: new AppBar(// 设置 AppBar 标题 title: new Text("My OSC",// 设置 AppBar 上文本的样式 style: new TextStyle(color: Colors.white)),// 设置 AppBar 上图标的样式 iconTheme: new IconThemeData(color: Colors.white)),body: new Text("MyOSC Client")),);}}


上面的代码中,为 MaterialApp 设置了theme参数,主要是为了改变页面主题颜色为绿色,在 Scaffold 的appBar属性中,为title设置了颜色为白色,如果不设置的话,默认为黑色,appBariconTheme属性也设置为了白色主题,如果不设置的话,AppBar 上的图标默认为黑色。

编写 4 个页面用于切换显示

在新建的 Flutter 项目的lib/目录下,新建一个pages/目录,该目录用于存放 App 中的所有页面,然后分别创建四个.dart 文件:NewsListPage.dart TweetsListPage.dart DiscoveryPage.dart MyInfoPage.dart,代表 App 中首页底部 4 个 Tab 切换时分别显示的页面,这四个页面暂时就在页面正中间显示一行文本,下面是资讯列表NewsListPage.dart代码:


// pages/NewsListPage.dartimport 'package:flutter/material.dart';


// 资讯列表页面 class NewsListPage extends StatelessWidget {@overrideWidget build(BuildContext context) {return new Center(child: new Text("NewsListPage"),);}}


其余三个页面代码跟上面的类似,只是换了类名和 Text 组件的文本。


在上一步中,我们的 Scaffold 组件里的 body 属性只是一个 Text 组件,为了加载上面的 4 个页面,需要用一个容器组件将这 4 个页面装起来,然后在点击 Tab 时切换页面,这就用到了我之前的博文里说到的IndexedStack组件了。IndexedStack 中可以有多个子组件,根据索引值来显示其中某个组件而隐藏其余的组件。


在第一步中MyOSCClientState类中定义两个变量:_tabIndex_body_tabIndex表示当前页面底部选中的 Tab 的索引,_body表示首页 Scaffold 组件的 body 属性值,然后给_tabIndex_body变量赋值,如下代码所示:


// 页面当前选中的 Tab 的索引 int _tabIndex = 0;


// 页面 body 部分组件 var _body = new IndexedStack(children: <Widget>[new NewsListPage(),new TweetsListPage(),new DiscoveryPage(),new MyInfoPage()],index: _tabIndex,);


上面用 IndexedStack 加载了 4 个页面用于切换 Tab 时显示,但是 Tab 我们还没有做出来,Flutter 中为页面添加底部导航 Tab 菜单很简单,已经有很多组件可以用了。

编写页面底部导航 Tab 菜单

给页面添加底部导航 Tab 菜单只需要给 Scaffold 组件添加一个bottomNavigationBar属性即可,这里的bottomNavigationBar我们用 Flutter 提供的 CupertinoTabBar 组件。


CupertinoTabBar 是 Flutter 内置的 iOS 风格的选项卡,用于在页面底部显示几个 Tab,要使用 Cupertino 风格的组件,必须先导入头文件,如下代码:


import 'package:flutter/cupertino.dart';


CupertinoTabBar 组件的用法也比较简单,代码如下:


new CupertinoTabBar(items: getBottomNavItems(),currentIndex: _tabIndex,onTap: (index) {// 底部 TabItem 的点击事件处理,点击时改变当前选择的 Tab 的索引值,则页面会自动刷新 setState((){_tabIndex = index;});},)


其中items是一个List<BottomNavigationBarItem>对象,currentIndex表示当前选中的 Tab 的索引值,onTap是 TabItem 点击事件,上面的代码中,getBottomNavItems()方法代码如下:


List<BottomNavigationBarItem> getBottomNavItems() {List<BottomNavigationBarItem> list = new List();for (int i = 0; i < 4; i++) {list.add(new BottomNavigationBarItem(icon: getTabIcon(i),title: getTabTitle(i)));}return list;}


// 根据索引值确定 Tab 是选中状态的样式还是非选中状态的样式 TextStyle getTabTextStyle(int curIndex) {if (curIndex == _tabIndex) {return tabTextStyleSelected;}return tabTextStyleNormal;}


// 根据索引值确定 TabItem 的 icon 是选中还是非选中 Image getTabIcon(int curIndex) {if (curIndex == _tabIndex) {return tabImages[curIndex][1];}return tabImages[curIndex][0];}


// 根据索引值返回页面顶部标题 Text getTabTitle(int curIndex) {return new Text(appBarTitles[curIndex],style: getTabTextStyle(curIndex));}


由于 TabItem 是由一个图标和一个文本组件构成,所以这里还需要在 MyOSCClientState 类中定义两个变量tabImagesappBarTitlestabImages是一个二维数组,表示 TabItem 中的图标(包括选中和未选中状态的图标),appBarTitles是一个字符串数组,表示每个 TabItem 对应的页面标题,这两个变量的赋值代码如下:


// 页面底部 TabItem 上的图标数组 var tabImages;


// 页面顶部的大标题(也是 TabItem 上的文本)var appBarTitles = ['资讯', '动弹', '发现', '我的'];


// 数据初始化,包括 TabIcon 数据和页面内容数据 void initData() {if (tabImages == null) {tabImages = [[getTabImage('images/ic_nav_news_normal.png'),getTabImage('images/ic_nav_news_actived.png')],[getTabImage('images/ic_nav_tweet_normal.png'),getTabImage('images/ic_nav_tweet_actived.png')],[getTabImage('images/ic_nav_discover_normal.png'),getTabImage('images/ic_nav_discover_actived.png')],[getTabImage('images/ic_nav_my_normal.png'),getTabImage('images/ic_nav_my_pressed.png')]];}}


// 传入图片路径,返回一个 Image 组件 Image getTabImage(path) {return new Image.asset(path, width: 20.0, height:


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


20.0);}


上面的代码中需要注意的是 Image 组件,要使用image/目录下的图片,必须确保项目根目录下的 pubspec.yaml 文件中已经添加了图片的路径,如下图:



如果没有上面的 assets 配置,直接加载图片是会报错的。


为了达到点击 Tab 切换不同的页面的功能,我们需要给 CupertinoTabBar 组件的 onTap 参数配置一个方法,该方法有一个 index 参数,我们将这个 index 赋值给前面定义的_tabIndex即可,并将这个赋值操作放到 setState 中执行,如下代码:


onTap: (index) {// 底部 TabItem 的点击事件处理,点击时改变当前选择的 Tab 的索引值,则页面会自动刷新 setState((){_tabIndex = index;});},


最后放上 MyOSCClientState 类的 build 方法代码:


@overrideWidget build(BuildContext context) {initData();return new MaterialApp(theme: new ThemeData(// 设置页面的主题色 primaryColor: const Color(0xFF63CA6C)),home: new Scaffold(appBar: new AppBar(// 设置 AppBar 标题 title: new Text(appBarTitles[_tabIndex],// 设置 AppBar 上文本的样式 style: new TextStyle(color: Colors.white)),// 设置 AppBar 上图标的样式 iconTheme: new IconThemeData(color: Colors.white)),body: _body,// bottomNavigationBar 属性为页面底部添加导航的 Tab,CupertinoTabBar 是 Flutter 提供的一个 iOS 风格的底部导航栏组件 bottomNavigationBar: new CupertinoTabBar(items: getBottomNavItems(),currentIndex: _tabIndex,onTap: (index) {// 底部 TabItem 的点击事件处理,点击时改变当前选择的 Tab 的索引值,则页面会自动刷新 setState((){_tabIndex = index;});},)),);}


在上面的代码中,body属性是_body变量,而_body变量是个 IndexedStack 对象,IndexedStack 对象的index值是_tabIndex,所以当我们在 setState 中改变了_tabIndex后,IndexedStack 就会自动切换显示子组件了,也就达到了切换页面的目的。


上面的代码运行在模拟器中如下图所示:




给首页加上侧滑菜单

侧滑菜单在 Flutter 中已有相关组件,所以为首页加上侧滑菜单的方法很简单:给 Scaffold 组件传个drawer参数即可,代码如下:

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
从0开始写一个基于Flutter的开源中国客户端(5),带你全面理解View的绘制流程