欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Flutter开发中使用fish_redux怎样在页面中创建TabController呢?

程序员文章站 2022-06-01 17:00:46
...

目前 Flutter 开发中,没有像 Android 那么好的生态,开发框架也没有 Android 那样的 mvc、mvp、mvvm 那么成熟,目前 Flutter 开发使用的框架,更多的说的是状态管理。
目前 Flutter 成熟的状态管理有如下几种:

  1. scope_model (provider) : Google 原生的状态管理,通过封装 InheritedWidget 实现了状态管理,而且一并提现 Google 的设计思想,单一原则,这个 Package 仅仅作为状态管理来用,几乎没有学习成本,如果是小型项目使用,只用 Scoped_model 来做状态管理,无疑是非常好的选择,但是越大的项目,使用 scoped_model 来做状态管理,会有点力不从心。
  2. bloc : 早期比较流行的一个状态管理(其实现在也依旧很流行),它能够很好地支持 Stream 方式,学习成本相对较高,不过大小项目皆宜。
  3. redux : 很好用的一个状态管理,学习成本较低,和前端框架的 redux 使用方式相似,如果是前端同学迁移到 Flutter,这个状态管理框架会是一个很好的学习入门方式。
  4. fish_redux : fish_redux 是基于 redux 封装的,不仅仅能够满足状态管理,更是集成了配置式的组装项目,Page 组装,Component 实现,非常干净,易于维护,易于协作,将集中、分治、复用、隔离做的更进一步,缺点就是代码量的急剧增大(而且是非常非常非常急剧增大);

fish_redux : 记录各个模块的用途和用法,通常一个 page (component) 是由以下几个模块组成的:

  1. action : 用来定义在这个页面中发生的动作,同时也可以通过 payload 参数传值,传递一些不能通过 state 传递的值。
  2. effect : 用来处理副作用操作的,比如:显示弹窗、网络请求等操作。它接收来自 View 的“意图”,也包括对应的生命周期的回调,它的处理可能是一个异步函数,数据可能在过程中被修改,所以这里不建议持有数据,而通过上下文来获取最新数据。effect 不修改数据,如果修改,应该发一个 Action 到 Reducer 里去处理。
  3. page : 用来路由注册,同时完成注册 effect、reducer、component、adapter 的功能。
  4. reducer : 用来更新 view ,即直接操作 view 状态。
  5. state : 用来定义页面中的数据,保存页面状态和数据。
  6. view : 用来展示给用户看到的页面。
    Flutter开发中使用fish_redux怎样在页面中创建TabController呢?

项目结构就如上图所示,每一个页面都需要很多文件。哈哈哈哈!!!

上面只是用来简单记录一下,回归正题,在使用 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 和接口完全不一样,是一个全新的特性。

  1. mixins 只能继承 Object
  2. mixins 没有构造函数
  3. 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啦!!!大功告成,去实现并测试一下吧!!!