不知不觉从18年接触Flutter断断续续到现在,说是一直在玩,其实接触得也都很浅~
实际说起来,貌似自己一点都不懂…
虽然自己断断续续也写了一些app:
玩安卓
钢铁直男版
也在公司app上集成了一个单页面的flutter首页
[捂脸] 但是说实话我自己都不想去玩,好垃圾~
所以才会想在年底比较闲的时候,做出一个至少我愿意装在我手机上的app,至少是…对我有用的app,所以才有了这个项目。
希望自己可以一直有恒心完善下去:
已完成
未完成
Flutter开发的一个爽点是:无脑堆代码(大雾),而最大的痛点也是这个,很多时候你会发现自己哼哧哼哧一通代码写下来:
class _TestPageState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: null,
title: null,
actions: [],
),
body: Column(children: [],),
bottomNavigationBar: Row(children: [],),
);
}
}
哇!! 一气呵成! 浑身通透!
再仔细一看:
),
),
),
),
),
),
),
),
)
],
),
);
}
}
而且这只是v的代码,更别说还有mc的代码,一个稍微复杂点的页面,轻而易举就上了几百行代码,更别说没有提供页面预览功能(新版as已经提供了),这给日后的界面修改和业务修改都增加了难度,这其实就是很多人被劝退的直接原因了。
有没有解决办法呢? 其实是有的,页面拆分就是一个不错的办法,把一个页面进行业务级的拆分,多个cell组成一个页面,单个cell可以独立,其实就是组件化的思想,但是!还是麻烦!!!
而且我也不满足于原生的方法,因为群里大佬已经在疯狂安利FishRedux了,而我想着说,反正是个2019的句号,索性我也画得疯狂一点,就用fisnRedex了。
###提前总结
代码量爆炸! 但是爽!!!! 爽得可以边写代码边喝酒边唱歌!
有坑!!!! 坑巨多!! 文档贼少!!!
大部份坑都是可以解决的,而且很爽
如果不是很了解fishRedux的可以去看下
fishRedux地址
用FishRedux完成一个登录页面
/// 创建应用的根 Widget
/// 1. 创建一个简单的路由,并注册页面
/// 2. 对所需的页面进行和 AppStore 的连接
/// 3. 对所需的页面进行 AOP 的增强
class AppRoute {
static AbstractRoutes _global;
static AbstractRoutes get global {
if (_global == null) {
_global = PageRoutes(
pages: >{
/// 闪屏页
'splash': SplashPage(),
/// 首页
'home': MainPage(),
/// 登录页面
'login': LoginPage(),
/// 注册页面
'register': RegisterPage(),
/// 首页的第二个tab
'second': SecondPage(),
/// 首页的第一个tab
'index': IndexPage(),
///项目目录
'project_list': ProjectListPage(),
/// 项目子目录
'project_child_list': ProjectChildPage(),
/// webView页面
'webView': WebLoadPage(),
/// 微信公众号列表页面
'wechat_author': AuthorPage(),
/// 微信公众号文章列表页面
'wechat_author_article': AuthorArticlePage(),
/// 用户积分
'user_point': UserPointPage(),
/// 用户排名
'user_rank': UserRankPage(),
/// 网址收藏
'web_collection': WebCollectionPage(),
///文章收藏
'article_collection': ArticleCollectionPage(),
/// 体系列表
'system': SystemPage(),
/// 体系列表下属文章
'system_child': SystemChildPage(),
/// 导航体系
'navi': NaviPage(),
/// 侧滑页面
'draw': DrawPage(),
/// 主题颜色修改
'theme_change': ThemeChangePage(),
/// 搜索页面
'search': SearchPage(),
},
visitor: (String path, Page
根据FishRedux的思想,我们把首页架构定义为:
一个大的page(MainPage),里面用pageView装载了两个大的page(SecondPage&IndexPage),
Widget buildView(MainState state, Dispatch dispatch, ViewService viewService) {
/// 渲染appBar
AppBar _renderAppBar() {
return AppBar(
backgroundColor: state.themeColor,
centerTitle: true,
titleSpacing: 60,
title: TabBar(
tabs: state.menuList
.map((e) => Tab(
text: e,
))
.toList(),
labelColor: Colors.white,
controller: state.tabControllerForMenu,
labelPadding: const EdgeInsets.all(0),
labelStyle: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
unselectedLabelStyle: TextStyle(fontSize: 14),
indicatorPadding: const EdgeInsets.all(0),
indicatorSize: TabBarIndicatorSize.label,
),
leading: Builder(builder: (ctx) {
return IconButton(
onPressed: () {
dispatch(MainActionCreator.onOpenDraw(ctx));
},
icon: Image.asset(
'images/icon_more.png',
color: Colors.white,
height: 24,
),
);
}),
actions: [
IconButton(
onPressed: () {
dispatch(MainActionCreator.onToSearch());
},
icon: Icon(Icons.search),
)
],
);
}
return Scaffold(
primary: true,
appBar: _renderAppBar(),
body: TabBarView(
controller: state.tabControllerForMenu,
children: [
KeepAliveWidget(AppRoute.global.buildPage('second', null)),
KeepAliveWidget(AppRoute.global.buildPage('index', null)),
],
),
drawer: AppRoute.global.buildPage('draw', null),
);
}
好像也没有其他什么需要注意的了,只有一个难点是TabController,以及page页面需要如何保活:
这个可以参考下之前的文章:在fishRedux中使用TabController
在普通的stf页面中,我们需要页面保持,只需要实现**AutomaticKeepAliveClientMixin **:
class _TestPageState extends State with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
/// 实现super方法
super.build(context);
return Container();
}
/// 返回true
@override
bool get wantKeepAlive => true;
}
而在fishRedux中就比较麻烦,我们需要把这个page用keepWidget包裹起来:
import 'package:flutter/material.dart';
/// 保持状态的包裹类
class KeepAliveWidget extends StatefulWidget {
final Widget child;
const KeepAliveWidget(this.child);
@override
State createState() => _KeepAliveState();
}
class _KeepAliveState extends State
with AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true;
@override
Widget build(BuildContext context) {
super.build(context);
return widget.child;
}
}
Widget keepAliveWrapper(Widget child) => KeepAliveWidget(child)
我们看下首页的布局,很明显由几个cell组成:
如果在Android里面,那很明显就是一个RecyclerView+itemType组成;
如果是在Flutter原生里面,那很明显就是一个ListView+ItemBuilder里面按item划分
而我们在FishRedux里面,我们把页面做了一个拆分,页面是由一个SingleScrollView组成,而无论bannerComponent,classifyComponent,projectComponent,都是它的一个cell,而重头戏是articleComponent,它带有了父组件带来的loadMore和Refresh(其实整个页面都可以由一个ListView组成,当时不是很熟就用了上面的方法),我们来看看布局层级:
其中的Index_view为:
child: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: viewService.buildComponent('banner'),
),
SliverToBoxAdapter(
child: viewService.buildComponent('classify'),
),
SliverToBoxAdapter(
child: viewService.buildComponent('hotArticle'),
),
],
),
首页我们需要关注的是首页文章的Adapter,它隶属于DynamicFlowAdapter,其他的还有
class ArticleAdapter extends DynamicFlowAdapter {
ArticleAdapter()
: super(
pool: >{
"article_cell": ArticleCellComponent(),
"comm_article_cell": CommArticleCellComponent(),
"hot_project_cell": ProjectComponent(),
},
connector: _ArticleAdapterConnector(),
reducer: buildReducer(),
);
}
class _ArticleAdapterConnector extends ConnOp> {
@override
List get(HotArticleState state) {
List _tempList = [];
_tempList.addAll(state.hotArticleDataSource
.map((e) => ItemBean(
"article_cell", ArticleCellState()..hotArticleCellBean = e))
.toList());
_tempList.add(ItemBean(
"hot_project_cell",
ProjectState()
..projectListDataSource = state.projectDataSource
..screenW = state.size?.width
..screenH = state.size?.height));
_tempList.addAll(state.commArticleDataSource
.map((e) =>
ItemBean("comm_article_cell", CommArticleCellState()..cellBean = e))
.toList());
return _tempList;
}
@override
void set(HotArticleState state, List items) {}
@override
subReducer(reducer) {
return super.subReducer(reducer);
}
}
我们稍微分析下:
本来还想写写其他页面的代码的,但是其实都是个人主页页面的代码的拓展,说难点其实没有,唯一的尴尬点就是代码量爆炸,还有一点是一开始用fishRedux会忘记使用方法,比如:
action
用来定义在这个页面中发生的动作,例如:登录,清理输入框,更换验证码框等。
同时可以通过payload参数传值,传递一些不能通过state传递的值。
effect
这个dart文件在fish_redux中是定义来处理副作用操作的,比如显示弹窗,网络请求,数据库查询等操作。
page
这个dart文件在用来在路由注册,同时完成注册effect,reducer,component,adapter的功能。
reducer
这个dart文件是用来更新View,即直接操作View状态。
state
state用来定义页面中的数据,用来保存页面状态和数据。
view
view很明显,就是flutter里面当中展示给用户看到的页面。