Flutter开发中使用fish_redux怎样在页面中创建TabController呢?
目前 Flutter 开发中,没有像 Android 那么好的生态,开发框架也没有 Android 那样的 mvc、mvp、mvvm 那么成熟,目前 Flutter 开发使用的框架,更多的说的是状态管理。
目前 Flutter 成熟的状态管理有如下几种:
- scope_model (provider) : Google 原生的状态管理,通过封装 InheritedWidget 实现了状态管理,而且一并提现 Google 的设计思想,单一原则,这个 Package 仅仅作为状态管理来用,几乎没有学习成本,如果是小型项目使用,只用 Scoped_model 来做状态管理,无疑是非常好的选择,但是越大的项目,使用 scoped_model 来做状态管理,会有点力不从心。
- bloc : 早期比较流行的一个状态管理(其实现在也依旧很流行),它能够很好地支持 Stream 方式,学习成本相对较高,不过大小项目皆宜。
- redux : 很好用的一个状态管理,学习成本较低,和前端框架的 redux 使用方式相似,如果是前端同学迁移到 Flutter,这个状态管理框架会是一个很好的学习入门方式。
- fish_redux : fish_redux 是基于 redux 封装的,不仅仅能够满足状态管理,更是集成了配置式的组装项目,Page 组装,Component 实现,非常干净,易于维护,易于协作,将集中、分治、复用、隔离做的更进一步,缺点就是代码量的急剧增大(而且是非常非常非常急剧增大);
fish_redux : 记录各个模块的用途和用法,通常一个 page (component) 是由以下几个模块组成的:
- action : 用来定义在这个页面中发生的动作,同时也可以通过 payload 参数传值,传递一些不能通过 state 传递的值。
- effect : 用来处理副作用操作的,比如:显示弹窗、网络请求等操作。它接收来自 View 的“意图”,也包括对应的生命周期的回调,它的处理可能是一个异步函数,数据可能在过程中被修改,所以这里不建议持有数据,而通过上下文来获取最新数据。effect 不修改数据,如果修改,应该发一个 Action 到 Reducer 里去处理。
- page : 用来路由注册,同时完成注册 effect、reducer、component、adapter 的功能。
- reducer : 用来更新 view ,即直接操作 view 状态。
- state : 用来定义页面中的数据,保存页面状态和数据。
-
view : 用来展示给用户看到的页面。
项目结构就如上图所示,每一个页面都需要很多文件。哈哈哈哈!!!
上面只是用来简单记录一下,回归正题,在使用 fish_redux 开发时,遇到一个小问题,在页面中怎样创建 TabController 呢?
不使用 fish_redux 时,我们通常是这么做的,代码如下:
class _HomePageState extends State<TestPage> with SingleTickerProviderStateMixin {
ScrollController _scrollViewController;
TabController _tabController;
@override
void initState() {
super.initState();
_scrollViewController = ScrollController(initialScrollOffset: 0.0);
_tabController = TabController(vsync: this, length: 3);
}
@override
void dispose() {
super.dispose();
_scrollViewController.dispose();
_tabController.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0.0,
title: Text("首页"),
),
body: NestedScrollView(
controller: _scrollViewController,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
pinned: true,
floating: true,
expandedHeight: 280,
... 省略部分代码 ...
bottom: TabBar(controller: _tabController, tabs: [
Tab(text: "aaa"),
Tab(text: "bbb"),
Tab(text: "ccc"),
]),
)
];
},
body: TabBarView(controller: _tabController, children: [
_buildListView("aaa:"),
_buildListView("bbb:"),
_buildListView("ccc:"),
]),
),
);
}
initState() 中创建 TabController ,并在 dispose() 中释放。
上面的代码在创建 TabController 时,构造方法中传入了一个 this,即我们在创建 TabController 时,需要传入 TickerProvider ,那么这个 TickerProvider 是哪里来的呢?
// 我们在构建这个页面时,该页面混入了其它的功能,即 with 后的 SingleTickerProviderStateMixin
// SingleTickerProviderStateMixin 实现了 TickerProvider,所以我们在构建 TabController 时传 this 即可
class _HomePageState extends State<TestPage> with SingleTickerProviderStateMixin {}
插入一个知识点,Dart 语言中 with 的含义:
with 表示 mixins (混入),就是在类中混入其他功能。
使用 mixins 可以实现类似于多继承的功能。mixins 和接口完全不一样,是一个全新的特性。
- mixins 只能继承 Object
- mixins 没有构造函数
- mixins 和接口实现一样可以在 with 后面使用多个 mixins,用逗号隔开
那么,使用 fish_redux 时,由于我们在构建一个页面时,要按照上图所示的结构,即 view 中构建需要展示给用户的界面,代码如下所示:
/// 首页
Widget buildView(SettingState state, Dispatch dispatch, ViewService viewService) {
return Scaffold(
backgroundColor: colorGeneration(),
body: Center(
child: Text('首页')
),
);
}
很显然这里没有混入 TickerProvider 相关的功能,那么在这个页面中创建 TabController 时传入 this 是会报错的,那么该如何实现呢?
解决步骤如下:
首先,需要创建一个 ComponentState ,代码如下:
import 'package:fish_redux/fish_redux.dart';
import '../state.dart';
/// 创建 TabComponent 继承自 ComponentState
class TabComponent extends ComponentState<MineState>
with SingleTickerProviderStateMixin {
}
其次在 page 页面重写 createState( ) 方法,并创建 TabComponent ,代码如下:
import 'package:fish_redux/fish_redux.dart';
import 'components/mine_tab_component.dart';
import 'effect.dart';
import 'reducer.dart';
import 'state.dart';
import 'view.dart';
class MinePage extends Page<MineState, Map<String, dynamic>> {
MinePage()
: super(
initState: initState,
effect: buildEffect(),
reducer: buildReducer(),
view: buildView,
dependencies: Dependencies<MineState>(
adapter: null,
slots: <String, Dependent<MineState>>{
}),
middleware: <Middleware<MineState>>[
],);
@override
ComponentState<MineState> createState() {
// 创建 TabComponent
return TabComponent();
}
}
然后在 effect 中,构建并初始化 TabController,然后赋值给 state 中的 tabController,代码如下:
import 'package:fish_redux/fish_redux.dart';
import 'action.dart';
import 'state.dart';
Effect<MineState> buildEffect() {
return combineEffects(<Object, Effect<MineState>>{
Lifecycle.initState: _initController,
});
}
/// 初始化 TabController
void _initController(Action action, Context<MineState> ctx) {
// 获取到 TickerProvider
final TickerProvider tickerProvider = ctx.stfState as TickerProvider;
// 创建 TabController
var _controller = TabController(vsync: tickerProvider, length: ctx.state.tabList.length);
// 赋值给 state 中的 tabController
ctx.state.tabController = _controller;
}
fish_redux 中的 Lifecycle 生命周期是跟 Flutter 中的生命周期是对应的,页面这里初始化的一些资源,在该页面销毁之前都是有效的。
state 中的代码如下所示:
import 'package:fish_redux/fish_redux.dart';
class MineState implements Cloneable<MineState> {
int tabIndex = 0; // 当前选中的tab索引
List<String> tabList = ["AAA", "BBB", "CCC"]; // tabs list
TabController tabController; // TabController
@override
MineState clone() {
return MineState()
..tabIndex = tabIndex
..tabList = tabList
..tabController = tabController;
}
}
MineState initState(Map<String, dynamic> args) {
return MineState();
}
最后,在 view 中就可以直接使用 state.tabController,代码如下所示:
Widget buildView(MineState state, Dispatch dispatch, ViewService viewService) {
_tabIndex = state.tabIndex;
return Scaffold(
backgroundColor: Color(0xFFFFFFFF),
body: extended.NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
pinned: true,
floating: true,
expandedHeight: 800.0,
... 省略部分代码 ...
bottom: TabBar(controller: state.tabController, tabs: [
Tab(child: Text("AAA")),
Tab(child: Text("BBB")),
Tab(child: Text("CCC")),
]),
),
];
},
body: TabBarView(controller: state.tabController, children: [
_buildListView("aaa:"),
_buildListView("bbb:"),
_buildListView("ccc:"),
]),
),
);
}
OK啦!!!大功告成,去实现并测试一下吧!!!