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.
- package/README.md +22 -17
- 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 +18901 -3313
- package/dist/index.d.cts +833 -72
- package/dist/index.d.ts +833 -72
- package/dist/index.js +18569 -3132
- package/dist/upgrade_snippets-BJ6CQY5Q.js +9 -0
- package/package.json +9 -4
- 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/snippets/flu-cli.code-snippets +35 -26
- 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
|
@@ -23,18 +23,15 @@ ProjectGenerator
|
|
|
23
23
|
|
|
24
24
|
### 2. 多平台应用商店上传 ✨
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
本模块提供了高效的应用包信息解析与处理能力。
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
- ✅ 华为 AppGallery — 国内商店
|
|
30
|
-
- ✅ 小米应用商店 — RSA 签名认证
|
|
31
|
-
- ✅ OPPO 应用商店 — HMAC-SHA256
|
|
32
|
-
- ✅ vivo 应用商店 — 一站式直传
|
|
33
|
-
- ✅ 腾讯应用宝 — COS 直传
|
|
34
|
-
- ✅ App Store — Transporter/altool
|
|
35
|
-
- ✅ 鸿蒙应用市场 — 华为 API 复用
|
|
28
|
+
### 主要功能
|
|
36
29
|
|
|
37
|
-
|
|
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)
|
|
73
|
-
| **可视化** | VSCode 插件
|
|
74
|
-
| **编程** | @huoye/app-ship npm | 自建发布系统 |
|
|
76
|
+
| 方式 | 工具 | 用途 |
|
|
77
|
+
| ---------- | ------------ | ----------------- |
|
|
78
|
+
| **命令行** | flu-cli (v2) | CI/CD、自动化发版 |
|
|
79
|
+
| **可视化** | VSCode 插件 | IDE 内点击操作 |
|
|
75
80
|
|
|
76
81
|
## 🔗 相关资源
|
|
77
82
|
|
|
78
|
-
- [
|
|
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/
|
|
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
|
+
};
|