flu-cli-core 1.0.4 → 1.1.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 (118) hide show
  1. package/README.md +22 -17
  2. package/dist/chunk-BGYZU6TU.js +466 -0
  3. package/dist/chunk-QGM4M3NI.js +37 -0
  4. package/dist/factory-LM2CTHPW.js +7 -0
  5. package/dist/factory-P6ABQFH3.js +7 -0
  6. package/dist/index.cjs +18901 -3313
  7. package/dist/index.d.cts +833 -72
  8. package/dist/index.d.ts +833 -72
  9. package/dist/index.js +18569 -3132
  10. package/dist/upgrade_snippets-BJ6CQY5Q.js +9 -0
  11. package/package.json +9 -4
  12. package/templates/README.md +12 -0
  13. package/templates/core_files/auth/auth_middleware.dart.template +33 -0
  14. package/templates/core_files/auth/auth_service.dart.template +22 -0
  15. package/templates/core_files/auth/auth_viewmodel_mixin.dart.template +9 -0
  16. package/templates/core_files/auth/index.dart.template +4 -0
  17. package/templates/core_files/base/base_service.dart.template +12 -0
  18. package/templates/core_files/base/index.dart.template +3 -0
  19. package/templates/core_files/config/agreement_document_page.dart.template +220 -0
  20. package/templates/core_files/config/app_agreement.dart.template +297 -0
  21. package/templates/core_files/config/app_config.dart.template +81 -22
  22. package/templates/core_files/config/app_env.dart.template +107 -0
  23. package/templates/core_files/config/app_initializer.dart.template +16 -23
  24. package/templates/core_files/config/index.dart.template +4 -1
  25. package/templates/core_files/config/privacy_dialog.dart.template +158 -0
  26. package/templates/core_files/index.dart.template +3 -0
  27. package/templates/core_files/mixins/page/keep_alive_mixin.dart.template +6 -0
  28. package/templates/core_files/mixins/page/scroll_controller_mixin.dart.template +7 -0
  29. package/templates/core_files/mixins/service/request_guard_mixin.dart.template +18 -0
  30. package/templates/core_files/mixins/viewmodel/debounce_mixin.dart.template +18 -0
  31. package/templates/core_files/network/app_http.dart.template +19 -4
  32. package/templates/core_files/network/index.dart.template +4 -0
  33. package/templates/core_files/network/interceptors/global_params_interceptor.dart.template +77 -0
  34. package/templates/core_files/network/interceptors/index.dart.template +3 -0
  35. package/templates/core_files/network/network_monitor.dart.template +18 -0
  36. package/templates/core_files/network/response_adapter.dart.template +8 -19
  37. package/templates/core_files/router/app_routes.dart.template +3 -6
  38. package/templates/core_files/storage/storage_keys.dart.template +6 -0
  39. package/templates/core_files/theme/app_color_config.dart.template +32 -0
  40. package/templates/core_files/theme/app_text_size_config.dart.template +22 -0
  41. package/templates/core_files/theme/app_text_style_config.dart.template +139 -0
  42. package/templates/core_files/theme/app_theme.dart.template +72 -12
  43. package/templates/core_files/theme/index.dart.template +3 -0
  44. package/templates/core_files/utils/loading_util.dart.template +1 -1
  45. package/templates/examples/eg_list_page.dart.template +1 -2
  46. package/templates/examples/eg_service.dart.template +27 -4
  47. package/templates/examples/home_feed_service.dart.template +37 -0
  48. package/templates/helper_examples/image_picker_example_page.dart.template +289 -0
  49. package/templates/helper_examples/index.dart.template +4 -0
  50. package/templates/helper_examples/payment_shell_example_page.dart.template +67 -0
  51. package/templates/helper_examples/permission_example_page.dart.template +365 -0
  52. package/templates/helper_examples/webview_example_page.dart.template +44 -0
  53. package/templates/helpers/image_picker/README.md.template +30 -0
  54. package/templates/helpers/image_picker/index.dart.template +73 -0
  55. package/templates/helpers/payment/README.md.template +29 -0
  56. package/templates/helpers/payment/index.dart.template +66 -0
  57. package/templates/helpers/permission/README.md.template +30 -0
  58. package/templates/helpers/permission/index.dart.template +67 -0
  59. package/templates/helpers/webview/README.md.template +29 -0
  60. package/templates/helpers/webview/index.dart.template +88 -0
  61. package/templates/snippets/flu-cli.code-snippets +35 -26
  62. package/templates/starter_project/.env.dev.template +14 -0
  63. package/templates/starter_project/.env.prod.example.template +14 -0
  64. package/templates/starter_project/.env.staging.template +14 -0
  65. package/templates/starter_project/.vscode/launch.json.template +54 -0
  66. package/templates/starter_project/DEVELOPER_GUIDE.md.template +150 -0
  67. package/templates/starter_project/README.md.template +99 -0
  68. package/templates/starter_project/analysis_options.yaml.template +28 -0
  69. package/templates/starter_project/lib/app.dart.template +22 -0
  70. package/templates/starter_project/lib/main.dart.template +34 -0
  71. package/templates/starter_project/lib/pages/splash_page.dart.template +154 -0
  72. package/templates/template_clean/lib/features/home/data/datasources/index.dart +1 -0
  73. package/templates/template_clean/lib/features/home/data/models/index.dart +1 -0
  74. package/templates/template_clean/lib/features/home/domain/index.dart +1 -0
  75. package/templates/template_clean/lib/features/home/presentation/pages/home_page.dart +290 -0
  76. package/templates/template_clean/lib/features/home/presentation/pages/index.dart +2 -0
  77. package/templates/template_clean/lib/features/home/presentation/pages/splash_page.dart +154 -0
  78. package/templates/template_clean/lib/features/home/presentation/viewmodels/home_viewmodel.dart +17 -0
  79. package/templates/template_clean/lib/features/index.dart +2 -0
  80. package/templates/template_clean/lib/features/user/data/datasources/home_feed_service.dart +37 -0
  81. package/templates/template_clean/lib/features/user/data/datasources/index.dart +4 -0
  82. package/templates/template_clean/lib/features/user/data/models/index.dart +3 -0
  83. package/templates/template_clean/lib/features/user/data/models/user.dart +15 -0
  84. package/templates/template_clean/lib/features/user/domain/index.dart +1 -0
  85. package/templates/template_clean/lib/features/user/presentation/pages/index.dart +1 -0
  86. package/templates/template_clean/lib/features/user/presentation/pages/user_list_page.dart +27 -0
  87. package/templates/template_clean/lib/features/user/presentation/viewmodels/user_list_viewmodel.dart +88 -0
  88. package/templates/template_clean/lib/features/user/presentation/widgets/user_item_card.dart +24 -0
  89. package/templates/template_clean/lib/shared/extensions/index.dart +1 -0
  90. package/templates/template_clean/lib/shared/widgets/index.dart +1 -0
  91. package/templates/template_lite/lib/models/index.dart +1 -0
  92. package/templates/template_lite/lib/pages/home_page.dart +290 -0
  93. package/templates/template_lite/lib/pages/index.dart +3 -0
  94. package/templates/template_lite/lib/pages/splash_page.dart +154 -0
  95. package/templates/template_lite/lib/pages/user_list_page.dart +29 -0
  96. package/templates/template_lite/lib/services/home_feed_service.dart +37 -0
  97. package/templates/template_lite/lib/services/index.dart +5 -0
  98. package/templates/template_lite/lib/utils/index.dart +1 -0
  99. package/templates/template_lite/lib/viewmodels/home_viewmodel.dart +34 -0
  100. package/templates/template_lite/lib/viewmodels/index.dart +2 -0
  101. package/templates/template_lite/lib/viewmodels/user_list_viewmodel.dart +103 -0
  102. package/templates/template_lite/lib/widgets/index.dart +1 -0
  103. package/templates/template_lite/lib/widgets/user_item_widget.dart +57 -0
  104. package/templates/template_modular/lib/features/home/index.dart +2 -0
  105. package/templates/template_modular/lib/features/home/models/index.dart +1 -0
  106. package/templates/template_modular/lib/features/home/pages/home_page.dart +290 -0
  107. package/templates/template_modular/lib/features/home/pages/index.dart +2 -0
  108. package/templates/template_modular/lib/features/home/pages/splash_page.dart +154 -0
  109. package/templates/template_modular/lib/features/home/services/index.dart +1 -0
  110. package/templates/template_modular/lib/features/home/viewmodels/home_viewmodel.dart +17 -0
  111. package/templates/template_modular/lib/features/index.dart +2 -0
  112. package/templates/template_modular/lib/features/user/index.dart +6 -0
  113. package/templates/template_modular/lib/features/user/pages/user_list_page.dart +26 -0
  114. package/templates/template_modular/lib/features/user/services/home_feed_service.dart +37 -0
  115. package/templates/template_modular/lib/features/user/viewmodels/user_list_viewmodel.dart +103 -0
  116. package/templates/template_modular/lib/features/user/widgets/user_item_widget.dart +24 -0
  117. package/templates/template_modular/lib/shared/utils/index.dart +1 -0
  118. package/templates/template_modular/lib/shared/widgets/index.dart +1 -0
