前言
一個開源庫,隨著不斷的迭代優化,難免會遇到一個很痛苦的問題
- 最初的設計並不是很合理:想新增的很多新功能都受此掣肘
想使得該庫更加的強大和健壯,必須要做一個重構
- 因為重構涉及到對外暴露的api,所以大家會遇到一個比較煩躁的問題:更新版本後,會大面積報錯
- 我考慮了很久,到底怎麼幫大家快速遷移呢?最終想到了一個還算合理的方案
對於flutter_smart_dialog 4.0版本的改動,很多是為了解決自己以前考慮不周的歷史遺留,以前這個庫的初心,主要是為了解決loading和dialog穿透問題;現在擴充套件到:custom dialog,attach dialog,loading,toast,最初的設計真的力不從心了,config中的api難以去細分的控制這四個模組功能,一些引數的設計基於現在的功能和場景也不太合理等等
希望大家能夠理解我為什麼要重構?,我絕對不是在搞事情?
快速遷移指南
相容API(必須)⭐️
說明
- show方法快速相容
SmartDialog.compatible.show();
SmartDialog.compatible.showAttach();
SmartDialog.compatible.showLoading();
SmartDialog.compatible.showToast();
- config快速相容
SmartDialog.compatible.config;
增加compatible
中間變數,可快速相容改動的各種引數
快速操作
- 使用全域性替換功能快速遷移:
SmartDialog.show
--->SmartDialog.compatible.show
- Mac:command + shift + r
- Windows:ctrl + shift + r
- Config:
SmartDialog.config
--->SmartDialog.compatible.config
- Mac:command + shift + r
- Windows:ctrl + shift + r
引數移除(必須)⭐️
- 4.0版本刪除了少量引數
方法 | 說明 |
---|---|
showLoading(...) | 刪除background 引數(compatible不相容該引數) |
showToast(...) | 刪除alignment 引數(compatible不相容該引數) |
showAttach(...) | 刪除highlight 引數(compatible相容該引數) |
- 刪除了這些引數,初始化自定義loading和toast的時候,需要做一點點調整
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage,
// here
navigatorObservers: [FlutterSmartDialog.observer],
// here
builder: FlutterSmartDialog.init(
//default toast widget
toastBuilder: (String msg) => CustomToastWidget(msg: msg),
//default loading widget
loadingBuilder: (String msg) => CustomLoadingWidget(msg: msg),
),
);
}
}
說明
background
和alignment
這倆個引數實在沒什麼用,用到的頻率實在太低了
一般都是自定義toast和loading樣式,想怎麼畫就怎麼畫;如果只是簡單用下toast和loading,這倆個引數做不到很強的自定義效果,實在過於累贅,索性刪除了
引數名變動(可選)
通過上面相容API
和引數移除
就可以完整遷移了
這裡我將變動的引數名完整的寫下,大家可以對照下
原引數名 | 變動引數名 |
---|---|
widget | builder:和路由dialog引數對齊(具體見下面builder引數說明) |
isLoading / isLoadingTemp | animationType:方便後期擴充套件多種動畫型別 |
isPenetrate / isPenetrateTemp | usePenetrate:true(點選事件將穿透背景),false(不穿透) |
isUseAnimation / isUseAnimationTemp | useAnimation:true(使用動畫),false(不使用) |
clickBgDismiss / clickBgDismissTemp | clickMaskDismiss:true(點選遮罩後,關閉dialog),false(不關閉) |
animationDuration / animationDurationTemp | animationTime:動畫持續時間 |
alignmentTemp | alignment:控制彈窗的位置 |
maskColorTemp | maskColor:遮罩顏色 |
maskWidgetTemp | maskWidget:可高度定製遮罩 |
debounceTemp | debounce:防抖功能 |
builder引數說明(重要)
4.0版本對自定義控制元件引數做了很大改變
- 老版本
SmartDialog.show(
widget: Container(
height: 80,
width: 180,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(10),
),
alignment: Alignment.center,
child: Text(
'easy custom dialog',
style: TextStyle(color: Colors.white),
),
),
);
- 4.0版本
SmartDialog.show(builder: (context) {
return Container(
height: 80,
width: 180,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(10),
),
alignment: Alignment.center,
child: Text(
'easy custom dialog',
style: TextStyle(color: Colors.white),
),
);
});
這個改動雖然會讓使用麻煩了一點,但是有很重要的意義
- 首先是為了和路由dialog的api對齊,路由dialog自定義控制元件引數也是builder
- 然後解決自定義dialog自身動態重新整理問題:自定義佈局有TextField,鍵盤彈起的時候,自定義dialog佈局可以動態調整距離(需要使用相應widget)
4.0版本新增功能
強大的Config
- 可以使用config獲取dialog是否存在情況
// 自定義dialog,attach或loading,是否存在在介面上
SmartDialog.config.isExist;
// 自定義dialog或attach是否存在在介面上
SmartDialog.config.isExistDialog;
// loading是否存在介面上
SmartDialog.config.isExistLoading;
// toast是否存在在介面上
SmartDialog.config.isExistToast;
- config可以更細緻的控制show,showAttach,showLoading,showToast等彈窗
- SmartConfigXxx()預設引數都是我經過深思後設定的,無特殊要求可以不用額外設定
- 如果不需要自定義config數值,下方初始化程式碼無需寫
SmartDialog.config
..custom = SmartConfigCustom()
..attach = SmartConfigAttach()
..loading = SmartConfigLoading()
..toast = SmartConfigToast();
- 可以自定任意config中的數值,以滿足相應的需求
- 下方程式碼是演示自定義引數
- 大家可以按需設定
SmartDialog.config
..custom = SmartConfigCustom(
maskColor: Colors.black.withOpacity(0.35),
useAnimation: true,
)
..attach = SmartConfigAttach(
animationType: SmartAnimationType.scale,
usePenetrate: false,
)
..loading = SmartConfigLoading(
clickMaskDismiss: false,
leastLoadingTime: const Duration(milliseconds: 0),
)
..toast = SmartConfigToast(
intervalTime: const Duration(milliseconds: 100),
displayTime: const Duration(milliseconds: 2000),
);
bindPage
說明
這個引數的含義是將SmartDialog將page繫結:如果在SmartDialog上跳轉頁面
- 和當前頁面繫結SmartDialog會自動隱藏
- 回到繫結頁面的時候,SmartDialog將會顯示
關於在Dialog上面跳轉頁面的問題,4.0之前的版本,可以使用useSystem
引數解決
- 使用
useSystem
引數時,本質是使用自帶dialog作為載體,這樣就可以合理的和page互動 - 但是因為自帶dialog的各種侷限,使用
useSystem
時:usePenetrate
,tag
,KeepSingle
,permanent
都被禁止使用了
4.0版本引入的bindPage的邏輯,可以避免使用useSystem
時的各種限制
bindPage是預設開啟的(可在config中配置),也可以在使用show和showAttach時手動關閉或開啟;在特殊的業務場景,按需使用bindPage
和useSystem
即可
使用效果
- 寫個演示demo,這個就是正常在彈窗上跳轉頁面操作
void _dialogBindPage() async {
var index = 0;
Function()? showDialog;
toNewPage(bool useSystem) async {
Get.to(
() {
return Scaffold(
appBar: AppBar(title: Text('New Page ${++index}')),
body: Container(
color: randomColor(),
alignment: Alignment.center,
child: ElevatedButton(
onPressed: () => showDialog?.call(),
child: Text('test bindPage $index'),
),
),
);
},
preventDuplicates: false,
);
}
showDialog = () {
SmartDialog.show(builder: (_) {
return Container(
width: 300,
height: 170,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: ElevatedButton(
onPressed: () => toNewPage(false),
child: Text('test bindPage $index'),
),
);
});
};
showDialog();
}
- 來看看效果
- 老實說,沒有使用useSystem功能時的效果絲滑
- 但是bindPage也解決了彈窗上跳轉頁面的問題,同時又保留了usePenetrate,tag,KeepSingle,permanent等功能
- 大家按需使用
關閉彈窗時攜帶資料
該功能和flutter路由關閉,攜帶返回資料功能對齊
- 看下demo:點選
show result
按鈕,關閉彈窗,並將輸入框中的資料返回
void _dialogCarryResult() async {
var result = await SmartDialog.show(
builder: (_) {
var message = '';
return Container(
width: 300,
height: 170,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
Container(
width: 100,
margin: EdgeInsets.only(bottom: 30),
child: TextField(onChanged: (msg) => message = msg),
),
ElevatedButton(
onPressed: () => SmartDialog.dismiss(result: message),
child: Text('show result'),
)
]),
);
},
);
SmartDialog.showToast("$result");
}
- 效果
永久化Dialog
將permanent
引數設定成true,開啟的dialog將變成永久化dialog,框架內部所做的所有兜底關閉操作(返回事件,路由pop,點選遮罩等)將失效,只能手動關閉
該功能請結合實際業務場景使用,請勿濫用
- 開啟一個永久化dialog
SmartDialog.show(
permanent: true,
usePenetrate: true,
builder: (_) => Container(width: 150, height: 150, color: Colors.black),
);
- 關閉永久化dialog
SmartDialog.dismiss(force: true);
- 來看下demo
void _dialogPermanent() async {
openPermanentDialog() {
SmartDialog.show(
permanent: true,
alignment: Alignment.centerRight,
usePenetrate: true,
clickMaskDismiss: false,
builder: (_) {
return Container(
width: 150,
height: double.infinity,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
bottomLeft: Radius.circular(20),
),
boxShadow: [
BoxShadow(color: Colors.grey, blurRadius: 8, spreadRadius: 0.2)
],
),
child: Text('permanent dialog'),
);
},
);
}
SmartDialog.show(builder: (_) {
return Container(
width: 300,
height: 170,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: Wrap(spacing: 20, children: [
ElevatedButton(
onPressed: () => openPermanentDialog(),
child: Text('open'),
),
ElevatedButton(
onPressed: () => SmartDialog.dismiss(force: true),
child: Text('close'),
)
]),
);
});
}
- 效果:可以看見pop路由,點選遮罩和返回事件,都不能關閉
permanent dialog
最小載入時間
config中loading中有個leastLoadingTime
引數,可以控制最小的載入時間
這個功能是為了解決介面請求太快,導致loading彈窗一閃而過的問題
- 使用:該引數請結合實際業務場景設定合適的資料,leastLoadingTime預設數值為0秒
- 此處僅做演示,才在此處給config.loading重新賦值,一般建議在app初始化位置就定好引數
- showLoading()之後立馬呼叫dismiss,loading會一閃而過
- 設定了leastLoadingTime為2秒,loading會強制等待倆秒之後,dismiss才會生效
void _loadingLeastTime() async {
SmartDialog.config.loading = SmartConfigLoading(
leastLoadingTime: const Duration(seconds: 2),
);
SmartDialog.showLoading();
SmartDialog.dismiss();
SmartDialog.config.loading = SmartConfigLoading();
}
- 效果
連續toast顯示間隔時間
當多個toast連續顯示的時候,前一個toast和後一個toast顯示無間隔時間,看起來有點突兀
此處在SmartConfigToast
中增加了一個intervalTime
引數,用以控制間隔時間
預設的intervalTime
已經是個合理引數,如無必要,最好不要更改
- 來看下效果,僅做演示,
intervalTime
數值就設定稍微大一些
void _toastIntervalTime() async {
SmartDialog.config.toast = SmartConfigToast(
intervalTime: const Duration(milliseconds: 800),
);
for (var i = 0; i < 3; i++) {
SmartDialog.showToast("toast $i").then((value) {
if (!SmartDialog.config.isExistToast) {
SmartDialog.config.toast = SmartConfigToast();
}
});
}
}
- 效果圖
總結
SmartDialog 4.0版本是個非常重要的版本,標誌著SmartDialog告別了羞澀,走向了成熟
經過這次重構,我也有了信心,去面對更加複雜的業務場景,進行各種擴充
這次重構我做了很多思考,也非常感謝大家給我提個各種issues
,是你們啟發了我!