flu-cli-core 1.0.5 → 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.
- package/README.md +9 -0
- package/dist/chunk-BGYZU6TU.js +466 -0
- package/dist/chunk-QGM4M3NI.js +37 -0
- package/dist/factory-LM2CTHPW.js +7 -0
- package/dist/factory-P6ABQFH3.js +7 -0
- package/dist/index.cjs +17765 -3244
- package/dist/index.d.cts +783 -99
- package/dist/index.d.ts +783 -99
- package/dist/index.js +17322 -2942
- package/dist/upgrade_snippets-BJ6CQY5Q.js +9 -0
- package/package.json +3 -3
- package/templates/README.md +12 -0
- package/templates/core_files/auth/auth_middleware.dart.template +33 -0
- package/templates/core_files/auth/auth_service.dart.template +22 -0
- package/templates/core_files/auth/auth_viewmodel_mixin.dart.template +9 -0
- package/templates/core_files/auth/index.dart.template +4 -0
- package/templates/core_files/base/base_service.dart.template +12 -0
- package/templates/core_files/base/index.dart.template +3 -0
- package/templates/core_files/config/agreement_document_page.dart.template +220 -0
- package/templates/core_files/config/app_agreement.dart.template +297 -0
- package/templates/core_files/config/app_config.dart.template +81 -22
- package/templates/core_files/config/app_env.dart.template +107 -0
- package/templates/core_files/config/app_initializer.dart.template +16 -23
- package/templates/core_files/config/index.dart.template +4 -1
- package/templates/core_files/config/privacy_dialog.dart.template +158 -0
- package/templates/core_files/index.dart.template +3 -0
- package/templates/core_files/mixins/page/keep_alive_mixin.dart.template +6 -0
- package/templates/core_files/mixins/page/scroll_controller_mixin.dart.template +7 -0
- package/templates/core_files/mixins/service/request_guard_mixin.dart.template +18 -0
- package/templates/core_files/mixins/viewmodel/debounce_mixin.dart.template +18 -0
- package/templates/core_files/network/app_http.dart.template +19 -4
- package/templates/core_files/network/index.dart.template +4 -0
- package/templates/core_files/network/interceptors/global_params_interceptor.dart.template +77 -0
- package/templates/core_files/network/interceptors/index.dart.template +3 -0
- package/templates/core_files/network/network_monitor.dart.template +18 -0
- package/templates/core_files/network/response_adapter.dart.template +8 -19
- package/templates/core_files/router/app_routes.dart.template +3 -6
- package/templates/core_files/storage/storage_keys.dart.template +6 -0
- package/templates/core_files/theme/app_color_config.dart.template +32 -0
- package/templates/core_files/theme/app_text_size_config.dart.template +22 -0
- package/templates/core_files/theme/app_text_style_config.dart.template +139 -0
- package/templates/core_files/theme/app_theme.dart.template +72 -12
- package/templates/core_files/theme/index.dart.template +3 -0
- package/templates/core_files/utils/loading_util.dart.template +1 -1
- package/templates/examples/eg_list_page.dart.template +1 -2
- package/templates/examples/eg_service.dart.template +27 -4
- package/templates/examples/home_feed_service.dart.template +37 -0
- package/templates/helper_examples/image_picker_example_page.dart.template +289 -0
- package/templates/helper_examples/index.dart.template +4 -0
- package/templates/helper_examples/payment_shell_example_page.dart.template +67 -0
- package/templates/helper_examples/permission_example_page.dart.template +365 -0
- package/templates/helper_examples/webview_example_page.dart.template +44 -0
- package/templates/helpers/image_picker/README.md.template +30 -0
- package/templates/helpers/image_picker/index.dart.template +73 -0
- package/templates/helpers/payment/README.md.template +29 -0
- package/templates/helpers/payment/index.dart.template +66 -0
- package/templates/helpers/permission/README.md.template +30 -0
- package/templates/helpers/permission/index.dart.template +67 -0
- package/templates/helpers/webview/README.md.template +29 -0
- package/templates/helpers/webview/index.dart.template +88 -0
- package/templates/starter_project/.env.dev.template +14 -0
- package/templates/starter_project/.env.prod.example.template +14 -0
- package/templates/starter_project/.env.staging.template +14 -0
- package/templates/starter_project/.vscode/launch.json.template +54 -0
- package/templates/starter_project/DEVELOPER_GUIDE.md.template +150 -0
- package/templates/starter_project/README.md.template +99 -0
- package/templates/starter_project/analysis_options.yaml.template +28 -0
- package/templates/starter_project/lib/app.dart.template +22 -0
- package/templates/starter_project/lib/main.dart.template +34 -0
- package/templates/starter_project/lib/pages/splash_page.dart.template +154 -0
- package/templates/template_clean/lib/features/home/data/datasources/index.dart +1 -0
- package/templates/template_clean/lib/features/home/data/models/index.dart +1 -0
- package/templates/template_clean/lib/features/home/domain/index.dart +1 -0
- package/templates/template_clean/lib/features/home/presentation/pages/home_page.dart +290 -0
- package/templates/template_clean/lib/features/home/presentation/pages/index.dart +2 -0
- package/templates/template_clean/lib/features/home/presentation/pages/splash_page.dart +154 -0
- package/templates/template_clean/lib/features/home/presentation/viewmodels/home_viewmodel.dart +17 -0
- package/templates/template_clean/lib/features/index.dart +2 -0
- package/templates/template_clean/lib/features/user/data/datasources/home_feed_service.dart +37 -0
- package/templates/template_clean/lib/features/user/data/datasources/index.dart +4 -0
- package/templates/template_clean/lib/features/user/data/models/index.dart +3 -0
- package/templates/template_clean/lib/features/user/data/models/user.dart +15 -0
- package/templates/template_clean/lib/features/user/domain/index.dart +1 -0
- package/templates/template_clean/lib/features/user/presentation/pages/index.dart +1 -0
- package/templates/template_clean/lib/features/user/presentation/pages/user_list_page.dart +27 -0
- package/templates/template_clean/lib/features/user/presentation/viewmodels/user_list_viewmodel.dart +88 -0
- package/templates/template_clean/lib/features/user/presentation/widgets/user_item_card.dart +24 -0
- package/templates/template_clean/lib/shared/extensions/index.dart +1 -0
- package/templates/template_clean/lib/shared/widgets/index.dart +1 -0
- package/templates/template_lite/lib/models/index.dart +1 -0
- package/templates/template_lite/lib/pages/home_page.dart +290 -0
- package/templates/template_lite/lib/pages/index.dart +3 -0
- package/templates/template_lite/lib/pages/splash_page.dart +154 -0
- package/templates/template_lite/lib/pages/user_list_page.dart +29 -0
- package/templates/template_lite/lib/services/home_feed_service.dart +37 -0
- package/templates/template_lite/lib/services/index.dart +5 -0
- package/templates/template_lite/lib/utils/index.dart +1 -0
- package/templates/template_lite/lib/viewmodels/home_viewmodel.dart +34 -0
- package/templates/template_lite/lib/viewmodels/index.dart +2 -0
- package/templates/template_lite/lib/viewmodels/user_list_viewmodel.dart +103 -0
- package/templates/template_lite/lib/widgets/index.dart +1 -0
- package/templates/template_lite/lib/widgets/user_item_widget.dart +57 -0
- package/templates/template_modular/lib/features/home/index.dart +2 -0
- package/templates/template_modular/lib/features/home/models/index.dart +1 -0
- package/templates/template_modular/lib/features/home/pages/home_page.dart +290 -0
- package/templates/template_modular/lib/features/home/pages/index.dart +2 -0
- package/templates/template_modular/lib/features/home/pages/splash_page.dart +154 -0
- package/templates/template_modular/lib/features/home/services/index.dart +1 -0
- package/templates/template_modular/lib/features/home/viewmodels/home_viewmodel.dart +17 -0
- package/templates/template_modular/lib/features/index.dart +2 -0
- package/templates/template_modular/lib/features/user/index.dart +6 -0
- package/templates/template_modular/lib/features/user/pages/user_list_page.dart +26 -0
- package/templates/template_modular/lib/features/user/services/home_feed_service.dart +37 -0
- package/templates/template_modular/lib/features/user/viewmodels/user_list_viewmodel.dart +103 -0
- package/templates/template_modular/lib/features/user/widgets/user_item_widget.dart +24 -0
- package/templates/template_modular/lib/shared/utils/index.dart +1 -0
- package/templates/template_modular/lib/shared/widgets/index.dart +1 -0
package/README.md
CHANGED
|
@@ -55,6 +55,15 @@ ProjectGenerator
|
|
|
55
55
|
- **Android**: APK / AAB 自动构建
|
|
56
56
|
- **iOS**: IPA 自动构建
|
|
57
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
|
+
|
|
58
67
|
## 💡 架构优势
|
|
59
68
|
|
|
60
69
|
- **极简核心** — `ProjectGenerator.ts` 从 1500 行精简至 100 行
|
|
@@ -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
|
+
};
|