package/README.md CHANGED
@@ -23,18 +23,15 @@ ProjectGenerator
23
23
 
24
24
  ### 2. 多平台应用商店上传 ✨
25
25
 
26
- 独立的 **@huoye/app-ship** 上传引擎,**8 个平台全部实测通过**:
26
+ 本模块提供了高效的应用包信息解析与处理能力。
27
27
 
28
- - ✅ 蒲公英 (Pgyer) — 内测分发
29
- - ✅ 华为 AppGallery — 国内商店
30
- - ✅ 小米应用商店 — RSA 签名认证
31
- - ✅ OPPO 应用商店 — HMAC-SHA256
32
- - ✅ vivo 应用商店 — 一站式直传
33
- - ✅ 腾讯应用宝 — COS 直传
34
- - ✅ App Store — Transporter/altool
35
- - ✅ 鸿蒙应用市场 — 华为 API 复用
28
+ ### 主要功能
36
29
 
37
- 详见 [@huoye/app-ship 文档](https://gitee.com/flu-cli/flu-cli/blob/main/packages/app-ship/README.md)
30
+ - **包体解析**:解析 Android (APK/AAB)、iOS (IPA) 和鸿蒙 (HAP) 的包名、版本、图标等信息。
31
+ - **权限分析**:自动扫描安装包中的敏感权限声明。
32
+ - **构建适配**:支持针对不同平台的构建参数优化。
33
+
34
+ 更多详情请查阅 [官方文档](https://huozhiye.cn/flu-cli)。
38
35
 
39
36
  ### 3. 代码生成
40
37
 
@@ -58,6 +55,15 @@ ProjectGenerator
58
55
  - **Android**: APK / AAB 自动构建
59
56
  - **iOS**: IPA 自动构建
60
57
 
58
+ ### 6. AI + SKILL(规则投影)
59
+
60
+ 通过 `.flu-cli.json`(单一事实来源)投影生成多份 AI 可读规则文件,保证 AI/CLI/VSCode 产出一致:
61
+
62
+ - `.cursor/rules/flu-project.mdc`
63
+ - `CLAUDE.md`
64
+ - `AI_RULES.md`
65
+ - `.agent/skills/<slug>/SKILL.md`
66
+
61
67
  ## 💡 架构优势
62
68
 
63
69
  - **极简核心** — `ProjectGenerator.ts` 从 1500 行精简至 100 行
@@ -67,17 +73,16 @@ ProjectGenerator
67
73
 
68
74
  ## 📦 三种使用方式
69
75
 
70
- | 方式 | 工具 | 用途 |
71
- | ---------- | ------------------- | ----------------- |
72
- | **命令行** | flu-cli (v2) | CI/CD、自动化发版 |
73
- | **可视化** | VSCode 插件 | IDE 内点击操作 |
74
- | **编程** | @huoye/app-ship npm | 自建发布系统 |
76
+ | 方式 | 工具 | 用途 |
77
+ | ---------- | ------------ | ----------------- |
78
+ | **命令行** | flu-cli (v2) | CI/CD、自动化发版 |
79
+ | **可视化** | VSCode 插件 | IDE 内点击操作 |
75
80
 
76
81
  ## 🔗 相关资源
77
82
 
78
- - [@huoye/app-ship 独立上传库](https://gitee.com/flu-cli/flu-cli/blob/main/packages/app-ship/README.md) — 多平台上传引擎
83
+ - [分发模块引用](https://huozhiye.cn/flu-cli) — 核心工具链集成
79
84
  - [VSCode 扩展](https://gitee.com/flu-cli/flu-cli/blob/main/packages/vscode-extension/README.md) — 可视化发布中心
80
- - [CLI v2](https://gitee.com/flu-cli/flu-cli/blob/main/packages/v2/README.md) — 命令行工具
85
+ - [CLI v2](https://gitee.com/flu-cli/flu-cli/blob/main/packages/cli/README.md) — 命令行工具
81
86
  - [官方文档](http://huozhiye.cn/flu-cli/) — 完整指南
82
87
 
83
88
  ## 🚀 核心组件
@@ -0,0 +1,466 @@
1
+ // src/generators/tasks/adapters/provider_adapter.ts
2
+ var ProviderAdapter = class {
3
+ name;
4
+ constructor(name = "provider") {
5
+ this.name = name;
6
+ }
7
+ getDependencies() {
8
+ return this.name === "provider" ? ["provider: ^6.1.1"] : [];
9
+ }
10
+ getBaseViewModelTemplate(context) {
11
+ return null;
12
+ }
13
+ getBasePageParent(isListPage) {
14
+ return isListPage ? "BaseListPage" : "BasePage";
15
+ }
16
+ getBaseViewModelParent(isListPage) {
17
+ return isListPage ? "BaseListViewModel" : "BaseViewModel";
18
+ }
19
+ getImports(relativeCorePath) {
20
+ return [`import '${relativeCorePath}';`];
21
+ }
22
+ patchAppEntry(content, context) {
23
+ return content;
24
+ }
25
+ getCommonInfo() {
26
+ return ` ViewState _state = ViewState.idle;
27
+ String? _errorMessage;
28
+ bool _isRefreshing = false;
29
+
30
+ // ==================== Getters ====================
31
+ // \u5F53\u524D\u89C6\u56FE\u72B6\u6001
32
+ ViewState get state => _state;
33
+ // \u9519\u8BEF\u4FE1\u606F
34
+ String? get errorMessage => _errorMessage;
35
+ // \u662F\u5426\u6B63\u5728\u52A0\u8F7D
36
+ bool get isLoading => _state == ViewState.loading;
37
+ // \u662F\u5426\u52A0\u8F7D\u5931\u8D25
38
+ bool get isError => _state == ViewState.error;
39
+ // \u662F\u5426\u52A0\u8F7D\u6210\u529F
40
+ bool get isSuccess => _state == ViewState.success;
41
+ // \u662F\u5426\u7A7A\u95F2\u72B6\u6001
42
+ bool get isIdle => _state == ViewState.idle;
43
+ // \u662F\u5426\u6B63\u5728\u5237\u65B0
44
+ bool get isRefreshing => _isRefreshing;
45
+
46
+ // ==================== \u72B6\u6001\u8BBE\u7F6E ====================
47
+
48
+ /// \u8BBE\u7F6E\u89C6\u56FE\u72B6\u6001
49
+ void setState(ViewState state, {String? error}) {
50
+ _state = state;
51
+ _errorMessage = error;
52
+ notifyListeners();
53
+ }
54
+
55
+ /// \u8BBE\u7F6E\u5237\u65B0\u72B6\u6001
56
+ void setRefreshing(bool refreshing) {
57
+ _isRefreshing = refreshing;
58
+ notifyListeners();
59
+ }
60
+ `;
61
+ }
62
+ };
63
+
64
+ // src/generators/tasks/adapters/getx_adapter.ts
65
+ var GetXAdapter = class {
66
+ name = "getx";
67
+ getDependencies() {
68
+ return ["get: ^4.6.6"];
69
+ }
70
+ getBaseViewModelTemplate(context) {
71
+ return `import 'package:get/get.dart';
72
+
73
+ enum ViewState { idle, loading, success, error }
74
+
75
+ class BaseViewModel extends GetxController {
76
+ ViewState _state = ViewState.idle;
77
+ String? _errorMessage;
78
+ bool _isRefreshing = false;
79
+ bool _disposed = false;
80
+
81
+ // ==================== Getters ====================
82
+ ViewState get state => _state;
83
+ String? get errorMessage => _errorMessage;
84
+ bool get isLoading => _state == ViewState.loading;
85
+ bool get isError => _state == ViewState.error;
86
+ bool get isSuccess => _state == ViewState.success;
87
+ bool get isIdle => _state == ViewState.idle;
88
+ bool get isRefreshing => _isRefreshing;
89
+ // GetX \u4F7F\u7528 isClosed \u5224\u65AD\u662F\u5426\u5DF2\u91CA\u653E
90
+ bool get isDisposed => _disposed || isClosed;
91
+
92
+ // ==================== \u72B6\u6001\u8BBE\u7F6E ====================
93
+ void setState(ViewState state, {String? error}) {
94
+ if (_disposed || isClosed) return;
95
+ _state = state;
96
+ _errorMessage = error;
97
+ update(); // GetX \u4F7F\u7528 update() \u800C\u975E notifyListeners()
98
+ }
99
+
100
+ void setRefreshing(bool refreshing) {
101
+ if (_disposed || isClosed) return;
102
+ _isRefreshing = refreshing;
103
+ update();
104
+ }
105
+
106
+ /// \u901A\u77E5\u6570\u636E\u53D8\u5316(\u4EC5\u7528\u4E8E\u5C40\u90E8\u5237\u65B0)
107
+ void notifyDataChange() {
108
+ if (_disposed || isClosed) return;
109
+ update();
110
+ }
111
+
112
+ // ==================== \u751F\u547D\u5468\u671F ====================
113
+ /// \u521D\u59CB\u5316\u94A9\u5B50
114
+ @override
115
+ Future<void> onInit() async {
116
+ super.onInit();
117
+ // \u5B50\u7C7B\u53EF\u4EE5\u91CD\u5199\u6B64\u65B9\u6CD5\u8FDB\u884C\u521D\u59CB\u5316
118
+ }
119
+
120
+ /// \u5237\u65B0\u6570\u636E(\u94A9\u5B50)
121
+ Future<void> refreshData() async {
122
+ // \u5B50\u7C7B\u53EF\u4EE5\u91CD\u5199\u6B64\u65B9\u6CD5\u5B9E\u73B0\u5237\u65B0\u903B\u8F91
123
+ }
124
+
125
+ @override
126
+ void onClose() {
127
+ _disposed = true;
128
+ super.onClose();
129
+ }
130
+ }
131
+ `;
132
+ }
133
+ getBasePageParent(isListPage) {
134
+ return isListPage ? "BaseListPage" : "BasePage";
135
+ }
136
+ getBaseViewModelParent(isListPage) {
137
+ return isListPage ? "BaseListViewModel" : "BaseViewModel";
138
+ }
139
+ getImports(relativeCorePath) {
140
+ return [`import '${relativeCorePath}';`];
141
+ }
142
+ /**
143
+ * 修改 app.dart
144
+ */
145
+ patchAppEntry(content, context) {
146
+ let raw = content;
147
+ if (!raw.includes("GetMaterialApp(")) {
148
+ raw = raw.replace(/MaterialApp\s*\(/, "GetMaterialApp(");
149
+ }
150
+ if (raw.includes("GetMaterialApp(") && !raw.includes("import 'package:get/get.dart'")) {
151
+ if (raw.includes("import 'package:flutter/material.dart';")) {
152
+ raw = raw.replace(
153
+ "import 'package:flutter/material.dart';",
154
+ "import 'package:flutter/material.dart';\nimport 'package:get/get.dart';"
155
+ );
156
+ } else {
157
+ raw = `import 'package:get/get.dart';
158
+ ` + raw;
159
+ }
160
+ }
161
+ if (raw.includes("routes: AppRoutes.routes")) {
162
+ raw = raw.replace("routes: AppRoutes.routes", "getPages: AppRoutes.pages");
163
+ }
164
+ if (raw.includes("navigatorKey: NavigatorUtil.navigatorKey")) {
165
+ raw = raw.replace(/navigatorKey:\s*NavigatorUtil\.navigatorKey\s*,?/g, "");
166
+ }
167
+ if (raw.includes("scaffoldMessengerKey: NavigatorUtil.scaffoldMessengerKey")) {
168
+ raw = raw.replace(
169
+ /scaffoldMessengerKey:\s*NavigatorUtil\.scaffoldMessengerKey\s*,?\s*\n?/g,
170
+ ""
171
+ );
172
+ }
173
+ return raw;
174
+ }
175
+ /**
176
+ * 修改 app_routes.dart
177
+ */
178
+ patchRoutes(content, context) {
179
+ let raw = content;
180
+ if (raw.includes("static List<GetPage> get pages")) {
181
+ return raw;
182
+ }
183
+ if (!raw.includes("import 'package:get/get.dart'")) {
184
+ if (raw.includes("import 'package:flutter/material.dart';")) {
185
+ raw = raw.replace(
186
+ "import 'package:flutter/material.dart';",
187
+ "import 'package:get/get.dart';"
188
+ );
189
+ } else {
190
+ raw = `import 'package:get/get.dart';
191
+ ` + raw;
192
+ }
193
+ }
194
+ const startMarker = "// __ROUTE_CONFIG_START__";
195
+ const endMarker = "// __ROUTE_CONFIG_END__";
196
+ const pagesGetter = `
197
+ /// GetX \u8DEF\u7531\u6620\u5C04
198
+ /// \u5728 app.dart \u4E2D\u4F7F\u7528: GetMaterialApp(getPages: AppRoutes.pages, ...)
199
+ static List<GetPage> get pages {
200
+ return [
201
+ GetPage(name: home, page: () => const HomePage()),
202
+ // --- \u5728\u6B64\u4E0B\u65B9\u6DFB\u52A0\u60A8\u7684\u81EA\u5B9A\u4E49\u8DEF\u7531 ---
203
+ ];
204
+ }
205
+ `;
206
+ const startIndex = raw.indexOf(startMarker);
207
+ const endIndex = raw.indexOf(endMarker);
208
+ if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
209
+ const before = raw.substring(0, startIndex);
210
+ const after = raw.substring(endIndex + endMarker.length);
211
+ raw = before + pagesGetter.trim() + after;
212
+ } else {
213
+ const standardRoutesPattern = /\s*\/\/\/\s*Material\s+路由映射[\s\S]*?static\s+Map<String,\s*WidgetBuilder>\s*get\s*routes\s*\{\s*return\s*\{[\s\S]*?\}\s*;\s*\}/;
214
+ if (standardRoutesPattern.test(raw)) {
215
+ raw = raw.replace(standardRoutesPattern, pagesGetter);
216
+ } else {
217
+ const lastBraceIndex = raw.lastIndexOf("}");
218
+ if (lastBraceIndex !== -1) {
219
+ raw = raw.slice(0, lastBraceIndex) + "\n" + pagesGetter + raw.slice(lastBraceIndex);
220
+ }
221
+ }
222
+ }
223
+ return raw;
224
+ }
225
+ };
226
+
227
+ // src/generators/tasks/adapters/bloc_adapter.ts
228
+ var BlocAdapter = class {
229
+ name = "bloc";
230
+ getDependencies() {
231
+ return ["flutter_bloc: ^8.1.3"];
232
+ }
233
+ getBaseViewModelTemplate(_context) {
234
+ return `import 'package:flutter_bloc/flutter_bloc.dart';
235
+
236
+ enum ViewState { idle, loading, success, error }
237
+
238
+ class BaseViewModel extends Cubit<ViewState> {
239
+ String? _errorMessage;
240
+ bool _isRefreshing = false;
241
+ bool _disposed = false;
242
+
243
+ BaseViewModel() : super(ViewState.idle);
244
+
245
+ String? get errorMessage => _errorMessage;
246
+ bool get isLoading => state == ViewState.loading;
247
+ bool get isError => state == ViewState.error;
248
+ bool get isSuccess => state == ViewState.success;
249
+ bool get isIdle => state == ViewState.idle;
250
+ bool get isRefreshing => _isRefreshing;
251
+ bool get isDisposed => _disposed;
252
+
253
+ void setState(ViewState state, {String? error}) {
254
+ if (_disposed) return;
255
+ _errorMessage = error;
256
+ emit(state);
257
+ }
258
+
259
+ void setRefreshing(bool refreshing) {
260
+ if (_disposed) return;
261
+ _isRefreshing = refreshing;
262
+ emit(state);
263
+ }
264
+
265
+ void notifyDataChange() {
266
+ if (_disposed) return;
267
+ emit(state);
268
+ }
269
+
270
+ Future<void> onInit() async {}
271
+ Future<void> refreshData() async {}
272
+
273
+ @override
274
+ Future<void> close() {
275
+ _disposed = true;
276
+ return super.close();
277
+ }
278
+ }
279
+ `;
280
+ }
281
+ getBasePageParent(isListPage) {
282
+ return isListPage ? "BaseListPage" : "BasePage";
283
+ }
284
+ getBaseViewModelParent(isListPage) {
285
+ return isListPage ? "BaseListViewModel" : "BaseViewModel";
286
+ }
287
+ getImports(relativeCorePath) {
288
+ return [`import 'package:flutter_bloc/flutter_bloc.dart';`, `import '${relativeCorePath}';`];
289
+ }
290
+ patchAppEntry(content, _context) {
291
+ let raw = content;
292
+ if (!raw.includes("import 'package:flutter_bloc/flutter_bloc.dart'")) {
293
+ if (raw.includes("import 'package:flutter/material.dart';")) {
294
+ raw = raw.replace(
295
+ "import 'package:flutter/material.dart';",
296
+ "import 'package:flutter/material.dart';\nimport 'package:flutter_bloc/flutter_bloc.dart';"
297
+ );
298
+ } else {
299
+ raw = `import 'package:flutter_bloc/flutter_bloc.dart';
300
+ ${raw}`;
301
+ }
302
+ }
303
+ return raw;
304
+ }
305
+ };
306
+
307
+ // src/generators/tasks/adapters/riverpod_adapter.ts
308
+ import { join } from "path";
309
+ import fsx from "fs-extra";
310
+ var RiverpodAdapter = class {
311
+ name = "riverpod";
312
+ getDependencies() {
313
+ return ["flutter_riverpod: ^2.4.9"];
314
+ }
315
+ getBasePageParent(isListPage) {
316
+ return isListPage ? "BaseListPageRiverpod" : "BasePageRiverpod";
317
+ }
318
+ getBaseViewModelParent(isListPage) {
319
+ return isListPage ? "BaseListNotifier" : "BaseNotifier";
320
+ }
321
+ getImports(relativeCorePath) {
322
+ return [
323
+ `import 'package:flutter_riverpod/flutter_riverpod.dart';`,
324
+ `import '${relativeCorePath}';`
325
+ ];
326
+ }
327
+ getBaseViewModelTemplate(context) {
328
+ return null;
329
+ }
330
+ patchAppEntry(content, context) {
331
+ let raw = content;
332
+ if (raw.includes("runApp(") && !raw.includes("ProviderScope")) {
333
+ raw = raw.replace(/runApp\((.*?)\)/, "runApp(const ProviderScope(child: $1))");
334
+ if (!raw.includes("import 'package:flutter_riverpod/flutter_riverpod.dart'")) {
335
+ raw = `import 'package:flutter_riverpod/flutter_riverpod.dart';
336
+ ` + raw;
337
+ }
338
+ }
339
+ return raw;
340
+ }
341
+ async onEnrich(context) {
342
+ const { projectPath } = context;
343
+ const baseDir = join(projectPath, "lib", "core", "base");
344
+ await fsx.ensureDir(baseDir);
345
+ await fsx.writeFile(
346
+ join(baseDir, "view_state.dart"),
347
+ "enum ViewState { idle, loading, success, error }\n"
348
+ );
349
+ await fsx.writeFile(
350
+ join(baseDir, "base_state.dart"),
351
+ `
352
+ import 'view_state.dart';
353
+
354
+ class BaseState {
355
+ final ViewState state;
356
+ final bool isRefreshing;
357
+ final String? error;
358
+
359
+ const BaseState({
360
+ this.state = ViewState.idle,
361
+ this.isRefreshing = false,
362
+ this.error,
363
+ });
364
+
365
+ BaseState copyWith({ ViewState? state, bool? isRefreshing, String? error }) {
366
+ return BaseState(
367
+ state: state ?? this.state,
368
+ isRefreshing: isRefreshing ?? this.isRefreshing,
369
+ error: error ?? this.error,
370
+ );
371
+ }
372
+
373
+ bool get isLoading => state == ViewState.loading;
374
+ bool get isSuccess => state == ViewState.success;
375
+ bool get isError => state == ViewState.error;
376
+ bool get isIdle => state == ViewState.idle;
377
+ }
378
+ `
379
+ );
380
+ await fsx.writeFile(
381
+ join(baseDir, "base_notifier.dart"),
382
+ `
383
+ import 'package:flutter_riverpod/flutter_riverpod.dart';
384
+ import 'view_state.dart';
385
+ import 'base_state.dart';
386
+
387
+ abstract class BaseNotifier<T extends BaseState> extends Notifier<T> {
388
+ T createInitialState();
389
+ @override
390
+ T build() => createInitialState();
391
+
392
+ void setLoading() { state = (state.copyWith(state: ViewState.loading, error: null)) as T; }
393
+ void setSuccess() { state = (state.copyWith(state: ViewState.success)) as T; }
394
+ void setError(String message) { state = (state.copyWith(state: ViewState.error, error: message)) as T; }
395
+ void setRefreshing(bool refreshing) { state = (state.copyWith(isRefreshing: refreshing)) as T; }
396
+
397
+ Future<void> run(Future<void> Function() task) async {
398
+ setLoading();
399
+ try {
400
+ await task();
401
+ setSuccess();
402
+ } catch (e) {
403
+ setError(e.toString());
404
+ }
405
+ }
406
+ }
407
+ `
408
+ );
409
+ await fsx.writeFile(
410
+ join(baseDir, "base_page_riverpod.dart"),
411
+ `
412
+ import 'package:flutter/material.dart';
413
+ import 'package:flutter_riverpod/flutter_riverpod.dart';
414
+ import 'base_state.dart';
415
+
416
+ typedef ContentBuilder<TState extends BaseState, TNotifier extends Notifier<TState>> = Widget Function(BuildContext context, TState state, TNotifier vm);
417
+
418
+ class BasePageRiverpod<TState extends BaseState, TNotifier extends Notifier<TState>> extends ConsumerWidget {
419
+ final ProviderListenable<TState> provider;
420
+ final String title;
421
+ final ContentBuilder<TState, TNotifier> builder;
422
+
423
+ const BasePageRiverpod({ super.key, required this.provider, required this.title, required this.builder });
424
+
425
+ @override
426
+ Widget build(BuildContext context, WidgetRef ref) {
427
+ final state = ref.watch(provider);
428
+ final vm = ref.read(provider as dynamic).notifier as TNotifier;
429
+ return Scaffold(
430
+ appBar: AppBar(title: Text(title)),
431
+ body: Center(
432
+ child: state.isLoading
433
+ ? const CircularProgressIndicator()
434
+ : state.isError
435
+ ? Text('Error: ' + (state.error ?? ''))
436
+ : builder(context, state, vm),
437
+ ),
438
+ );
439
+ }
440
+ }
441
+ `
442
+ );
443
+ }
444
+ };
445
+
446
+ // src/generators/tasks/adapters/factory.ts
447
+ var StateManagerAdapterFactory = class {
448
+ static getAdapter(name) {
449
+ switch (name.toLowerCase()) {
450
+ case "getx":
451
+ return new GetXAdapter();
452
+ case "bloc":
453
+ return new BlocAdapter();
454
+ case "provider":
455
+ return new ProviderAdapter("provider");
456
+ case "riverpod":
457
+ return new RiverpodAdapter();
458
+ default:
459
+ return new ProviderAdapter("default");
460
+ }
461
+ }
462
+ };
463
+
464
+ export {
465
+ StateManagerAdapterFactory
466
+ };
@@ -0,0 +1,37 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
8
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
9
+ }) : x)(function(x) {
10
+ if (typeof require !== "undefined") return require.apply(this, arguments);
11
+ throw Error('Dynamic require of "' + x + '" is not supported');
12
+ });
13
+ var __commonJS = (cb, mod) => function __require2() {
14
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
15
+ };
16
+ var __copyProps = (to, from, except, desc) => {
17
+ if (from && typeof from === "object" || typeof from === "function") {
18
+ for (let key of __getOwnPropNames(from))
19
+ if (!__hasOwnProp.call(to, key) && key !== except)
20
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
21
+ }
22
+ return to;
23
+ };
24
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
25
+ // If the importer is in node compatibility mode or this is not an ESM
26
+ // file that has been converted to a CommonJS file using a Babel-
27
+ // compatible transform (i.e. "__esModule" has not been set), then set
28
+ // "default" to the CommonJS "module.exports" for node compatibility.
29
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
30
+ mod
31
+ ));
32
+
33
+ export {
34
+ __require,
35
+ __commonJS,
36
+ __toESM
37
+ };
@@ -0,0 +1,7 @@
1
+ import {
2
+ StateManagerAdapterFactory
3
+ } from "./chunk-BGYZU6TU.js";
4
+ import "./chunk-QGM4M3NI.js";
5
+ export {
6
+ StateManagerAdapterFactory
7
+ };
@@ -0,0 +1,7 @@
1
+ import {
2
+ StateManagerAdapterFactory
3
+ } from "./chunk-ANN4MX7M.js";
4
+ import "./chunk-QGM4M3NI.js";
5
+ export {
6
+ StateManagerAdapterFactory
7
+ };