从0开始写一个基于Flutter的开源中国客户端(5)——App整体布局框架搭建

| 4 | 从0开始写一个基于Flutter的开源中国客户端(4)
Flutter布局基础
|
| 👉5 | 从0开始写一个基于Flutter的开源中国客户端(5)
App整体布局框架搭建
|
| 6 | 从0开始写一个基于Flutter的开源中国客户端(6)
各个静态页面的实现
|
| 7 | 从0开始写一个基于Flutter的开源中国客户端(7)
App网络请求和数据存储
|
| 8 | 从0开始写一个基于Flutter的开源中国客户端(8)
插件的使用
|

App整体布局框架搭建

在我们日常生活中经常使用的App比如微信、微博、QQ等,基本上都是使用首页底部多个Tab可切换页面,加上可侧滑的菜单这种布局方式来组合。基于Flutter的开源中国客户端也是使用这种布局组合来实现的App。本篇要实现的页面效果如下图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下面一步步来完成这个布局框架的搭建。

新建项目

在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 {
@override
State createState() => new MyOSCClientState();
}

class MyOSCClientState extends State {
@override
Widget 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.dart
import ‘package:flutter/material.dart’;

// 资讯列表页面
class NewsListPage extends StatelessWidget {
@override
Widget 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: [
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 getBottomNavItems() {
List 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: 20.0);
}

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

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

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

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

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

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

@override
Widget 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参数即可,代码如下:

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

最后

由于题目很多整理答案的工作量太大,所以仅限于提供知识点,详细的很多问题和参考答案我都整理成了 PDF文件


《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
x = index;
});

最后

由于题目很多整理答案的工作量太大,所以仅限于提供知识点,详细的很多问题和参考答案我都整理成了 PDF文件

[外链图片转存中…(img-1EFnTxpO-1715065963781)]

[外链图片转存中…(img-TtKosv7E-1715065963782)]

[外链图片转存中…(img-vQDY2r5f-1715065963784)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值