# Flutter项目分享
使用Flutter技术实现原生 合伙人项目 (opens new window)的全部功能,通过新技术学习更加深入理解flutter移动端UI跨平台方案存在的优缺点,对未来新项目的技术方向提供新的实现手段,项目按以下五个方面进行简单分类梳理
# 一、工程结构
#
pubspec.yaml
文件SDK环境版本、资源目录、第三方库(Flutter pub)
#
assets
资源库;如图片资源:assets/images/
#
External Libraries
Dart Package 查看Flutter SDK和第三库源码;
Flutter Plugins 插件库源码;
... 其它
#
android
、ios
应用ID、版本、打包等配置
自定义插件
使用权限
UI启动初始化
原生第三方库
#
test
测试普通函数
test("金额格式化", () { } /// ... group 组合不同条件
测试Widgets
testWidgets('登录UI功能', (WidgetTester tester) async { }
#
lib
业务结构
lib
├── data
│ ├── account
│ │ └── token_controller.dart
│ ├── entity
│ │ ├── util
│ │ ├── token.dart
│ │ └── version.dart
│ └── user_controller.dart
├── generated
│ └── json
│ ├── base
│ ├── token_helper.dart
│ └── version_helper.dart
├── io
│ ├── client
│ │ └── client.dart
│ ├── exception
│ │ └── api_exception.dart
│ ├── response
│ │ └── reponse_result.dart
│ ├── error_code.dart
│ ├── http_dio.dart
│ └── params.dart
├── plugin
│ └── mmm_flutter_plugin.dart
├── repository
│ ├── api
│ │ ├── api_common.dart
│ │ └── api_partner.dart
│ ├── account_repository.dart
│ └── partner_repository.dart
├── ui
│ ├── global_store
│ │ ├── action.dart
│ │ ├── reducer.dart
│ │ ├── state.dart
│ │ └── store.dart
│ ├── login
│ │ ├── action.dart
│ │ ├── effect.dart
│ │ ├── page.dart
│ │ ├── reducer.dart
│ │ ├── state.dart
│ │ └── view.dart
├── app.dart
├── global.dart
├── global_stream.dart
└── main.dart
# 二、项目使用第三方库
dependencies:
dio: ^3.0.9 # 网络请求库
event_bus: ^1.1.1 # 事件通知订阅
fish_redux: ^0.3.4 # 基于 Redux 数据管理的组装式 flutter 应用框架
uuid: ^2.0.4 # UUID
synchronized: ^2.2.0+2 # 同步锁
device_info: ^0.4.2+4 # 获取设备信息
package_info: ^0.4.1 # 获取应用包信息
shared_preferences: ^0.5.8 # 简单数据持久存储
flutter_spinkit: ^4.1.2 # 加载指示器
flutter_datetime_picker: ^1.4.0 # 时间选择器
permission_handler: ^5.0.1+1 # 系统权限
date_format: ^1.0.8 # 日期时间格式化
...
JSON转换使用插件 JsonToDartBeanAction
进行生成(最好第一次使用,后续对象尽可能手动添加,插件存在BUG);
# 三、应用框架
# 主流框架
Scoped Model、BLoC (Business Logic Componet)、Provide、Redux、Fish_Redux;
# fish_redux
Redux 是一个专注于状态管理的框架;Fish Redux 是基于 Redux 做状态管理的应用框架。
应用框架不仅仅要解决状态管理的问题,还要解决分治,通信,数据驱动,解耦等等问题。
常用的几个对象概念:
Action
用来定义在这个页面中发生的动作,例如:登录,清理输入框,更换验证码框等;
可以通过payload参数传值;
Effect
处理副作用操作的,比如显示弹窗,网络请求,数据库查询等操作;
Reducer
用来更新View,通过创建新State进行更新View状态;
Page
注册的路由页面,同时完成注册effect,reducer,component,adapter的功能;
State
用来定义页面中的数据(属性),用来保存页面状态和数据;
View
展示给用户看到的页面;
Adapter
面向 ListView 场景的分治设计 Adapter
# StaticFlowAdapter 接受 Object|Map 的数据驱动,模版接收一个 Dependent 的数组,每一个 Dependent 可以是 Component 或者 Adapter + Connector<T,P> 的组合。
class ItemBodyComponent extends Component<ItemBodyState> { ItemBodyComponent() : super( view: buildItemBody, dependencies: Dependencies<ItemBodyState>( adapter: StaticFlowAdapter<ItemBodyState>( slots: <Dependent<ItemBodyState>>[ VideoAdapter().asDependent(videoConnector()), UserInfoComponent().asDependent(userInfoConnector()), DescComponent().asDependent(descConnector()), ItemImageComponent().asDependent(itemImageConnector()), OriginDescComponent().asDependent(originDescConnector()), ]), ), ); }
# DynamicFlowAdapter 模版是一个 Map,接受一个数组类型的数据驱动;
class RecommendAdapter extends DynamicFlowAdapter<RecommendState> { RecommendAdapter() : super( pool: <String, Component<Object>>{ 'card_0': RecommendTitleComponent(), 'card_1': RecommendRowComponent(), }, connector: RecommendCardListConnector(), ); }
# 四、页面路由
# 下面3种写法效果是一致的;
Navigator.pushNamed(context, routeName, arguments)
Navigator.of(context).pushNamed(routeName, arguments)
Navigator.push(BuildContext context, Route<T> route)
# 参数:context上下文环境,routeName路由名称,arguments传递参数
# 使用:
# pushNamed,跳转到新页面
# pushReplacementNamed,当前页面替换成新页面
- 案例:A-B-C,在 C-D 过程时调用该函数,A-B-D
# pushNamedAndRemoveUntil,跳转到新页面,对先前的路由,依据predicate条件判断,返回true为止,false 继续pop出栈;
- 案例:A-B-C-D,在 D-E 过程时,想移除B、C, 最终栈顺序为 A-D-E,就可使用该函数;
# pop,退出当前页面
# popUntil,连续出栈,依据predicate条件判断,返回true为止,false 继续pop出栈;
- 案例:A-B-C-D,想达到D-A的效果,可以使用该函数;
# popAndPushNamed,退出当前页面,跳转到新页面
- 案例:A-B-C,在 C-D 过程时调用该函数,最终栈顺序 A-B-D;
# 五、常见问题
# RIGHT/BOTTOM PVERFLOWED BY xxx PIXELS
Row、Column 布局中时出现超出屏幕范围时, 大部分用 Expanded 嵌套即可解决;