GetxScaffold-Flutter快速開發(fā)腳手架

pub.dev
github

關(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 等工具類。

  1. DateUtil : 日期轉(zhuǎn)換格式化輸出。
  2. EncryptUtil : 異或?qū)ΨQ加/解密,md5 加密,Base64 加/解密。
  3. JsonUtil : 簡單封裝 json 字符串轉(zhuǎn)對象。
  4. JwtDecoder : jwt 解析。
  5. LogUtil : 全局 log 控制。
  6. MoneyUtil : 精確轉(zhuǎn)換,元轉(zhuǎn)分,分轉(zhuǎn)元,支持格式輸出。
  7. NumUtil : 保留 x 位小數(shù), 精確加、減、乘、除, 防止精度丟失。
  8. ObjectUtil : 判斷對象是否為空(String List Map),判斷兩個 List 是否相等。
  9. RegexUtil : 正則驗證手機號,身份證,郵箱等等。
  10. RSAUtils : RSA 加密解密驗簽。
  11. TextUtil : 銀行卡號每隔 4 位加空格,每隔 3 三位加逗號,隱藏手機號等等。
  12. TimelineUtil : 時間軸。
  13. 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();

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容