flu-cli-core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +60 -0
  2. package/dist/chunk-FOMWV2YP.js +378 -0
  3. package/dist/chunk-SW6YDKXI.js +112 -0
  4. package/dist/factory-6DDXZYQP.js +6 -0
  5. package/dist/index.cjs +4668 -0
  6. package/dist/index.d.cts +644 -0
  7. package/dist/index.d.ts +644 -0
  8. package/dist/index.js +4037 -0
  9. package/dist/upgrade_snippets-XFR7Q444.js +8 -0
  10. package/locales/en-US.json +59 -0
  11. package/locales/zh-CN.json +59 -0
  12. package/package.json +52 -0
  13. package/templates/README.md +129 -0
  14. package/templates/core_files/base/base_list_page.dart.template +225 -0
  15. package/templates/core_files/base/base_list_viewmodel.dart.template +164 -0
  16. package/templates/core_files/base/base_page.dart.template +252 -0
  17. package/templates/core_files/base/base_viewmodel.dart.template +68 -0
  18. package/templates/core_files/base/index.dart.template +5 -0
  19. package/templates/core_files/config/app_config.dart.template +142 -0
  20. package/templates/core_files/config/app_initializer.dart.template +74 -0
  21. package/templates/core_files/config/index.dart.template +3 -0
  22. package/templates/core_files/index.dart.template +8 -0
  23. package/templates/core_files/network/README.md +378 -0
  24. package/templates/core_files/network/app_error_code.dart.template +49 -0
  25. package/templates/core_files/network/app_http.dart.template +306 -0
  26. package/templates/core_files/network/app_response.dart.template +81 -0
  27. package/templates/core_files/network/index.dart.template +12 -0
  28. package/templates/core_files/network/interceptors/app_response_interceptor.dart.template +44 -0
  29. package/templates/core_files/network/interceptors/auth_interceptor.dart.template +30 -0
  30. package/templates/core_files/network/interceptors/error_interceptor.dart.template +48 -0
  31. package/templates/core_files/network/interceptors/index.dart.template +6 -0
  32. package/templates/core_files/network/interceptors/log_interceptor.dart.template +97 -0
  33. package/templates/core_files/network/interceptors/network_error_interceptor.dart.template +58 -0
  34. package/templates/core_files/network/interceptors/retry_interceptor.dart.template +69 -0
  35. package/templates/core_files/network/response_adapter.dart.template +69 -0
  36. package/templates/core_files/router/app_routes.dart.template +32 -0
  37. package/templates/core_files/router/index.dart.template +3 -0
  38. package/templates/core_files/router/navigator_util_getx.dart.template +131 -0
  39. package/templates/core_files/router/navigator_util_material.dart.template +191 -0
  40. package/templates/core_files/storage/index.dart.template +3 -0
  41. package/templates/core_files/storage/storage_keys.dart.template +34 -0
  42. package/templates/core_files/storage/storage_util.dart.template +102 -0
  43. package/templates/core_files/theme/app_theme.dart.template +37 -0
  44. package/templates/core_files/theme/index.dart.template +3 -0
  45. package/templates/core_files/theme/status_views_theme.dart.template +40 -0
  46. package/templates/core_files/utils/index.dart.template +2 -0
  47. package/templates/core_files/utils/loading_util.dart.template +55 -0
  48. package/templates/core_files/utils/toast_util.dart.template +128 -0
  49. package/templates/examples/eg_list_page.dart.template +340 -0
  50. package/templates/examples/eg_list_viewmodel.dart.template +31 -0
  51. package/templates/examples/eg_service.dart.template +78 -0
  52. package/templates/examples/mock_data.dart.template +50388 -0
  53. package/templates/examples/tu_chong_model.dart.template +633 -0
  54. package/templates/request_helper.dart.template +59 -0
  55. package/templates/snippets/flu-cli.code-snippets +268 -0
  56. package/templates/snippets/flu-cli.code-snippets.backup +268 -0
