导航栏添加:
继续接着上一次Flutter项目实战之女装商城------项目概述、创建项目、目录结构、入口程序编写 - cexo - 博客园的项目实操进行编写,在上一次基本没写啥,算是打了一个小地基吧,接下来则来构建一下底部的导航栏:
效果演示:
具体实现:
关于导航栏的处理也比较简单,就是用BottomNavigatorBar控件来进行处理,基础可以参考开启Fluter基础之旅<二>-------Future再论、常用组件、Material Design风格组件学习 - cexo - 博客园,但是这里会引入provide来进行状态管理,算是一个新知识,所以虽然简单但是对于学习的意义还是挺大的,下面来实现一下。
导航项添加:
1、先来为首页新建一个页面:
2、接下来准备TAB数据:
其它的Tab项目则依葫芦画瓢:
空页面添加:
接下来则需要准备点击TAB时对应的页面,类似于Android中的Fragment,这里先占一个位:
具体每个页面都很简单,直接贴出来:
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Center(
child: Text('首页'),
);
}
}
import 'package:flutter/material.dart';
class CategoryPage extends StatefulWidget {
@override
_CategoryPageState createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
@override
Widget build(BuildContext context) {
return Center(
child: Text('分类'),
);
}
}
import 'package:flutter/material.dart';
class CartPage extends StatefulWidget {
@override
_CartPageState createState() => _CartPageState();
}
class _CartPageState extends State<CartPage> {
@override
Widget build(BuildContext context) {
return Center(
child: Text('购物车'),
);
}
}
import 'package:flutter/material.dart';
class MemberPage extends StatefulWidget {
@override
_MemberPageState createState() => _MemberPageState();
}
class _MemberPageState extends State<MemberPage> {
@override
Widget build(BuildContext context) {
return Center(
child: Text('会员中心'),
);
}
}
页面渲染:
接下来则将咱们定义的数据构成底部导航栏的显示了,此时就需要用到脚手架widget了,代码如下:
import 'package:flutter/material.dart';
import 'package:fluttershop/config/index.dart';
import 'cart_page.dart';
import 'category_page.dart';
import 'home_page.dart';
import 'member_page.dart';
class IndexPage extends StatelessWidget {
int _currentIndex = 0;
final List<BottomNavigationBarItem> bottomTabs = [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text(KString.homeTitle), //首页
),
BottomNavigationBarItem(
icon: Icon(Icons.category),
title: Text(KString.categoryTitle), //分类
),
BottomNavigationBarItem(
icon: Icon(Icons.shopping_cart),
title: Text(KString.shoppingCartTitle), //购物车
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
title: Text(KString.memberTitle), //会员中心
),
];
final List<Widget> tabBodies = [
HomePage(),
CategoryPage(),
CartPage(),
MemberPage()
];
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromRGBO(244, 245, 245, 1.0),
body: IndexedStack(
index: _currentIndex,
children: tabBodies,
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
items: bottomTabs,
),
);
}
}
然后将它挂到main.dart上:
此时运行看一下:
此时还得处理一下占击事件实现而面的切换,由于目前用的是StatelessWidget,而要点击的话则将它变为StatefulWidget,所以咱们先来修改一下:
import 'package:flutter/material.dart';
import 'package:fluttershop/config/index.dart';
import 'cart_page.dart';
import 'category_page.dart';
import 'home_page.dart';
import 'member_page.dart';
class IndexPage extends StatefulWidget {
@override
_IndexPageState createState() => _IndexPageState();
}
class _IndexPageState extends State<IndexPage> {
int _currentIndex = 0;
final List<BottomNavigationBarItem> bottomTabs = [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text(KString.homeTitle), //首页
),
BottomNavigationBarItem(
icon: Icon(Icons.category),
title: Text(KString.categoryTitle), //分类
),
BottomNavigationBarItem(
icon: Icon(Icons.shopping_cart),
title: Text(KString.shoppingCartTitle), //购物车
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
title: Text(KString.memberTitle), //会员中心
),
];
final List<Widget> tabBodies = [
HomePage(),
CategoryPage(),
CartPage(),
MemberPage()
];
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromRGBO(244, 245, 245, 1.0),
body: IndexedStack(
index: _currentIndex,
children: tabBodies,
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
items: bottomTabs,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
),
);
}
}
运行看一下:
状态管理:
对于这次要学习的目标其实已经在上面达成了,但是!!!这里重点是要学习一个provider的状态管理机制,对于未来的项目开发大大的有用的。
状态管理基础:
为啥要状态管理?
先来看两种场景,第一种:
这个页面是非常之简单的,其实也就是官方默认生成的页面逻辑,当点击Floating Action Icon时会将页面中的Text文本不断的进行数字更新,对于这种简单页面的更新是非常之容易的,但是!!下面再来看一下复杂的页面:
看到这么多线,头都晕了。。如果说想:
例如当你进入一个文章点赞,退出到外部缩略展示的时候,外部也需要显示点赞数,这时候就需要同步这两个状态。这时候,我们便迫切的需要一个架构来帮助我们理清这些关系,状态管理框架应运而生。
状态管理方案列举:
- Scoped Model:
它的github为:scoped_model | Flutter Package,待之后用到时再来了解。 - Redux:
它的github为:flutter_redux | Flutter Package,在网上搜到个对它的介绍Flutter | 状态管理探索篇——Redux(二) - 简书,贴一下:由于木有使用过,所以木有发言权,弱弱的感受一下既可,感觉使用起来步骤还是挺多的,比较复杂。
- Bloc:
它的github为:flutter_bloc | Flutter Package, - State:
它就是目前咱们使用的这种状态通知方式:它在小项目中用还行,但是如果项目过大则这种使用方式耦合性太强,比较混乱。
- Provide:【重点学习】
它的github地址:GitHub - google/flutter-provide: A simple framework for state management in Flutter.,它其实跟Scoped_model一样,Provide也是借助了InheritWidget【它是一个非常重要的widget,状态管理基本都是通过它来实现的】,将共享状态放到顶层MaterialApp之上,也就是:
底层部件通过Provier获取该状态,并通过混合ChangeNotifier通知依赖于该状态的组件刷新。下面从官网了解一下它:
那看一下它使用方式:
这里重点是关注它的使用,关于底层原理待之后有时间再研究。
当前索引状态处理:
接下来则用Provide来改造咱们的程序,也就是用它来处理底部Tab的切换状态,因为有时候切换状态并不一定是用户点击才产生,也有可能是某个详情子页面会要让主页的Tab切换到某个页面,所以此时如果集成了Provide它来管理状态则会变得很轻松,下面开始改造咱们的程序,让其代替之前咱们用setState()的方式。
1、添加Provide的依赖:
2、定义一个Provide:
其实也就是定义如官方所示的这个:
这里将其统一存到provide目录中:
3、修改main()初始化Provide:
先来看一下咱们的main()的主代码:
其实接下来要做的就是如上面的理论说的这句:
对应官方的代码如下:
依葫芦画瓢改造:
然后给MyApp用Container进行包裹一下:
4、读取Provide的状态值:
此时就可以回到咱们的Tab切换的逻辑上来,将其要切换的位置信息从Provide中进行读取,以便在之后用Provide来更改状态能及时得到切换状态的效果,先来看一下目前的代码:
如何改写呢?看下面,基本上写一遍套路就学会了:
然后它里面的命名构造如下:
所以可以这样构造:
然后之前咱们写的脚手架就可以写在里面了,如下:
好,关键步骤来了,此时咱们得去掉我们用setState来处理的状态代码,改用Provide的方式,如下:
此时的_currentIndex的值就可以通过Provide来读取了,如何读呢,看下面:
其实也就是读取咱们定义的这个值:
5、Tab点击改用provide:
接下来则来处理Tab点击,此时看一下如何用Provide来更新索引状态:
这样状态管理就已经改造成了,其效果当然跟setState()是一样的,串一下整个流程,当我们按TAB切换时会调用它:
此时就会回到这个监听处:
这样用Provide来管理状态的一个好处就是在我们的任何页面都可以用类似于这样的语句来改变Tab的状态:
而且是跟业务完全解耦的,对于商用复杂项目而言是非常之重要的一个技法。
加入屏幕适配的代码:
在最后,这里引入一个屏幕适配的工具库,如下:
其使用也比较简单:
其思想可以瞅一下该博主的Flutter屏幕适配方案之Flutter_ScreenUtil - Toeii的个人博客,贴一下核心思想:
所以费话不多说,集成一下,添加一下库依赖:
然后在主页中加一句话既可:
关注个人公众号,获得实时推送