Flutter-Habit诞生
Flutter-Habit
本文主要讲解,Flutter-Habit架构图解,全文架构,完全参照Android目前最流行的MVVM+DataBinding方式封装,目的旨在让客户端人员更加友好理解Flutter-Habit框架,进而能够进入快速开发
Flutter-基础功能拆解
- base–提供基础组件,如缺省页,基础容器
- constants–提供全局使用的常量、属性
- helper–跨组件的全局属性
- local–本地存储
- network–网络管理,包括加解密
- localizetion–多语言管理
- utlis–工具类
- widget–自定义控件
- constant-config 配置文件
- habit 插件交互管理
- view_model 对外暴露
Flutter 核心功能讲解
- BaseScaffold包括了几个核心的功能
1.全局的ToolBar设置控制
/// 设置ToolBar
final Widget toolBar;
同时提供默认的ToolBar Widget
/// 获取当前的Toolbar的参数
Widget _findCurrentToolBar() {
/// 优先显示设置的toolBar
if (widget.toolBar != null) {
return widget.toolBar;
}
/// 显示默认的toolbar
if (widget.toolBar == null && widget.viewModel.appBarIsShow) {
return AppBarWidget(widget.viewModel);
}
return null;
}
2.全局缺省页控制以及自定义
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
KeyboardUtils.hideByContext(context);
},
child: ValueListenableBuilder<EmptyState>(
valueListenable: widget.viewModel.emptyState,
builder: (context, state, _) => state == EmptyState.NORMAL
? widget.body
: BaseEmptyStateWidget<VM>(
toolBar: widget.toolBar,
),
),
)
3.生命周期的监听
///页面生命周期
enum PageState {
RESUMED,
INACTIVE,
PAUSED,
DETACHED,
}
声明页面的生命周期,进行相关业务开发
由于本文基于Android Mvvm+Databinding模式进行Flutter开发,所以,这里数据监听方式使用的是ValueNotifier,以下以AppBarWidget进行一个简单描述
import 'package:flutter/material.dart';
import 'package:habit/example/widget/base_view_model.dart';
import 'package:habit/habit.dart';
///全局的ToolBar
class AppBarWidget extends StatelessWidget with PreferredSizeWidget {
final BaseViewModel appBarProperty;
AppBarWidget(this.appBarProperty);
@override
Widget build(BuildContext context) {
return ValueListenableListBuilder(
valueListenables: [
appBarProperty.appBarTitle,
appBarProperty.appBarShowBackIcon,
appBarProperty.appBarBackIconColor,
appBarProperty.appBarTitleColor,
appBarProperty.appBarTitleSize,
appBarProperty.appBarBgColor,
appBarProperty.appBarBrightness,
// appBarProperty.appBarLeadingCallBack
],
builder: (context, value, child) {
return AppBar(
brightness: appBarProperty.appBarBrightness.value,
backgroundColor: appBarProperty.appBarBgColor.value==null
? Theme.of(context).accentColor
: appBarProperty.appBarBgColor.value,
elevation: 0,
centerTitle: true,
title: Text(
appBarProperty.appBarTitle.value,
style: TextStyle(
fontSize: appBarProperty.appBarTitleSize.value,
color: appBarProperty.appBarTitleColor.value,
fontWeight: FontWeight.bold,
),
),
leading: Visibility(
visible: appBarProperty.appBarShowBackIcon.value,
child: IconButton(
onPressed: () {
// appBarProperty.appBarLeadingCallBack.value?.call();
///执行默认的返回按钮
if (appBarProperty.appBarLeadingCallBack.value == null) {
Navigator.pop(context);
} else {
appBarProperty.appBarLeadingCallBack.value.call();
}
},
icon: Icon(
Icons.arrow_back,
color: appBarProperty.appBarBackIconColor.value,
size: 25,
),
),
),
);
},
);
}
@override
Size get preferredSize => AppBar().preferredSize;
}
全文重点在于ValueListenableListBuilder,直接上源码
class ValueListenableListBuilder<T> extends StatefulWidget {
const ValueListenableListBuilder({
Key key,
@required this.valueListenables,
@required this.builder,
this.child,
}) : assert(valueListenables != null),
assert(builder != null),
super(key: key);
///看这里,这里是关键
final List<ValueListenable<T>> valueListenables;
final ValueListWidgetBuilder<T> builder;
final Widget child;
@override
State<StatefulWidget> createState() => _ValueListenableListBuilderState<T>();
}
通过源码可以知道,ValueListenable用来监听数据改变,从而刷新UI,也就是我们常说的数据驱动UI。到这里,我们暂且思考下,Mvvm+livedata+databinding是不是也是这个模式?其实基本一毛一样了。
我随便写一个例子对比下,通过MutableLiveData绑定数据,继而在xml进行vm的绑定
xxviewModel.kt
/**
* 视频地址观察者
*/
val videoUrl = MutableLiveData<String>(currentAlbum?.realPath)
<com.example.widget.player.VideoPlayerView
android:id="@+id/videoView"
android:layout_width="0dp"
android:layout_height="0dp"
binding:autoPlay="@{true}"
binding:currentTimeTextView="@{current}"
binding:layout_constraintBottom_toBottomOf="parent"
binding:layout_constraintEnd_toEndOf="parent"
binding:layout_constraintStart_toStartOf="parent"
binding:layout_constraintTop_toTopOf="parent"
binding:looping="@{true}"
binding:playTag="@{viewModel.videoPlayTag}"
binding:seekBar="@{progress}"
binding:totalTimeTextView="@{total}"
binding:videoUrl="@{viewModel.videoUrl}" />
等我写完,放源码