您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
高效编写Flutter页面最佳实践
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
高效编写Flutter页面最佳实践
自猿其说Tech
2021-12-23
IP归属:未知
704浏览
计算机编程
Flutter
### 1 背景 团队在刚引入Flutter技术栈时,编码上往往会出现代码风格混乱、相同功能重复实现、标准不统一,导致后期维护成本高,进而导致开发效率偏低且排查问题困难重重。经过一段时间的实践经验积累,我们基于MVVM框架的编程思想提炼出了一套非常简单易用的页面脚手架,LoadDataStatusModelMixin中封装了ViewModel管理数据的核心功能,UpdateLoadDataStatusStateMixin中封装了View层用户交互与组件刷新的核心功能,配合自动注册监听和Model自动探测机制,极大简化了代码编写过程,让开发者将更多的精力聚焦到业务需求上。截止目前京象、汪师傅的每个页面几乎都采用此脚手架,并且我们针对不同模块制定对应的Live Templates, 实现在编码窗口上键入业务模板代码缩略词前缀就可以快速导入指定的模板,解放双手,对频繁复制粘贴SayBye,极大地提高了开发效率。 ### 2 页面UI脚手架 大多时候一个App中页面的大体结构是相对固定的,通常由导航栏、内容、底部工具栏三大部分组成,Flutter中为我们提供了Scaffold组件用来构建一个页面,但是功能相对简陋,重复代码较多,我们封装的StoneBasePageMixin很好的解决了这些痛点,配合StatelessWidget或StatefulWidget,在StoneBasePageMixin提供的页面基础框架之上可以快速的构建页面内容。 组合式UI是Flutter的一大特性,在编写UI代码时非常灵活,但是像页面脚手架这种场景,融入模版代码模式的组合式UI是更好的选择,将页面框架中使用频率较高的一些小部件和基础样式通过计算属性来提供,可以使代码变得简洁、易读,同时减少了嵌套,再配合StoneFlutterKit库中的一些UI组件和主题配置,能以非常少量的代码完成除内容区域以外的配置,当然内容区域我们也做了一些非常好用的封装,后面章节会讲到。  备注:左图体现的是页面的常见结构,中间图是通过StoneBasePageMixin构建的一个计数器示例,右图是汪师傅中的完工反馈页面。StoneBasePageMixin使用Dart中mixin的特性可以同时应用于StatefulWidget或StatelessWidget,实际开发中99%以上的页面都在使用,使用模版代码减少了大量的重复代码,统一了页面的基础样式,大幅的提高了开发效率。 #### 2.1 脚手架API列表   #### 2.2 使用示例 ##### 2.2.1 计数器  ##### 2.2.2 业务实践案例 汪师傅完工反馈页面的骨架代码如下,完工反馈是汪师傅中比较复杂的一个模块,有数千行代码,通过StoneBasePageMixin和组合式的UI,做到了骨架分只有一百多行代码,并且结构非常清晰。  ### 3 页面状态管理 在Flutter中状态管理是随处可见并且非常重要的一环,团队刚转到Flutter技术栈时,大家的编码风格比较混乱,重复代码很多,也经常会遇到一些不好排查的小问题,经过一段时间的经验积累后,提炼出了一套非常简单易用的基于Provider的状态管理框架,下图是Provider框架的继承结构,这里不过多展开介绍。通常我们使用ChangeNotiferProvider更多一些,后面讲的状态管理框架也主要是针对ChangeNotiferProvider的扩展,框架适用于多种页面加载数据的场景,可以大幅的简化代码,并使代码模版化。  #### 3.1 View层数据加载状态管理 页面的数据加载状态分为加载中、加载失败、加载成功三种,StoneStateBody是一个可以切换状态的页面内容脚手架,JDLFlutterKit中的buildStateBody()在这基础上实现了一套适合物流体系风格的内容脚手架。 下面是StoneStateBody的类图,API非常简单,StoneStateBody是显示容器,StoneStateBodyController用来更新容器的状态。  下图是StoneStateBody的一个示例页面,如果不需要物流风格的空视图或需要自定义,使用StoneStateBody是更好的选择,只需要实现loadingBuilder、errorBuilder,通常会将其封装为两个可复用的组件。  如果需要物流风格的空视图,JDLFlutterKit中的buildStateBody()则使用起来更简单,真正的开箱即用,效果如下图所示。  #### 3.2 ViewModel层数据状态管理 StoneStateBody配合LoadDataStatus后可以实现开箱即用的体验,LoadDataStatusModelMixin中封装了ViewModel管理数据的核心功能,UpdateLoadDataStatusStateMixin中封装了View层用户交互与组件刷新的核心功能,配合自动注册监听和Model自动探测机制,大幅的简化了代码,屏蔽了很多复杂细节,在开发过程中可以将更多的精力放到业务需求上,不会再为复杂的状态管理而头疼。 下面是LoadDataStatus的类图,在封装了复杂逻辑后暴露的API同样是非常简单的。  常见的 3 种加载数据的场景,LoadDataStatus正是对下面这些场景的统一封装: 1)仅使用Refresher: - 首次加载/刷新:通过下拉触发。 - 加载更多:通过上拉触发。 2)仅使用StoneStateBody: - 首次加载/重试/刷新:在页面中央展示Loading动画。 - 加载更多:无。 3)同时使用StoneStateBody和Refresher: - 首次加载/重试:在页面中央展示Loading动画,首次加载成功后才构建Refresher。 - 刷新:通过下拉触发。 - 加载更多:通过上拉触发。 在汪师傅和京象App中,通常是结合StoneBasePageMixin来使用的,从页面框架到内容的状态管理,提供了一套完整的框架,常见的三种组合方式如下。  StoneStateBody配合LoadDataStatus后,ViewModel部分的核心功能也无需编写代码,如下图所示。  #### 3.3 实践案例(一个商品列表): 以下是View层的示例代码,使用StoneBasePageMixin搭建页面,通过UpdateLoadDataStatusStateMixin来获取数据、刷新组件。  以下是ViewModel的示例代码,使用LoadDataStatusModelMixin管理数据加载状态、通知View层刷新。  ### 4 业务代码模板 #### 4.1 制作代码模板 将通用的业务代码模块如View层、ViewModel模块、Model模块数据请求参数类、返回值类进行提取成为Template,根据IntelliJ IDEA中Live Templates的规范,将模版中可变的、需要在使用中动态设置的地方用$$前后包住,例如:$Class$、$ListViewName$;页面目前常用的有两种:列表页面、普通页面,下面是以列表为例的模板 ##### 4.1.1 View层模板 列表模板代码示例 ```java import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:large_goods_base/large_goods_base.dart'; import 'package:provider/provider.dart'; import 'package:stone_flutter_kit/stone_flutter_kit.dart'; class $class$Page extends StatefulWidget { @override _$class$PageState createState() => _$class$PageState(); } class _$class$PageState extends State<$class$Page> with StoneBasePageMixin { @override Widget get appBarTitle => Text('State management example'); @override Widget buildContent(BuildContext context) { return ChangeNotifierProvider( create: (_) => $modelName$Model(), child: _$listname$View(), ); } } class _$listname$View extends StatefulWidget { @override __$listname$ViewState createState() => __$listname$ViewState(); } class __$listname$ViewState extends State<_$listname$View> with UpdateLoadDataStatusStateMixin<_$listname$View, $modelName$Model> { @override StoneStateBodyController stateBodyController = StoneStateBodyController(); @override RefreshController refreshController = RefreshController(); @override bool hasData($modelName$Model model) => model.dataList?.isNotEmpty ?? false; @override Widget build(BuildContext context) { return buildStateBody<$modelName$Model>( onRetry: () => model.fetchData(), controller: stateBodyController, contentBuilder: (_, viewModel) => buildRefresher( controller: refreshController, headerConfig: RefresherHeaderConfig( topPadding: 10.0, onRefresh: () => viewModel.fetchData(), ), footerConfig: RefresherFooterConfig( bottomPadding: 10.0, onLoading: () => viewModel.fetchData(isLoadMore: true), ), child: ListView.builder( itemCount: , itemBuilder: , ), ), ), ); } } ``` 2)模板在AS IDE中制作过程: Preferences->Editor->Live Templates, 首先新建一个Live Templates Group, JXFlutterTemplate, 然后新建lst_view模版,将上面的代码复制到Template text区域,点击Apply应用,然后点击ok  3)模版在Flutter中的应用: 新建一个Flutter页面,然后在空白区域输入lst_view就会出现提示,然后按下Enter导入模版代码  ##### 4.1.2 ViewModel层模板 1)列表模板代码示例: ```java import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:large_goods_base/large_goods_base.dart'; class $modelName$Model extends ChangeNotifier with LoadDataStatusModelMixin { $modelName$Model() { fetchData(); } @override void dispose() { cancelRequest(_requestToken); super.dispose(); } List<$itemDataClass$RespDto> dataList; int _currentPage = 1; CancelToken _requestToken; void fetchData({bool isLoadMore = false}) { _currentPage = isLoadMore ? _currentPage : 1; cancelRequest(_requestToken); updateLoadStatus( LoadDataState.loading, scene: isLoadMore ? LoadDataScene.loadMore : (dataList?.isEmpty ?? true) ? LoadDataScene.init : LoadDataScene.refresh, ); //网络请求 } } ``` 2)模版在AS IDE中制作过程:  3)模版在Flutter中的应用:  ##### 4.1.3 Model层数据模版 其制作方法不再展开描述,相信大家效仿上面的例子很快能制作出来。 #### 4.2 模板导入与导出 模版制定完毕后,可将模版导出、分享至团队的其它成员,成员再导入模版进行使用。如下是导入导出的操作流程:  ------------ ###### 自猿其说Tech-京东物流技术发展部 ###### 作者:王志明 介晓奇
原创文章,需联系作者,授权转载
上一篇:京东晒出技术年度成绩单 “数实融合”用工匠精神助力实体经济发展
下一篇:Pigeon在Flutter多端接口统一中的应用
相关文章
Taro小程序跨端开发入门实战
Flutter For Web实践
配运基础数据缓存瘦身实践
自猿其说Tech
文章数
426
阅读量
2179279
作者其他文章
01
深入JDK中的Optional
本文将从Optional所解决的问题开始,逐层解剖,由浅入深,文中会出现Optioanl方法之间的对比,实践,误用情况分析,优缺点等。与大家一起,对这项Java8中的新特性,进行理解和深入。
01
Taro小程序跨端开发入门实战
为了让小程序开发更简单,更高效,我们采用 Taro 作为首选框架,我们将使用 Taro 的实践经验整理了出来,主要内容围绕着什么是 Taro,为什么用 Taro,以及 Taro 如何使用(正确使用的姿势),还有 Taro 背后的一些设计思想来进行展开,让大家能够对 Taro 有个完整的认识。
01
Flutter For Web实践
Flutter For Web 已经发布一年多时间,它的发布意味着我们可以真正地使用一套代码、一套资源部署整个大前端系统(包括:iOS、Android、Web)。渠道研发组经过一段时间的探索,使用Flutter For Web技术开发了移动端可视化编程平台—Flutter乐高,在这里希望和大家分享下使用Flutter For Web实践过程和踩坑实践
01
配运基础数据缓存瘦身实践
在基础数据的常规能力当中,数据的存取是最基础也是最重要的能力,为了整体提高数据的读取能力,缓存技术在基础数据的场景中得到了广泛的使用,下面会重点展示一下配运组近期针对数据缓存做的瘦身实践。
自猿其说Tech
文章数
426
阅读量
2179279
作者其他文章
01
深入JDK中的Optional
01
Taro小程序跨端开发入门实战
01
Flutter For Web实践
01
配运基础数据缓存瘦身实践
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号