前面一节已经介绍完了闪屏广告页,那么今天要实现的就是
HomePage
界面,先来看看home
界面长啥样?如下图所示:
- 底部是一个bottomNavigationBar,放置五个分类导航按钮,分别对应五个不同功能页面;
- 除去底部导航区之外是个内容展示区,当点击底部不同的导航按钮时,该区域显示不同的内容,类似于Android原生开发的FrameLayout布局。
接下来就一步步实现这个homePage页面
在lib\page
目录下新建一个home_page.dart
文件,添加如下内容:
class HomePage extends StatefulWidget {
HomePage({Key key}) : super(key: key);
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State
with AutomaticKeepAliveClientMixin {
//添加AutomaticKeepAliveClientMixin,
//并实现对应的方法bool get wantKeepAlive => true;
//同时build方法实现父方法 super.build(context);
@override
Widget build(BuildContext context) {
super.build(context);
// 方式一:默认设置宽度1080px,高度1920px
ScreenUtil.init(context);
// 方式二:设置宽度750px,高度1334px
// ScreenUtil.init(context, width: 750, height: 1334);
// 方式三:设置宽度750px,高度1334px,根据系统字体进行缩放
// ScreenUtil.init(context, width: 1080, height: 1920, allowFontScaling: true);
return Scaffold(
body: null,
bottomNavigationBar: BottomNavigationBar(
items: null,
onTap: _onTapHandler,
currentIndex: 0,
type: BottomNavigationBarType.fixed,
fixedColor: Colors.red,
unselectedItemColor: Colors.black87,
backgroundColor: Colors.white,
),
);
}
}
上面的代码只是一个大概的框架。
- 首先在引入
AutomaticKeepAliveClientMixin
,这样当点击底部导航按钮切换的时候再切换回之前的页面仍能保持页面跳转之前的浏览位置,比如说在第一个导航按钮页面浏览到中间的位置点击了第二个导航按钮切换到新页面然后又点击第一个导航按钮切换回第一个页面,这时如果不混入AutomaticKeepAliveClientMixin
则第一个页面就会重新加载,所有数据都会恢复初始状态,但是现在加入了这句AutomaticKeepAliveClientMixin
就能保持之前的页面浏览状态而不重新加载。混入这个之后要重写其wantKeepAlive
属性,只要返回一个true
即可。
@override
bool get wantKeepAlive => true;
- 同时引入第三方的屏幕适配插件
flutter_screenutil: ^1.1.0
,并在build
方法下进行初始化ScreenUtil.init(context)
,整个App
的设计稿尺寸按默认尺寸配置(1920*1080)。 - 添加底部导航按钮对应的
page
页面,在lib\page
目录下新建五个页面,分别命名为food_set_page.dart
、food_show_page.dart
、food_reviews_page.dart
、heath_eat_page.dart
及my_page.dart
,然后在build
方法中添加如下代码:
//底部需要切换的页面
final List _pages = [
FoodSetPage(),
FoodShowPage(),
FoodReviewsPage(),
HeathEatPage(),
MyPage(),
];
- 底部的导航区采用的是
BottomNavigationBar
,其构造方法如下,
BottomNavigationBar({
Key key,
@required this.items,
this.onTap,
this.currentIndex = 0,
this.elevation = 8.0,
BottomNavigationBarType type, //
Color fixedColor,
this.backgroundColor,
this.iconSize = 24.0,
Color selectedItemColor,
this.unselectedItemColor,
this.selectedIconTheme = const IconThemeData(),
this.unselectedIconTheme = const IconThemeData(),
this.selectedFontSize = 14.0,
this.unselectedFontSize = 12.0,
this.selectedLabelStyle,
this.unselectedLabelStyle,
this.showSelectedLabels = true,
bool showUnselectedLabels,
})
参数说明:
属性 | 说明 |
---|---|
type | 风格BottomNavigationBarType.fixed(超过4个按键一定要加这个,说明是多个按钮)BottomNavigationBarType.shifting |
items | List |
onTap | 选中变化回调函数 |
currentIndex | 索引值,默认0 默认选中第几个 |
elevation | 默认8 |
iconSize | 图片大小 |
BottomNavigationBarType.fixed | 风格属性 |
fixedColor | 选中的颜色 |
backgroundColor | 背景颜色 |
BottomNavigationBarType.shifting | 风格属性 |
selectedItemColor | 选中时颜色 |
unselectedItemColor | 未选中时颜色 |
selectedIconTheme | 选中时Icon的主题 |
unselectedIconTheme | 未选中时Icon的主题 |
selectedFontSize | 默认14 |
unselectedFontSize | 默认12 |
selectedLabelStyle | 选中时Label的样式 |
unselectedLabelStyle | 未选中时Label的样式 |
showSelectedLabels | 默认true |
showUnselectedLabels | 未选中时的Label |
从上面看出,items
是必填的属性参数,也就是一个 BottomNavigationBarItem Widget
列表。
每个按钮对应一个BottomNavigationBarItem
,其构造函数如下
const BottomNavigationBarItem({
@required this.icon,
this.title,
Widget activeIcon,
this.backgroundColor,
}) : activeIcon = activeIcon ?? icon,
assert(icon != null);
参数说明:
属性 | 说明 |
---|---|
icon | 图标 |
title | 文字 |
activeIcon | 选中后的图标 |
backgroundColor | 背景色 |
icon
和 activeIcon
是两个状态下的图标,title
则是文本,只有默认的 icon
是必须的。用一个List
来存放底部的导航item
//底部导航item的文字和图片
final List tabs = [
BottomNavigationBarItem(
icon: Icon(FontAwesome.home),
title: Text('食集'),
),
BottomNavigationBarItem(
icon: Icon(FontAwesome.gift),
title: Text('食秀'),
),
BottomNavigationBarItem(
icon: Icon(FontAwesome.smile_o),
title: Text('食记'),
),
BottomNavigationBarItem(
icon: Icon(FontAwesome.question_circle_o),
title: Text('吃什么'),
),
BottomNavigationBarItem(
icon: Icon(FontAwesome.github),
title: Text('我的'),
),
];
然后在build
方法下的Scaffold
中设置bottomNavigationBar
的相关属性,如下:
bottomNavigationBar: BottomNavigationBar(
items: tabs,
onTap: _onTapHandler,
currentIndex: _currentIndex,
type: BottomNavigationBarType.fixed,
fixedColor: Colors.red,
unselectedItemColor: Colors.black87,
backgroundColor: Colors.white,
),
其中_onTapHandler
是点击item
时触发的回调函数,
//当前点击的item
int _currentIndex = 0;
//点击底部item跳转相应的page
void _onTapHandler(int index) {
setState(() {
_currentIndex = index;
});
}
-
Scaffold
的body
属性放置一个IndexedStack
,其在同一时刻只能显示子控件中的一个控件,通过Index属性来设置显示的控件。故完整的Scaffold
设置如下:
return Scaffold(
body: IndexedStack(
index: _currentIndex,
children: _pages,
),
bottomNavigationBar: BottomNavigationBar(
items: tabs,
onTap: _onTapHandler,
currentIndex: _currentIndex,
type: BottomNavigationBarType.fixed,
fixedColor: Colors.red,
unselectedItemColor: Colors.black87,
backgroundColor: Colors.white,
),
);
☞传送门 喜欢的请给个Star ☆