@@ -0,0 +1,69 @@
1
+ import 'package:dio/dio.dart';
2
+ import 'package:flutter/foundation.dart';
3
+
4
+ /// 请求重试拦截器
5
+ ///
6
+ /// 网络错误时自动重试
7
+ ///
8
+ /// 使用示例:
9
+ /// ```dart
10
+ /// AppHttp.init(
11
+ /// interceptors: [
12
+ /// RetryInterceptor(
13
+ /// dio: AppHttp().dio,
14
+ /// maxRetries: 3,
15
+ /// retryDelay: const Duration(seconds: 1),
16
+ /// ),
17
+ /// ],
18
+ /// );
19
+ /// ```
20
+ class RetryInterceptor extends Interceptor {
21
+ final Dio dio;
22
+ final int maxRetries;
23
+ final Duration retryDelay;
24
+
25
+ RetryInterceptor({
26
+ required this.dio,
27
+ this.maxRetries = 3,
28
+ this.retryDelay = const Duration(seconds: 1),
29
+ });
30
+
31
+ @override
32
+ Future<void> onError(
33
+ DioException err,
34
+ ErrorInterceptorHandler handler,
35
+ ) async {
36
+ // 只重试网络相关错误
37
+ if (!_shouldRetry(err)) {
38
+ return handler.next(err);
39
+ }
40
+
41
+ final retryCount = err.requestOptions.extra['retryCount'] ?? 0;
42
+
43
+ if (retryCount >= maxRetries) {
44
+ debugPrint('❌ 已达最大重试次数 ($maxRetries)');
45
+ return handler.next(err);
46
+ }
47
+
48
+ debugPrint('🔄 网络错误,第 ${retryCount + 1} 次重试...');
49
+
50
+ // 延迟后重试
51
+ await Future.delayed(retryDelay);
52
+
53
+ err.requestOptions.extra['retryCount'] = retryCount + 1;
54
+
55
+ try {
56
+ final response = await dio.fetch(err.requestOptions);
57
+ return handler.resolve(response);
58
+ } catch (e) {
59
+ return handler.next(err);
60
+ }
61
+ }
62
+
63
+ /// 判断是否应该重试
64
+ bool _shouldRetry(DioException err) {
65
+ return err.type == DioExceptionType.connectionTimeout ||
66
+ err.type == DioExceptionType.receiveTimeout ||
67
+ err.type == DioExceptionType.connectionError;
68
+ }
69
+ }
@@ -0,0 +1,69 @@
1
+ import 'app_error_code.dart';
2
+
3
+ /// 响应数据适配器
4
+ ///
5
+ /// 用于将不同后端接口返回的非标准 JSON 格式,
6
+ /// 统一转换为项目标准的 {code, msg, data} 格式。
7
+ abstract class AppResponseAdapter {
8
+ Map<String, dynamic> adapt(dynamic json);
9
+ }
10
+
11
+ /// 默认适配器
12
+ /// 对应格式: { "code": 200, "msg": "成功", "data": ... }
13
+ class DefaultResponseAdapter extends AppResponseAdapter {
14
+ @override
15
+ Map<String, dynamic> adapt(dynamic json) {
16
+ if (json is Map) {
17
+ return {
18
+ 'code': json['code'] ?? AppErrorCode.networkError,
19
+ 'msg': json['msg'] ?? json['message'] ?? '未知错误',
20
+ 'data': json['data'],
21
+ };
22
+ }
23
+ return _buildError('数据格式错误');
24
+ }
25
+ }
26
+
27
+ /// WanAndroid 适配器 (示例)
28
+ /// 对应格式: { "errorCode": 0, "errorMsg": "", "data": ... }
29
+ class WanAndroidAdapter extends AppResponseAdapter {
30
+ @override
31
+ Map<String, dynamic> adapt(dynamic json) {
32
+ if (json is Map) {
33
+ return {
34
+ 'code': json['errorCode'] ?? AppErrorCode.networkError,
35
+ 'msg': json['errorMsg'] ?? '',
36
+ 'data': json['data'],
37
+ };
38
+ }
39
+ return _buildError('数据格式错误');
40
+ }
41
+ }
42
+
43
+ /// 辅助方法:构建错误数据
44
+ Map<String, dynamic> _buildError(String msg) {
45
+ return {
46
+ 'code': AppErrorCode.parseError,
47
+ 'msg': msg,
48
+ 'data': null,
49
+ };
50
+ }
51
+
52
+ /// 图虫 API 适配器
53
+ /// 对应格式: { "result": "SUCCESS", "message": "...", "feedList": [...] }
54
+ class TuChongAdapter extends AppResponseAdapter {
55
+ @override
56
+ Map<String, dynamic> adapt(dynamic json) {
57
+ if (json is Map) {
58
+ final result = json['result'];
59
+ final isSuccess = result == 'SUCCESS';
60
+
61
+ return {
62
+ 'code': isSuccess ? 200 : -1,
63
+ 'msg': json['message'] ?? '',
64
+ 'data': json['feedList'] ?? json['data'],
65
+ };
66
+ }
67
+ return _buildError('数据格式错误');
68
+ }
69
+ }
@@ -0,0 +1,32 @@
1
+ import 'package:flutter/material.dart';
2
+ // PAGES_IMPORT_PLACEHOLDER
3
+
4
+ /// 应用路由配置
5
+ ///
6
+ /// 使用方式:
7
+ /// Navigator.pushNamed(context, AppRoutes.home);
8
+ /// 或 NavigatorUtil.pushNamed(AppRoutes.home);
9
+ class AppRoutes {
10
+ AppRoutes._();
11
+
12
+ // 路由常量
13
+ static const String home = '/';
14
+ static const String splash = '/splash';
15
+ static const String egList = '/eg-list';
16
+ static const String userList = '/user-list';
17
+
18
+ // __ROUTE_CONFIG_START__
19
+ /// Material 路由映射
20
+ /// 在 app.dart 中使用: MaterialApp(routes: AppRoutes.routes, ...)
21
+ static Map<String, WidgetBuilder> get routes {
22
+ return {
23
+ home: (context) => const HomePage(),
24
+ userList: (context) => const UserListPage(),
25
+ {{#if_network}}
26
+ egList: (context) => const EgListPage(),
27
+ {{/if_network}}
28
+ // --- 在此下方添加您的自定义路由 ---
29
+ };
30
+ }
31
+ // __ROUTE_CONFIG_END__
32
+ }
@@ -0,0 +1,3 @@
1
+ // 导出路由相关类 (按字母顺序排列)
2
+ export 'app_routes.dart';
3
+ export 'navigator_util.dart';
@@ -0,0 +1,131 @@
1
+ import 'package:flutter/material.dart';
2
+ import 'package:get/get.dart';
3
+
4
+ // ------------------------------
5
+ // 导航工具类 (GetX 模式)
6
+ // 提供页面跳转、返回等常用导航功能
7
+ // ------------------------------
8
+ class NavigatorUtil {
9
+ // ------------------------------
10
+ // 1. 静态属性
11
+ // ------------------------------
12
+ /// 私有构造函数
13
+ NavigatorUtil._();
14
+
15
+ // GetX 模式:不需要 GlobalKey
16
+
17
+ // ------------------------------
18
+ // 2. 页面跳转方法
19
+ // ------------------------------
20
+ /// 普通页面跳转
21
+ ///
22
+ /// [page] 目标页面
23
+ /// [replace] 是否替换当前页面
24
+ /// [clearStack] 是否清空导航栈
25
+ static Future<T?> push<T>(
26
+ Widget page, {
27
+ bool replace = false,
28
+ bool clearStack = false,
29
+ }) async {
30
+ if (clearStack) {
31
+ return await Get.offAll<T>(() => page);
32
+ } else if (replace) {
33
+ return await Get.off<T>(() => page);
34
+ } else {
35
+ return await Get.to<T>(() => page);
36
+ }
37
+ }
38
+
39
+ /// 带自定义动画的页面跳转
40
+ ///
41
+ /// [page] 目标页面
42
+ /// [duration] 动画持续时间
43
+ static Future<T?> pushWithAnimation<T>(
44
+ Widget page, {
45
+ required RouteTransitionsBuilder transition,
46
+ Duration duration = const Duration(milliseconds: 300),
47
+ Curve curve = Curves.easeInOut,
48
+ }) async {
49
+ // GetX 模式:使用 Get.to 的 transition 参数
50
+ return await Get.to<T>(
51
+ () => page,
52
+ transition: Transition.fadeIn,
53
+ duration: duration,
54
+ );
55
+ }
56
+
57
+ /// 命名路由跳转
58
+ ///
59
+ /// [routeName] 路由名称
60
+ /// [arguments] 路由参数
61
+ /// [replace] 是否替换当前页面
62
+ /// [clearStack] 是否清空导航栈
63
+ static Future<T?> pushNamed<T>(
64
+ String routeName, {
65
+ Object? arguments,
66
+ bool replace = false,
67
+ bool clearStack = false,
68
+ }) async {
69
+ if (clearStack) {
70
+ return await Get.offAllNamed<T>(routeName, arguments: arguments);
71
+ } else if (replace) {
72
+ return await Get.offNamed<T>(routeName, arguments: arguments);
73
+ } else {
74
+ return await Get.toNamed<T>(routeName, arguments: arguments);
75
+ }
76
+ }
77
+
78
+ // ------------------------------
79
+ // 3. 页面返回方法
80
+ // ------------------------------
81
+ /// 返回上一页
82
+ ///
83
+ /// [result] 返回结果
84
+ static void pop<T>([T? result]) {
85
+ Get.back<T>(result: result);
86
+ }
87
+
88
+ /// 返回上一页,如果键盘弹起则先关闭键盘
89
+ static void popWithKeyboardCheck<T>([T? result]) {
90
+ // GetX 模式:直接返回
91
+ Get.back<T>(result: result);
92
+ }
93
+
94
+ /// 检查是否可以返回
95
+ static bool canPop() {
96
+ return Get.isDialogOpen ?? false;
97
+ }
98
+
99
+ /// 返回到指定页面
100
+ ///
101
+ /// [routeName] 目标路由名称
102
+ static void popUntil(String routeName) {
103
+ Get.until((route) => route.settings.name == routeName);
104
+ }
105
+
106
+ /// 返回到根页面
107
+ static void popToRoot() {
108
+ Get.offAllNamed('/');
109
+ }
110
+
111
+ // ------------------------------
112
+ // 4. 路由参数处理方法
113
+ // ------------------------------
114
+ /// 获取路由传递的参数
115
+ static T? getArguments<T>([T? defaultValue]) {
116
+ try {
117
+ final args = Get.arguments;
118
+ if (args == null) return defaultValue;
119
+
120
+ if (args is T) {
121
+ return args ;
122
+ } else {
123
+ debugPrint('路由参数类型不匹配: 期望 ${T.toString()}, 实际 ${args.runtimeType}');
124
+ return defaultValue;
125
+ }
126
+ } catch (e) {
127
+ debugPrint('获取路由参数异常: $e');
128
+ return defaultValue;
129
+ }
130
+ }
131
+ }
@@ -0,0 +1,191 @@
1
+ import 'package:flutter/material.dart';
2
+
3
+ // ------------------------------
4
+ // 导航工具类 (Material 模式)
5
+ // 提供页面跳转、返回等常用导航功能
6
+ // ------------------------------
7
+ class NavigatorUtil {
8
+ // ------------------------------
9
+ // 1. 静态属性
10
+ // ------------------------------
11
+ /// 私有构造函数
12
+ NavigatorUtil._();
13
+
14
+ /// 全局导航键(Material 模式)
15
+ static final GlobalKey<NavigatorState> navigatorKey =
16
+ GlobalKey<NavigatorState>();
17
+
18
+ /// 全局 ScaffoldMessenger 键,用于显示 SnackBar
19
+ static final GlobalKey<ScaffoldMessengerState> scaffoldMessengerKey =
20
+ GlobalKey<ScaffoldMessengerState>();
21
+
22
+ /// 获取导航状态
23
+ static NavigatorState? get navigator => navigatorKey.currentState;
24
+
25
+ /// 获取当前上下文
26
+ static BuildContext? get context => navigatorKey.currentContext;
27
+
28
+ // ------------------------------
29
+ // 2. 页面跳转方法
30
+ // ------------------------------
31
+ /// 普通页面跳转
32
+ ///
33
+ /// [page] 目标页面
34
+ /// [replace] 是否替换当前页面
35
+ /// [clearStack] 是否清空导航栈
36
+ static Future<T?> push<T>(
37
+ Widget page, {
38
+ bool replace = false,
39
+ bool clearStack = false,
40
+ }) async {
41
+ final Route<T> route = MaterialPageRoute<T>(
42
+ builder: (BuildContext context) => page,
43
+ );
44
+
45
+ if (clearStack) {
46
+ return await navigator?.pushAndRemoveUntil<T>(
47
+ route,
48
+ (Route<dynamic> route) => false,
49
+ );
50
+ } else if (replace) {
51
+ return await navigator?.pushReplacement<T, dynamic>(route);
52
+ } else {
53
+ return await navigator?.push<T>(route);
54
+ }
55
+ }
56
+
57
+ /// 带自定义动画的页面跳转
58
+ ///
59
+ /// [page] 目标页面
60
+ /// [transition] 过渡动画构建器
61
+ /// [duration] 动画持续时间
62
+ /// [curve] 动画曲线
63
+ static Future<T?> pushWithAnimation<T>(
64
+ Widget page, {
65
+ required RouteTransitionsBuilder transition,
66
+ Duration duration = const Duration(milliseconds: 300),
67
+ Curve curve = Curves.easeInOut,
68
+ }) async {
69
+ final Route<T> route = PageRouteBuilder<T>(
70
+ pageBuilder: (
71
+ BuildContext context,
72
+ Animation<double> animation,
73
+ Animation<double> secondaryAnimation,
74
+ ) {
75
+ return page;
76
+ },
77
+ transitionDuration: duration,
78
+ transitionsBuilder: transition,
79
+ );
80
+
81
+ return await navigator?.push<T>(route);
82
+ }
83
+
84
+ /// 命名路由跳转
85
+ ///
86
+ /// [routeName] 路由名称
87
+ /// [arguments] 路由参数
88
+ /// [replace] 是否替换当前页面
89
+ /// [clearStack] 是否清空导航栈
90
+ static Future<T?> pushNamed<T>(
91
+ String routeName, {
92
+ Object? arguments,
93
+ bool replace = false,
94
+ bool clearStack = false,
95
+ }) async {
96
+ if (clearStack) {
97
+ return await navigator?.pushNamedAndRemoveUntil<T>(
98
+ routeName,
99
+ (Route<dynamic> route) => false,
100
+ arguments: arguments,
101
+ );
102
+ } else if (replace) {
103
+ return await navigator?.pushReplacementNamed<T, dynamic>(
104
+ routeName,
105
+ arguments: arguments,
106
+ );
107
+ } else {
108
+ return await navigator?.pushNamed<T>(
109
+ routeName,
110
+ arguments: arguments,
111
+ );
112
+ }
113
+ }
114
+
115
+ // ------------------------------
116
+ // 3. 页面返回方法
117
+ // ------------------------------
118
+ /// 返回上一页
119
+ ///
120
+ /// [result] 返回结果
121
+ static void pop<T>([T? result]) {
122
+ if (navigator?.canPop() ?? false) {
123
+ navigator?.pop<T>(result);
124
+ }
125
+ }
126
+
127
+ /// 返回上一页,如果键盘弹起则先关闭键盘
128
+ static void popWithKeyboardCheck<T>([T? result]) {
129
+ final context = navigatorKey.currentContext;
130
+ if (context == null) {
131
+ pop<T>(result);
132
+ return;
133
+ }
134
+
135
+ final hasFocus = FocusScope.of(context).hasFocus;
136
+ if (hasFocus) {
137
+ FocusScope.of(context).unfocus();
138
+ WidgetsBinding.instance.addPostFrameCallback((_) {
139
+ Future.delayed(const Duration(milliseconds: 400), () {
140
+ pop<T>(result);
141
+ });
142
+ });
143
+ } else {
144
+ pop<T>(result);
145
+ }
146
+ }
147
+
148
+ /// 检查是否可以返回
149
+ static bool canPop() {
150
+ return navigator?.canPop() ?? false;
151
+ }
152
+
153
+ /// 返回到指定页面
154
+ ///
155
+ /// [routeName] 目标路由名称
156
+ static void popUntil(String routeName) {
157
+ navigator?.popUntil(ModalRoute.withName(routeName));
158
+ }
159
+
160
+ /// 返回到根页面
161
+ static void popToRoot() {
162
+ navigator?.popUntil((route) => route.isFirst);
163
+ }
164
+
165
+ // ------------------------------
166
+ // 4. 路由参数处理方法
167
+ // ------------------------------
168
+ /// 获取路由传递的参数
169
+ static T? getArguments<T>([T? defaultValue]) {
170
+ final context = navigatorKey.currentContext;
171
+ if (context == null) return defaultValue;
172
+
173
+ final ModalRoute<dynamic>? route = ModalRoute.of(context);
174
+ if (route == null) return defaultValue;
175
+
176
+ final args = route.settings.arguments;
177
+ if (args == null) return defaultValue;
178
+
179
+ try {
180
+ if (args is T) {
181
+ return args as T;
182
+ } else {
183
+ debugPrint('路由参数类型不匹配: 期望 ${T.toString()}, 实际 ${args.runtimeType}');
184
+ return defaultValue;
185
+ }
186
+ } catch (e) {
187
+ debugPrint('获取路由参数异常: $e');
188
+ return defaultValue;
189
+ }
190
+ }
191
+ }
@@ -0,0 +1,3 @@
1
+ // 导出存储层类 (按字母顺序排列)
2
+ export 'storage_keys.dart';
3
+ export 'storage_util.dart';
@@ -0,0 +1,34 @@
1
+ /// 存储键常量
2
+ ///
3
+ /// 统一管理所有本地存储的键名,避免硬编码
4
+ abstract class StorageKeys {
5
+ // ==================== 应用相关 ====================
6
+
7
+ /// 是否首次启动
8
+ static const isFirstLaunch = 'app_is_first_launch';
9
+
10
+ /// 当前应用版本(用于版本迁移)
11
+ static const appVersion = 'app_version';
12
+
13
+ // ==================== 用户相关 ====================
14
+
15
+ /// 用户 Token
16
+ static const userToken = 'user_token';
17
+
18
+ /// 用户 ID
19
+ static const userId = 'user_id';
20
+
21
+ /// 是否已登录
22
+ static const isLoggedIn = 'user_is_logged_in';
23
+
24
+ // ==================== 设置相关 ====================
25
+
26
+ /// 主题模式(light/dark/system)
27
+ static const themeMode = 'setting_theme_mode';
28
+
29
+ /// 语言
30
+ static const language = 'setting_language';
31
+
32
+ // ==================== 用户可扩展 ====================
33
+ // 在此添加项目特定的键...
34
+ }
@@ -0,0 +1,102 @@
1
+ import 'package:shared_preferences/shared_preferences.dart';
2
+
3
+ /// 本地存储工具
4
+ ///
5
+ /// 封装 SharedPreferences,提供类型安全的存取方法
6
+ ///
7
+ /// 使用示例:
8
+ /// ```dart
9
+ /// // 存储
10
+ /// await StorageUtil.setString('username', 'john');
11
+ /// await StorageUtil.setBool('isVip', true);
12
+ ///
13
+ /// // 读取
14
+ /// final username = StorageUtil.getString('username');
15
+ /// final isVip = StorageUtil.getBool('isVip', defaultValue: false);
16
+ /// ```
17
+ class StorageUtil {
18
+ static SharedPreferences? _prefs;
19
+
20
+ /// 初始化(必须在使用前调用)
21
+ static Future<void> init() async {
22
+ _prefs = await SharedPreferences.getInstance();
23
+ }
24
+
25
+ static SharedPreferences get _instance {
26
+ if (_prefs == null) {
27
+ throw StateError('StorageUtil 未初始化,请先调用 StorageUtil.init()');
28
+ }
29
+ return _prefs!;
30
+ }
31
+
32
+ /// 是否已初始化
33
+ static bool get isInitialized => _prefs != null;
34
+
35
+ // ==================== String ====================
36
+
37
+ static Future<bool> setString(String key, String value) =>
38
+ _instance.setString(key, value);
39
+
40
+ static String? getString(String key) => _instance.getString(key);
41
+
42
+ static String getStringOrDefault(String key, {String defaultValue = ''}) =>
43
+ _instance.getString(key) ?? defaultValue;
44
+
45
+ // ==================== Int ====================
46
+
47
+ static Future<bool> setInt(String key, int value) =>
48
+ _instance.setInt(key, value);
49
+
50
+ static int? getInt(String key) => _instance.getInt(key);
51
+
52
+ static int getIntOrDefault(String key, {int defaultValue = 0}) =>
53
+ _instance.getInt(key) ?? defaultValue;
54
+
55
+ // ==================== Double ====================
56
+
57
+ static Future<bool> setDouble(String key, double value) =>
58
+ _instance.setDouble(key, value);
59
+
60
+ static double? getDouble(String key) => _instance.getDouble(key);
61
+
62
+ static double getDoubleOrDefault(String key, {double defaultValue = 0.0}) =>
63
+ _instance.getDouble(key) ?? defaultValue;
64
+
65
+ // ==================== Bool ====================
66
+
67
+ static Future<bool> setBool(String key, bool value) =>
68
+ _instance.setBool(key, value);
69
+
70
+ static bool? getBool(String key) => _instance.getBool(key);
71
+
72
+ static bool getBoolOrDefault(String key, {bool defaultValue = false}) =>
73
+ _instance.getBool(key) ?? defaultValue;
74
+
75
+ // ==================== StringList ====================
76
+
77
+ static Future<bool> setStringList(String key, List<String> value) =>
78
+ _instance.setStringList(key, value);
79
+
80
+ static List<String>? getStringList(String key) =>
81
+ _instance.getStringList(key);
82
+
83
+ static List<String> getStringListOrDefault(
84
+ String key, {
85
+ List<String> defaultValue = const [],
86
+ }) =>
87
+ _instance.getStringList(key) ?? defaultValue;
88
+
89
+ // ==================== 通用操作 ====================
90
+
91
+ /// 删除指定键
92
+ static Future<bool> remove(String key) => _instance.remove(key);
93
+
94
+ /// 清空所有数据
95
+ static Future<bool> clear() => _instance.clear();
96
+
97
+ /// 是否包含指定键
98
+ static bool containsKey(String key) => _instance.containsKey(key);
99
+
100
+ /// 获取所有键
101
+ static Set<String> getKeys() => _instance.getKeys();
102
+ }
@@ -0,0 +1,37 @@
1
+ import 'package:flutter/material.dart';
2
+
3
+ class AppTheme {
4
+ /// 亮色主题(函数级注释)
5
+ static ThemeData light() {
6
+ return ThemeData(
7
+ brightness: Brightness.light,
8
+ primaryColor: const Color(0xFF3B82F6),
9
+ colorScheme: const ColorScheme.light(
10
+ primary: Color(0xFF3B82F6),
11
+ secondary: Color(0xFF22C55E),
12
+ ),
13
+ appBarTheme: const AppBarTheme(
14
+ elevation: 0,
15
+ centerTitle: true,
16
+ backgroundColor: Colors.white,
17
+ foregroundColor: Colors.black87,
18
+ ),
19
+ scaffoldBackgroundColor: const Color(0xFFF8FAFC),
20
+ );
21
+ }
22
+
23
+ /// 暗色主题(函数级注释)
24
+ static ThemeData dark() {
25
+ return ThemeData(
26
+ brightness: Brightness.dark,
27
+ colorScheme: const ColorScheme.dark(
28
+ primary: Color(0xFF60A5FA),
29
+ secondary: Color(0xFF34D399),
30
+ ),
31
+ appBarTheme: const AppBarTheme(
32
+ elevation: 0,
33
+ centerTitle: true,
34
+ ),
35
+ );
36
+ }
37
+ }
@@ -0,0 +1,3 @@
1
+ // 导出主题类 (按字母顺序排列)
2
+ export 'app_theme.dart';
3
+ export 'status_views_theme.dart';