Flutter-Habit誕生
Flutter-Habit
本文主要講解,F(xiàn)lutter-Habit架構(gòu)圖解,全文架構(gòu)盹愚,完全參照Android目前最流行的MVVM+DataBinding方式封裝茉稠,目的旨在讓客戶端人員更加友好理解Flutter-Habit框架歼捐,進而能夠進入快速開發(fā)
Flutter-基礎(chǔ)功能拆解
- base–提供基礎(chǔ)組件蔬螟,如缺省頁厅各,基礎(chǔ)容器
- constants–提供全局使用的常量盗蟆、屬性
- helper–跨組件的全局屬性
- local–本地存儲
- network–網(wǎng)絡(luò)管理戈二,包括加解密
- localizetion–多語言管理
- utlis–工具類
- widget–自定義控件
- constant-config 配置文件
- habit 插件交互管理
- view_model 對外暴露
Flutter 核心功能講解
- BaseScaffold包括了幾個核心的功能
1.全局的ToolBar設(shè)置控制
/// 設(shè)置ToolBar
final Widget toolBar;
同時提供默認的ToolBar Widget
/// 獲取當前的Toolbar的參數(shù)
Widget _findCurrentToolBar() {
/// 優(yōu)先顯示設(shè)置的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.生命周期的監(jiān)聽
///頁面生命周期
enum PageState {
RESUMED,
INACTIVE,
PAUSED,
DETACHED,
}
聲明頁面的生命周期,進行相關(guān)業(yè)務(wù)開發(fā)
由于本文基于Android Mvvm+Databinding模式進行Flutter開發(fā)喳资,所以觉吭,這里數(shù)據(jù)監(jiān)聽方式使用的是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();
///執(zhí)行默認的返回按鈕
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);
///看這里亏栈,這里是關(guān)鍵
final List<ValueListenable<T>> valueListenables;
final ValueListWidgetBuilder<T> builder;
final Widget child;
@override
State<StatefulWidget> createState() => _ValueListenableListBuilderState<T>();
}
通過源碼可以知道,ValueListenable用來監(jiān)聽數(shù)據(jù)改變宏赘,從而刷新UI绒北,也就是我們常說的數(shù)據(jù)驅(qū)動UI。到這里察署,我們暫且思考下闷游,Mvvm+livedata+databinding是不是也是這個模式?其實基本一毛一樣了贴汪。
我隨便寫一個例子對比下,通過MutableLiveData綁定數(shù)據(jù)脐往,繼而在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}" />
等我寫完,放源碼