關(guān)于 GetX Scaffold
GetXScaffold 快速開發(fā)腳手架在 GetX 框架和一些常用插件的基礎(chǔ)上,構(gòu)建了一套完整的快速開發(fā)模板。其中包括新增了部分常用功能的全局方法、常用的擴展方法和各種工具類、部分常用組件的封裝、簡單易用的對話框、二次封裝的 Dio 網(wǎng)絡(luò)請求工具、二次封裝的 GetxController、二次封裝的應(yīng)用主題和國際化實現(xiàn)等。GetXScaffold 是對以上這些內(nèi)容的過度封裝,包括一些組件的擴展方法會違背 Flutter 本身的開發(fā)規(guī)范,改變你的開發(fā)習(xí)慣。所以本腳手架單純?yōu)榱颂岣唛_發(fā)效率,減少重復(fù)代碼,減少開發(fā)成本。如果您是剛接觸 Flutter 開發(fā)并還處在學(xué)習(xí)過程中的話,并不推薦您使用該腳手架。以下只是部分功能的使用示例,建議您通過示例項目或者源碼了解全部使用方法。

適配 Flutter 版本
| Version | Flutter 版本 |
|---|---|
| 0.0.3 | 3.19.5 |
| 0.0.4 | 3.22.2 |
運行示例項目
cd example
flutter pub get
flutter run
快速開始
1. 添加依賴
Flutter 工程中 pubspec.yaml 文件里加入以下依賴:
dependencies:
getx_scaffold: ^0.0.1
# 如果您的項目中需要使用國際化,請?zhí)砑右韵乱蕾? flutter_localizations:
sdk: flutter
2. 初始化腳手架
在 main.dart 中初始化 GetXScaffold:
import 'package:getx_scaffold/getx_scaffold.dart';
void main() async {
WidgetsBinding widgetsBinding = await init(
isDebug: kDebugMode,
logTag: 'GetxScaffold',
supportedLocales: TranslationLibrary.supportedLocales,
);
runApp(
GetxApp(
// 設(shè)計稿尺寸 單位:dp
designSize: const Size(390, 844),
// Getx Log
enableLog: kDebugMode,
// 默認的跳轉(zhuǎn)動畫
defaultTransition: Transition.rightToLeft,
// 主題模式
themeMode: GlobalService.to.themeMode,
// 主題
theme: AppTheme.light,
// Dark主題
darkTheme: AppTheme.dark,
// 國際化配置
locale: GlobalService.to.locale,
translations: TranslationLibrary(),
fallbackLocale: TranslationLibrary.fallbackLocale,
supportedLocales: TranslationLibrary.supportedLocales,
localizationsDelegates: TranslationLibrary.localizationsDelegates,
// AppTitle
title: 'GetxScaffold',
// 首頁
home: const HomePage(),
),
);
}
GetxApp 是 GetMaterialApp 嵌套了 ScreenUtilInit 對全局進行屏幕適配,通過 GlobalService 可以方便的實現(xiàn)多主題和多語言的切換。
3. 定義主題
import 'package:flutter/material.dart';
class AppTheme {
static const String fontMontserrat = 'Montserrat';
static const Color themeColor = Color.fromARGB(255, 11, 107, 47);
static const Color darkThemeColor = Color.fromARGB(255, 27, 31, 139);
/// 亮色主題樣式
static ThemeData light = ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: themeColor,
brightness: Brightness.light,
),
fontFamily: fontMontserrat,
cardTheme: CardTheme(
surfaceTintColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
),
);
/// 暗色主題樣式
static ThemeData dark = ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: darkThemeColor,
brightness: Brightness.dark,
),
fontFamily: fontMontserrat,
cardTheme: CardTheme(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
),
);
}
以上是主題示例,GetXScaffold 的所有內(nèi)置組件均遵循 Material3 設(shè)計規(guī)范。如果你使用 colorScheme 定義了主題顏色,那么你可以通過以下方法使用所有的主題顏色:
ThemeColor.onPrimary
ThemeColor.onSecondary
ThemeColor.onSurface
......
4. 國際化
GetXScaffold 再次簡化的國際化實現(xiàn),您需要創(chuàng)建 TranslationLibrary 并實現(xiàn)以下方法:
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:getx_scaffold/getx_scaffold.dart';
import 'locales/locale_en.dart';
import 'locales/locale_es.dart';
import 'locales/locale_zh.dart';
class TranslationLibrary extends Translations {
// 默認語言 Locale(語言代碼, 國家代碼)
static const fallbackLocale = Locale('zh', 'CN');
static const supportedLocales = [
Locale('zh', 'CN'),
Locale('en', 'US'),
Locale('es', 'ES'),
];
@override
Map<String, Map<String, String>> get keys => {
'zh': zh,
'en': en,
'es': es,
};
static const localizationsDelegates = [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
];
}
在 main.dart GetxApp 中加入以下代碼:
// 國際化配置
locale: GlobalService.to.locale,
translations: TranslationLibrary(),
fallbackLocale: TranslationLibrary.fallbackLocale,
supportedLocales: TranslationLibrary.supportedLocales,
localizationsDelegates: TranslationLibrary.localizationsDelegates,
5. 添加頁面
GetXScaffold 的所有頁面使用 GetView 加 GetxController 視圖與邏輯分離的開發(fā)方式。這里注意,所有 GetxController 必須混入 BaseControllerMixin,以相應(yīng)全局刷新。
HomePage.dart
import 'package:flutter/material.dart';
import 'package:example/common/langs/index.dart';
import 'package:getx_scaffold/getx_scaffold.dart';
import 'index.dart';
class HomePage extends GetView<HomeController> {
const HomePage({super.key});
// 主視圖
Widget _buildView() {
return <Widget>[
ListTile(
title: Text(TextKey.zhuTi.tr),
onTap: () {
Get.to(() => const ThemePage());
},
),
ListTile(
title: Text(TextKey.yuYan.tr),
onTap: () {
Get.to(() => const LanguagePage());
},
),
]
.toListView(
separator: const DividerX(),
)
.scrollbar()
.safeArea();
}
Widget _buildFloatingActionButton() {
return FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.info),
).padding(all: 20.w);
}
@override
Widget build(BuildContext context) {
return GetBuilder<HomeController>(
init: HomeController(),
//這里的id需要與HomeController中的builderId一致
id: 'home',
builder: (_) {
//雙擊退出
return DoublePressBackWidget(
child: Scaffold(
appBar: AppBar(
title: const Text("GetxScaffold"),
centerTitle: true,
elevation: 1,
),
floatingActionButton: _buildFloatingActionButton(),
body: _buildView(),
),
);
},
);
}
}
HomeController.dart
import 'package:getx_scaffold/getx_scaffold.dart';
class HomeController extends GetxController with BaseControllerMixin {
@override
String get builderId => 'home';
HomeController();
@override
void onInit() {
super.onInit();
//刷新ui
updateUi();
//返回
back();
//延時退出
delayedBack();
}
/// 是否監(jiān)聽生命周期事件
@override
bool get listenLifecycleEvent => true;
/// listenLifecycleEvent設(shè)置為true時,會調(diào)用以下生命周期方法
@override
void onDetached() {
log('onDetached');
}
@override
void onHidden() {
log('onHidden');
}
@override
void onInactive() {
log('onInactive');
}
@override
void onPaused() {
log('onPaused');
}
@override
void onResumed() {
log('onResumed');
}
}
全局方法
/// 獲取當前時間戳(Millisecond)
int getTimeStamp({bool isSecond = false})
/// 獲取當前時間戳(Second)
int getTimeStampSecond()
/// 獲取當前日期字符串
String getNowDateString()
/// 獲取當前日期時間字符串
String getNowDateTimeString()
/// 獲取當前時間字符串
String getNowTimeString()
/// 判斷設(shè)備是否連接網(wǎng)絡(luò)
Future<bool> isNetworkAvailable()
/// 判斷設(shè)備是否連接移動網(wǎng)絡(luò)
Future<bool> isConnectedToMobile()
/// 判斷設(shè)備是否連接WiFi
Future<bool> isConnectedToWiFi()
/// 顯示Toast
void showToast(String msg)
/// 顯示成功Toast
void showSuccessToast(String msg)
/// 顯示提示Toast
void showInfoToast(String msg)
/// 顯示警告Toast
void showWarningToast(String msg)
/// 顯示錯誤Toast
void showErrorToast(String msg)
/// 顯示loading
void showLoading([String? msg])
/// 顯示錯誤
void showError([String? msg])
/// 顯示提示
void showInfo(String msg)
/// 隱藏loading
void dismissLoading()
/// 延時執(zhí)行
void delayed(int milliseconds, Function() callback)
/// 監(jiān)聽事件總線
StreamSubscription<T> eventListen<T>(
void Function(T)? onData, {
Function? onError,
void Function()? onDone,
bool? cancelOnError,
})
/// 發(fā)送事件總線
sendEvent<T>(T event)
/// 刷新App所有頁面
void refreshAppui()
/// 統(tǒng)一Log輸出
void log(String log, [String? tag])
/// 切換主題模式
void changeThemeMode(ThemeMode themeMode)
/// 更改語言
void changeLanguage(Locale locale)
/// 打開網(wǎng)頁
void openWebPage(String url, {LaunchMode mode = LaunchMode.platformDefault})
/// 撥打電話
void callPhone(String phoneNumber)
/**
* 需要在App啟動后調(diào)用
*/
/// 獲取包信息
Future<PackageInfo> getPackageInfo()
/// 獲取AppName
Future<String> getAppName()
/// 獲取PackageName
Future<String> getPackageName()
/// 獲取Version
Future<String> getVersion()
/// 獲取BuildNumber
Future<String> getBuildNumber()
/// 獲取設(shè)備信息
Future<BaseDeviceInfo> getDeviceInfo()
// 獲取設(shè)備名稱
Future<String?> getDeviceName()
// 獲取系統(tǒng)版本
Future<String?> getDeviceSystemVersion()
/// 隱藏輸入法
void hideKeyboard()
/// Change status bar Color and Brightness
Future<void> setStatusBarColor()
/// Dark Status Bar
void setDarkStatusBar()
/// Light Status Bar
void setLightStatusBar()
/// This function will show status bar
Future<void> showStatusBar()
// Enter FullScreen Mode (Hides Status Bar and Navigation Bar)
void enterFullScreen()
// Unset Full Screen to normal state (Now Status Bar and Navigation Bar Are Visible)
void exitFullScreen()
/// This function will hide status bar
Future<void> hideStatusBar()
/// Set orientation to portrait
void setOrientationPortrait()
/// Set orientation to landscape
void setOrientationLandscape()
/// SharedPreferences:
/// Add a value in SharedPref based on their type - Must be a String, int, bool, double, Map<String, dynamic> or StringList
Future<bool> setValue(String key, dynamic value, {bool print = true})
/// Returns List of Keys that matches with given Key
List<String> getMatchingSharedPrefKeys(String key)
/// Returns a StringList if exists in SharedPref
List<String>? getStringListAsync(String key)
/// Returns a Bool if exists in SharedPref
bool getBoolAsync(String key, {bool defaultValue = false})
/// Returns a Double if exists in SharedPref
double getDoubleAsync(String key, {double defaultValue = 0.0})
/// Returns a Int if exists in SharedPref
int getIntAsync(String key, {int defaultValue = 0})
/// Returns a String if exists in SharedPref
String getStringAsync(String key, {String defaultValue = ''})
/// Returns a JSON if exists in SharedPref
Map<String, dynamic> getJSONAsync(String key,{Map<String, dynamic>? defaultValue})
/// remove key from SharedPref
Future<bool> removeKey(String key)
/// clear SharedPref
Future<bool> clearSharedPref()
擴展方法
String?擴展:
/// 是否為空或null
bool get isEmptyOrNull => _isEmptyOrNull();
/// 是否不為空或null
bool get isNotEmptyOrNull => !_isEmptyOrNull();
/// 格式化時間字符串
String? dateFormat(String pattern)
/// 格式化時間字符串為日期
String? toDateString()
/// 格式化時間字符串為日期時間
String? toDateTimeString()
/// 格式化時間字符串為時間
String? toTimeString()
/// 獲取DateTime對象
DateTime? getDateTime({bool? isUtc})
/// MD5加密
String? md5()
/// Base64編碼
String? encodeBase64()
/// Base64解碼
String? decodeBase64()
//轉(zhuǎn)為金額字符串
String? toPrice(
int amount, {
MoneyFormats format = MoneyFormats.NORMAL,
MoneyUnit unit = MoneyUnit.NORMAL,
})
/// 轉(zhuǎn)為int類型
int? toInt({int defValue = 0})
/// 轉(zhuǎn)為double類型
double? toDouble({double defValue = 0})
/// 轉(zhuǎn)為num類型
num? toNumber({num defValue = 0})
/// 判斷是否為手機號(簡易驗證)
bool isMobileSimple()
/// 判斷是否為手機號(嚴格驗證)
bool isMobileExact()
/// 判斷是否為座機號碼
bool isTel()
/// 判斷是否為身份證號碼
bool isIDCard18()
/// 判斷是否成年
bool isAdult()
/// 判斷是否為Email
bool isEmail()
/// 判斷是否為Url
bool isURL()
/// 判斷是否為IP
bool isIP()
num?擴展:
/// 根據(jù)格式將時間戳(Milliseconds)格式化日期
String? dateFormat(String pattern, {bool isSecond = false})
/// 將時間戳(Milliseconds)格式化日期
String? toDateString({bool isSecond = false})
/// 將時間戳(Milliseconds)格式化日期時間
String? toDateTimeString({bool isSecond = false})
/// 將時間戳(Milliseconds)格式化時間
String? toTimeString({bool isSecond = false})
/// 將字節(jié)轉(zhuǎn)為容量單位
String? toFileSize({int decimals = 0})
/// 時間戳(Milliseconds)距離當前的時間
String? getTimeDifference({bool isShowDay = true, bool isSecond = false})
/// 時間戳(Milliseconds)距離當前的時間描述
String? getTimeDifferenceDescription({bool isSecond = false})
/// 轉(zhuǎn)為金額字符串
String? toPrice(
int amount, {
MoneyFormats format = MoneyFormats.NORMAL,
MoneyUnit unit = MoneyUnit.NORMAL,
})
/// 加 (精確相加,防止精度丟失).
/// add (without loosing precision).
double? add(num value)
/// 減 (精確相減,防止精度丟失).
/// subtract (without loosing precision).
double? subtract(num value)
/// 乘 (精確相乘,防止精度丟失).
/// multiply (without loosing precision).
double? multiply(num value)
/// 除 (精確相除,防止精度丟失).
/// divide (without loosing precision).
double? divide(num value)
/// 間距
Widget spacing()
List<Widget>擴展
/// 轉(zhuǎn) Wrap
Widget toWrap();
/// 轉(zhuǎn) Column
Widget toColumn();
/// 轉(zhuǎn) Row
Widget toRow();
/// 轉(zhuǎn) ListView
Widget toListView()
// 轉(zhuǎn) Stack
Widget toStack();
// 使用示例:
Widget _buildView() {
return <Widget>[
ListTile(
title: Text(TextKey.genSuiXiTong.tr),
trailing: GlobalService.to.themeMode == ThemeMode.system
? const Icon(Icons.check)
: null,
onTap: () {
changeThemeMode(ThemeMode.system);
},
),
ListTile(
title: Text(TextKey.liangSeZhuTi.tr),
trailing: GlobalService.to.themeMode == ThemeMode.light
? const Icon(Icons.check)
: null,
onTap: () {
changeThemeMode(ThemeMode.light);
},
),
ListTile(
title: Text(TextKey.anSeZhuTi.tr),
trailing: GlobalService.to.themeMode == ThemeMode.dark
? const Icon(Icons.check)
: null,
onTap: () {
changeThemeMode(ThemeMode.dark);
},
),
].toListView(
separator: const DividerX(),
);
}
Widget 擴展:
/// 控制組件隱藏顯示
Widget visibility();
/// 比例布局
Widget aspectRatio();
/// 卡片布局
Widget card();
/// 居中布局
Widget center();
/// 裁剪圓形
Widget clipOval();
/// 裁剪矩形
Widget clipRect();
/// 裁剪圓角
Widget clipRRect();
/// 陰影
Widget elevation();
/// expand
Widget expand();
/// 縮放布局
Widget fittedBox();
/// 彈性布局
Widget flexible();
/// 百分比布局
Widget fractionallySizedBox();
/// 限制盒子 最大寬高
Widget limitedBox();
/// 偏移
Widget offstage();
/// 透明度
Widget opacity();
/// 溢出
Widget overflow();
/// Stack布局 位置
Widget positioned();
/// 墨水紋
Widget inkWell();
/// 漣漪
Widget ripple();
/// 比例縮放
Widget scale();
/// 滾動視圖
Widget scrollable();
/// 滾動條
Widget scrollbar();
/// Transforms Matrix4
Widget transform();
/// Translate 變化位置
Widget translate();
/// 約束
Widget constrained();
/// 約束寬高
Widget tight();
/// 約束寬度
Widget width();
/// 約束高度
Widget height();
/// 取消父級約束
Widget unConstrained();
/// 安全區(qū)域
Widget safeArea();
/// 對齊
Widget align();
/// 對齊 上左邊
Widget alignTopLeft();
/// 對齊 頂部居中
Widget alignTopCenter();
/// 對齊 上右邊
Widget alignTopRight();
/// 對齊 左邊
Widget alignCenterLeft();
/// 對齊 中間
Widget alignCenter();
/// 對齊 右邊
Widget alignCenterRight();
/// 對齊 下左邊
Widget alignBottomLeft();
/// 對齊 底部
Widget alignBottomCenter();
/// 對齊 下右邊
Widget alignBottomRight();
/// 盒子裝飾器
Widget decorated();
/// 背景顏色
Widget backgroundColor();
/// 邊框
Widget border();
/// 陰影
Widget boxShadow();
/// 手勢
Widget gestures();
/// 點擊
Widget onTap();
/// 內(nèi)間距
Widget padding();
/// Sliver 內(nèi)間距
Widget sliverPadding();
/// 內(nèi)間距 縱向
Widget sliverPaddingVertical(double val);
/// 內(nèi)間距 橫向
Widget sliverPaddingHorizontal(double val);
/// 內(nèi)間距 上
Widget sliverPaddingTop(double val);
/// 內(nèi)間距 下
Widget sliverPaddingBottom(double val);
/// 內(nèi)間距 左
Widget sliverPaddingLeft(double val);
/// 內(nèi)間距 右
Widget sliverPaddingRight(double val);
/// SliverToBoxAdapter
Widget sliver();
Widgets
GetXScaffold 并不是一個 UI 組件庫,里面僅封裝了最常用的一些組件并優(yōu)化了使用方法。如有其他需求請按需引入其他組件庫。
import 'package:example/common/langs/index.dart';
import 'package:flutter/material.dart';
import 'package:getx_scaffold/common/index.dart';
import 'package:getx_scaffold/getx_scaffold.dart';
import 'index.dart';
class BaseWidgetsPage extends GetView<BaseWidgetsController> {
const BaseWidgetsPage({super.key});
// 主視圖
Widget _buildView() {
return <Widget>[
TextX.displayLarge('DisplayLarge'),
TextX.displayMedium('DisplayMedium'),
TextX.displaySmall('DisplaySmall'),
TextX.headlineLarge('HeadlineLarge'),
TextX.headlineMedium('HeadlineMedium'),
TextX.headlineSmall('HeadlineSmall'),
TextX.titleLarge('TitleLarge'),
TextX.titleMedium('TitleMedium'),
TextX.titleSmall('TitleSmall'),
TextX.bodyLarge('BodyLarge'),
TextX.bodyMedium('BodyMedium'),
TextX.bodySmall('BodySmall'),
TextX.labelLarge('LabelLarge'),
TextX.labelMedium('LabelMedium'),
TextX.labelSmall('LabelSmall'),
const TextX('Weight 100', weight: FontWeight.w100),
const TextX('Weight 200', weight: FontWeight.w200),
const TextX('Weight 300', weight: FontWeight.w300),
const TextX('Weight 400', weight: FontWeight.w400),
const TextX('Weight 500', weight: FontWeight.w500),
const TextX('Weight 600', weight: FontWeight.w600),
const TextX('Weight 700', weight: FontWeight.w700),
const TextX('Weight 800', weight: FontWeight.w800),
const TextX('Weight 900', weight: FontWeight.w900),
//可點擊文本
RichTextX(children: [
TextSpanItem(
'簡易',
),
TextSpanItem('可點擊', onTap: () {
showInfoToast('onClick!');
}),
TextSpanItem(
'文本測試',
),
]).padding(top: 10.w),
//TextTag
<Widget>[
const TextTag('Text Tag'),
const TextTag(
'Text Tag Outline',
outline: true,
),
const TextTag(
'Text Tag Orange',
color: Colors.orange,
),
].toWrap(spacing: 10.w).padding(top: 10.w),
//Button
<Widget>[
ButtonX(
'General',
dot: true,
icon: Icons.info,
onPressed: () {},
),
ButtonX.primary(
'Primary',
onPressed: () {},
),
ButtonX.outline(
'Outline',
onPressed: () {},
),
ButtonX.icon(
Icons.add,
badge: controller.number.toString(),
onPressed: () {
controller.increment();
},
),
ButtonX.text(
'Text',
onPressed: () {},
),
ButtonX.text(
'Small Text Button',
textColor: Colors.orange,
textSize: 12.sp,
textWeight: FontWeight.bold,
minSize: Size.zero,
innerPadding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 5.h),
borderRadius: 3.r,
onPressed: () {},
),
].toWrap(spacing: 10.w).padding(top: 10.w),
//Icon
//Antd圖標庫:https://www.iconfont.cn/collections/detail?spm=a313x.collections_index.i1.d9df05512.40943a815QJHPE&cid=9402
<Widget>[
IconX.icon(
AntdIcon.project,
size: 40.sp,
dot: controller.showDot,
).onTap(() {
controller.updateDot();
}),
IconX.icon(
Icons.add_circle_sharp,
size: 40.sp,
badge: controller.number.toString(),
).onTap(() {
controller.increment();
}),
IconX.svg(
'assets/svgs/icon1.svg',
size: 40.sp,
badge: 'New',
),
IconX.image(
'assets/icons/ic_launcher_adaptive_dark.png',
size: 40.sp,
).backgroundColor(Colors.orange).clipRRect(all: 8.r),
].toWrap(spacing: 10.w).padding(top: 10.w),
//SpinKit
//https://pub-web.flutter-io.cn/packages/flutter_spinkit
<Widget>[
SpinKitFoldingCube(
color: ThemeColor.primaryContainer,
size: 22.sp,
).padding(all: 10.w),
20.w.spacing(),
SpinKitDoubleBounce(
color: ThemeColor.primaryContainer,
size: 30.sp,
).padding(all: 10.w),
20.w.spacing(),
SpinKitFadingCircle(
color: ThemeColor.primaryContainer,
size: 30.sp,
).padding(all: 10.w),
20.w.spacing(),
SpinKitRipple(
color: ThemeColor.primaryContainer,
size: 30.sp,
).padding(all: 10.w),
].toRow().padding(top: 20.w),
//lottie
//https://pub-web.flutter-io.cn/packages/lottie
Lottie.asset(
'assets/lottie/error.json',
package: pluginPackageName,
width: 0.7.sw,
).padding(top: 10.w),
//image
ImageX.url(
'https://i0.hdslb.com/bfs/archive/ac72ae36271a6970f92b1de485e6ae6c9e4c1ebb.jpg',
width: 0.7.sw,
radius: 5.r,
).padding(top: 10.w),
]
.toColumn(crossAxisAlignment: CrossAxisAlignment.start)
.padding(all: 10.w, bottom: 50.w)
.scrollable()
.scrollbar()
.width(1.sw);
}
//導(dǎo)航欄
Widget _buildNavigationBar() {
return NavigationX(
currentIndex: controller.pageIndex, // 當前選中的tab索引
onTap: (index) {
controller.pageIndex = index;
controller.updateUi();
}, // 切換tab事件
items: [
NavigationItemModel(
label: '首頁',
icon: AntdIcon.home,
selectedIcon: AntdIcon.home_fill,
dot: true,
),
NavigationItemModel(
label: '日歷',
icon: AntdIcon.calendar,
selectedIcon: AntdIcon.calendar_fill,
badge: '18',
),
NavigationItemModel(
label: '設(shè)置',
icon: AntdIcon.setting,
selectedIcon: AntdIcon.setting_fill,
),
NavigationItemModel(
label: '設(shè)置',
icon: AntdIcon.setting,
selectedIcon: AntdIcon.setting_fill,
),
],
);
}
@override
Widget build(BuildContext context) {
return GetBuilder<BaseWidgetsController>(
init: BaseWidgetsController(),
id: 'baseWidgets',
builder: (_) {
return Scaffold(
extendBody: false,
resizeToAvoidBottomInset: false,
appBar: AppBar(title: Text(TextKey.jiChuZuJian.tr), elevation: 1),
bottomNavigationBar: _buildNavigationBar(),
body: SafeArea(
child: _buildView(),
),
);
},
);
}
}
.....
LoadContainer
內(nèi)置了加載中,網(wǎng)絡(luò)錯誤,空數(shù)據(jù)三種狀態(tài),通過控制器進行切換。每種狀態(tài)的 Widget 可以自定義。
import 'package:example/common/langs/index.dart';
import 'package:flutter/material.dart';
import 'package:getx_scaffold/getx_scaffold.dart';
import 'index.dart';
class LoadContainerPage extends GetView<LoadContainerController> {
const LoadContainerPage({super.key});
// 主視圖
Widget _buildView() {
return <Widget>[
TextX.titleLarge('Page contents'),
ButtonX(
'Show Load Error',
onPressed: () => controller.onError(),
).width(double.infinity).padding(top: 30.h, horizontal: 50.w),
ButtonX(
'Show Load Empty',
onPressed: () => controller.onEmpty(),
).width(double.infinity).padding(top: 10.h, horizontal: 50.w),
].toColumn(mainAxisSize: MainAxisSize.min).center();
}
@override
Widget build(BuildContext context) {
return GetBuilder<LoadContainerController>(
init: LoadContainerController(),
id: 'loadContainer',
builder: (_) {
return Scaffold(
appBar: AppBar(title: Text(TextKey.zhuTi.tr), elevation: 1),
body: SafeArea(
child: LoadContainer(
controller: controller.loadController!,
onReLoad: controller.onLoad,
child: _buildView(),
),
),
);
},
);
}
}
import 'package:getx_scaffold/getx_scaffold.dart';
class LoadContainerController extends GetxController with BaseControllerMixin {
@override
String get builderId => 'loadContainer';
LoadController? loadController = LoadController();
@override
void onInit() {
super.onInit();
onLoad();
}
@override
void onClose() {
super.onClose();
loadController?.dispose();
loadController = null;
}
void onLoad() {
loadController?.loading();
delayed(3000, () => loadController?.complete());
}
void onEmpty() {
loadController?.loading();
delayed(3000, () => loadController?.empty());
}
void onError() {
loadController?.loading();
delayed(3000, () => loadController?.error());
}
}
AntdIcon
腳手架中引入了 AntDesign 圖標庫,可以直接通過 AntdIcon.xxx 使用。查看全部圖標
AntdIcon.home
AntdIcon.home_fill
......
Dialog
// 顯示確認彈窗
DialogX.to.showConfirmDialog();
// 顯示通知彈窗
DialogX.to.showNoticeDialog();
// 顯示提示彈窗
DialogX.to.showPromptDialog();
// 顯示菜單彈窗
DialogX.to.showMenuDialog();
Utils
整合了 common_utils 庫和 nb_utils 庫中的常用工具類,并補全了 RSAUtils 等工具類。
- DateUtil : 日期轉(zhuǎn)換格式化輸出。
- EncryptUtil : 異或?qū)ΨQ加/解密,md5 加密,Base64 加/解密。
- JsonUtil : 簡單封裝 json 字符串轉(zhuǎn)對象。
- JwtDecoder : jwt 解析。
- LogUtil : 全局 log 控制。
- MoneyUtil : 精確轉(zhuǎn)換,元轉(zhuǎn)分,分轉(zhuǎn)元,支持格式輸出。
- NumUtil : 保留 x 位小數(shù), 精確加、減、乘、除, 防止精度丟失。
- ObjectUtil : 判斷對象是否為空(String List Map),判斷兩個 List 是否相等。
- RegexUtil : 正則驗證手機號,身份證,郵箱等等。
- RSAUtils : RSA 加密解密驗簽。
- TextUtil : 銀行卡號每隔 4 位加空格,每隔 3 三位加逗號,隱藏手機號等等。
- TimelineUtil : 時間軸。
- TimerUtil : 倒計時,定時任務(wù)。
網(wǎng)絡(luò)請求
GetXScaffold 對 Dio 進行了二次封裝,提供了更簡潔的調(diào)用方式和完整的請求日志。通過 HttpService 進行調(diào)用。
// 設(shè)置BaseURL
HttpService.to.setBaseUrl('https://api.vvhan.com');
// 設(shè)置Authorization
HttpService.to.setAuthorization('1234567890');
HttpService.to.clearAuthorization();
// 統(tǒng)一的響應(yīng)處理,這里返null則正常通過,返回Error字符串則會攔截該請求并彈出錯誤提示。這里可以對403等錯誤進行處理。
HttpService.to.setOnResponseHandler(
(response) async {
try {
var result = BaseModel.fromJson(response.data);
if (result.success == true) {
return null;
} else {
return '服務(wù)器異常';
}
} on Exception catch (e) {
e.printInfo();
return '服務(wù)器異常';
}
},
);
// 取消統(tǒng)一響應(yīng)處理
HttpService.to.setOnResponseHandler(null);
// get請求
showLoading();
var response = await HttpService.to.get(
'/api/wallpaper/acg?type=json',
);
if (response != null) {
dismissLoading();
var result = BaseModel.fromJson(response.data);
showToast(result.url ?? '');
}
// 取消請求
HttpService.to.cancel();