flu-cli-core 1.0.5 → 1.1.1

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 +9 -0
  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 +17766 -3244
  7. package/dist/index.d.cts +783 -99
  8. package/dist/index.d.ts +783 -99
  9. package/dist/index.js +17323 -2942
  10. package/dist/upgrade_snippets-BJ6CQY5Q.js +9 -0
  11. package/package.json +3 -3
  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/starter_project/.env.dev.template +14 -0
  62. package/templates/starter_project/.env.prod.example.template +14 -0
  63. package/templates/starter_project/.env.staging.template +14 -0
  64. package/templates/starter_project/.vscode/launch.json.template +54 -0
  65. package/templates/starter_project/.vscode/settings.json.template +14 -0
  66. package/templates/starter_project/DEVELOPER_GUIDE.md.template +169 -0
  67. package/templates/starter_project/README.md.template +117 -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
@@ -0,0 +1,289 @@
1
+ import 'package:flutter/material.dart';
2
+
3
+ import 'package:{{projectName}}/helpers/image_picker/index.dart';
4
+
5
+ /// 图片选择示例页。
6
+ ///
7
+ /// 示例页只控制首页入口和交互演示;图片选择能力本体位于 lib/helpers/image_picker。
8
+ class ImagePickerExamplePage extends StatefulWidget {
9
+ const ImagePickerExamplePage({super.key});
10
+
11
+ @override
12
+ State<ImagePickerExamplePage> createState() => _ImagePickerExamplePageState();
13
+ }
14
+
15
+ class _ImagePickerExamplePageState extends State<ImagePickerExamplePage> {
16
+ final _helper = ImagePickerHelper();
17
+ PickedImageAsset? _selectedAsset;
18
+ ImagePickerSource? _lastSource;
19
+ bool _isPicking = false;
20
+ String? _lastFeedback;
21
+
22
+ Future<void> _pick(ImagePickerSource source) async {
23
+ setState(() {
24
+ _isPicking = true;
25
+ _lastSource = source;
26
+ });
27
+
28
+ try {
29
+ final assets = await _helper.pick(
30
+ ImagePickerRequest(
31
+ source: source,
32
+ compressQuality: 90,
33
+ ),
34
+ );
35
+ if (!mounted) return;
36
+ setState(() {
37
+ _isPicking = false;
38
+ _selectedAsset = assets.isEmpty ? null : assets.first;
39
+ _lastFeedback = assets.isEmpty
40
+ ? '本次没有选中图片。'
41
+ : '已从${source == ImagePickerSource.gallery ? '相册' : '相机'}获取图片,可继续扩展裁剪、上传或表单回填。';
42
+ });
43
+ } catch (error) {
44
+ if (!mounted) return;
45
+ setState(() {
46
+ _isPicking = false;
47
+ _lastFeedback = '图片选择失败,请检查平台权限、模拟器照片库或插件状态。';
48
+ });
49
+ }
50
+ }
51
+
52
+ String _formatBytes(int? bytes) {
53
+ if (bytes == null || bytes <= 0) return '未知大小';
54
+ if (bytes < 1024) return '$bytes B';
55
+ if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB';
56
+ return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB';
57
+ }
58
+
59
+ Widget _buildActionItem({
60
+ required IconData icon,
61
+ required String title,
62
+ required String subtitle,
63
+ required String buttonLabel,
64
+ required VoidCallback onPressed,
65
+ bool outlined = false,
66
+ }) {
67
+ final button = outlined
68
+ ? OutlinedButton.icon(
69
+ onPressed: _isPicking ? null : onPressed,
70
+ icon: Icon(icon),
71
+ label: Text(buttonLabel),
72
+ )
73
+ : FilledButton.icon(
74
+ onPressed: _isPicking ? null : onPressed,
75
+ icon: Icon(icon),
76
+ label: Text(buttonLabel),
77
+ );
78
+
79
+ return Container(
80
+ margin: const EdgeInsets.only(bottom: 14),
81
+ padding: const EdgeInsets.all(16),
82
+ decoration: BoxDecoration(
83
+ color: Colors.white,
84
+ borderRadius: BorderRadius.circular(18),
85
+ border: Border.all(color: const Color(0xFFE5E7EB)),
86
+ boxShadow: const [
87
+ BoxShadow(
88
+ color: Color(0x080F172A),
89
+ blurRadius: 16,
90
+ offset: Offset(0, 8),
91
+ ),
92
+ ],
93
+ ),
94
+ child: Row(
95
+ children: [
96
+ Container(
97
+ width: 44,
98
+ height: 44,
99
+ decoration: BoxDecoration(
100
+ color: const Color(0xFF3B82F6).withOpacity(0.12),
101
+ borderRadius: BorderRadius.circular(14),
102
+ ),
103
+ child: Icon(icon, color: const Color(0xFF2563EB)),
104
+ ),
105
+ const SizedBox(width: 14),
106
+ Expanded(
107
+ child: Column(
108
+ crossAxisAlignment: CrossAxisAlignment.start,
109
+ children: [
110
+ Text(
111
+ title,
112
+ style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w700),
113
+ ),
114
+ const SizedBox(height: 4),
115
+ Text(
116
+ subtitle,
117
+ style: const TextStyle(
118
+ fontSize: 13,
119
+ height: 1.45,
120
+ color: Color(0xFF475569),
121
+ ),
122
+ ),
123
+ ],
124
+ ),
125
+ ),
126
+ const SizedBox(width: 12),
127
+ button,
128
+ ],
129
+ ),
130
+ );
131
+ }
132
+
133
+ Widget _buildPreviewCard() {
134
+ final asset = _selectedAsset;
135
+ if (asset == null) {
136
+ return Container(
137
+ padding: const EdgeInsets.all(22),
138
+ decoration: BoxDecoration(
139
+ color: const Color(0xFFF8FAFC),
140
+ borderRadius: BorderRadius.circular(20),
141
+ ),
142
+ child: const Column(
143
+ children: [
144
+ Icon(Icons.image_outlined, size: 40, color: Color(0xFF94A3B8)),
145
+ SizedBox(height: 10),
146
+ Text(
147
+ '尚未选择图片',
148
+ style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700),
149
+ ),
150
+ SizedBox(height: 6),
151
+ Text(
152
+ '从相册选择或拍照后,这里会直接展示预览和结果信息。',
153
+ textAlign: TextAlign.center,
154
+ style: TextStyle(fontSize: 13, height: 1.5, color: Color(0xFF64748B)),
155
+ ),
156
+ ],
157
+ ),
158
+ );
159
+ }
160
+
161
+ return Container(
162
+ padding: const EdgeInsets.all(16),
163
+ decoration: BoxDecoration(
164
+ color: Colors.white,
165
+ borderRadius: BorderRadius.circular(20),
166
+ border: Border.all(color: const Color(0xFFE5E7EB)),
167
+ ),
168
+ child: Column(
169
+ crossAxisAlignment: CrossAxisAlignment.start,
170
+ children: [
171
+ if (asset.bytes != null)
172
+ ClipRRect(
173
+ borderRadius: BorderRadius.circular(16),
174
+ child: Image.memory(
175
+ asset.bytes!,
176
+ height: 220,
177
+ width: double.infinity,
178
+ fit: BoxFit.cover,
179
+ ),
180
+ )
181
+ else
182
+ Container(
183
+ height: 180,
184
+ decoration: BoxDecoration(
185
+ color: const Color(0xFFF8FAFC),
186
+ borderRadius: BorderRadius.circular(16),
187
+ ),
188
+ alignment: Alignment.center,
189
+ child: const Icon(Icons.broken_image_outlined, size: 42, color: Color(0xFF94A3B8)),
190
+ ),
191
+ const SizedBox(height: 14),
192
+ Row(
193
+ children: [
194
+ const Expanded(
195
+ child: Text(
196
+ '当前已选择图片',
197
+ style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700),
198
+ ),
199
+ ),
200
+ Container(
201
+ padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
202
+ decoration: BoxDecoration(
203
+ color: const Color(0xFFDBEAFE),
204
+ borderRadius: BorderRadius.circular(999),
205
+ ),
206
+ child: Text(
207
+ _lastSource == ImagePickerSource.camera ? '拍照结果' : '相册结果',
208
+ style: const TextStyle(
209
+ fontSize: 12,
210
+ fontWeight: FontWeight.w700,
211
+ color: Color(0xFF1D4ED8),
212
+ ),
213
+ ),
214
+ ),
215
+ ],
216
+ ),
217
+ const SizedBox(height: 10),
218
+ Text(
219
+ asset.name ?? '未命名图片',
220
+ style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
221
+ ),
222
+ const SizedBox(height: 6),
223
+ Text(
224
+ '大小:${_formatBytes(asset.size)}',
225
+ style: const TextStyle(fontSize: 13, color: Color(0xFF475569)),
226
+ ),
227
+ ],
228
+ ),
229
+ );
230
+ }
231
+
232
+ @override
233
+ Widget build(BuildContext context) {
234
+ return Scaffold(
235
+ appBar: AppBar(title: const Text('图片选择示例')),
236
+ body: ListView(
237
+ padding: const EdgeInsets.all(20),
238
+ children: [
239
+ Container(
240
+ padding: const EdgeInsets.all(18),
241
+ decoration: BoxDecoration(
242
+ color: const Color(0xFFF8FAFC),
243
+ borderRadius: BorderRadius.circular(20),
244
+ ),
245
+ child: const Column(
246
+ crossAxisAlignment: CrossAxisAlignment.start,
247
+ children: [
248
+ Text(
249
+ '图片选择最小闭环',
250
+ style: TextStyle(fontSize: 22, fontWeight: FontWeight.w800),
251
+ ),
252
+ SizedBox(height: 10),
253
+ Text(
254
+ '示例会直接展示选中的图片预览、来源和大小,业务项目可在 helper 基础上继续扩展裁剪、多选、上传和表单绑定。',
255
+ style: TextStyle(fontSize: 14, height: 1.55, color: Color(0xFF475569)),
256
+ ),
257
+ ],
258
+ ),
259
+ ),
260
+ const SizedBox(height: 20),
261
+ _buildActionItem(
262
+ icon: Icons.photo_library_outlined,
263
+ title: '从相册选择',
264
+ subtitle: '适合头像、封面或资料图片上传前的选择场景。',
265
+ buttonLabel: _isPicking && _lastSource == ImagePickerSource.gallery ? '选择中...' : '选择',
266
+ onPressed: () => _pick(ImagePickerSource.gallery),
267
+ ),
268
+ _buildActionItem(
269
+ icon: Icons.photo_camera_outlined,
270
+ title: '拍照',
271
+ subtitle: '适合即时拍摄票据、实物或扫码拍照等场景。',
272
+ buttonLabel: _isPicking && _lastSource == ImagePickerSource.camera ? '拍摄中...' : '拍照',
273
+ onPressed: () => _pick(ImagePickerSource.camera),
274
+ outlined: true,
275
+ ),
276
+ const SizedBox(height: 6),
277
+ _buildPreviewCard(),
278
+ if (_lastFeedback != null) ...[
279
+ const SizedBox(height: 14),
280
+ Text(
281
+ _lastFeedback!,
282
+ style: const TextStyle(fontSize: 13, height: 1.5, color: Color(0xFF475569)),
283
+ ),
284
+ ],
285
+ ],
286
+ ),
287
+ );
288
+ }
289
+ }
@@ -0,0 +1,4 @@
1
+ export 'image_picker_example_page.dart';
2
+ export 'payment_shell_example_page.dart';
3
+ export 'permission_example_page.dart';
4
+ export 'webview_example_page.dart';
@@ -0,0 +1,67 @@
1
+ import 'package:flutter/material.dart';
2
+
3
+ import 'package:{{projectName}}/helpers/payment/index.dart';
4
+
5
+ /// 支付协议壳示例页。
6
+ ///
7
+ /// Payment 是明确例外:这里只展示统一协议与模拟 executor,不内置真实支付 SDK。
8
+ class PaymentShellExamplePage extends StatefulWidget {
9
+ const PaymentShellExamplePage({super.key});
10
+
11
+ @override
12
+ State<PaymentShellExamplePage> createState() => _PaymentShellExamplePageState();
13
+ }
14
+
15
+ class _PaymentShellExamplePageState extends State<PaymentShellExamplePage> {
16
+ PaymentResult? _result;
17
+
18
+ Future<void> _simulatePayment() async {
19
+ final helper = PaymentHelper(
20
+ executor: (request) async {
21
+ return PaymentResult(
22
+ status: PaymentStatus.cancelled,
23
+ message: '当前只是协议壳示例,请接入真实支付 SDK 后替换 executor。',
24
+ raw: request.payload,
25
+ );
26
+ },
27
+ );
28
+ final result = await helper.pay(
29
+ const PaymentRequest(
30
+ channel: PaymentChannel.custom,
31
+ orderId: 'demo_order_001',
32
+ payload: <String, Object?>{'amount': 1},
33
+ ),
34
+ );
35
+ if (!mounted) return;
36
+ setState(() => _result = result);
37
+ }
38
+
39
+ @override
40
+ Widget build(BuildContext context) {
41
+ return Scaffold(
42
+ appBar: AppBar(title: const Text('支付协议壳示例')),
43
+ body: ListView(
44
+ padding: const EdgeInsets.all(20),
45
+ children: [
46
+ const Text(
47
+ 'Payment 只生成统一协议壳。',
48
+ style: TextStyle(fontSize: 18, fontWeight: FontWeight.w700),
49
+ ),
50
+ const SizedBox(height: 12),
51
+ const Text('真实支付 SDK、渠道签名和订单校验需要由业务项目按渠道接入。'),
52
+ const SizedBox(height: 20),
53
+ FilledButton.icon(
54
+ onPressed: _simulatePayment,
55
+ icon: const Icon(Icons.payments_outlined),
56
+ label: const Text('运行协议壳示例'),
57
+ ),
58
+ if (_result != null) ...[
59
+ const SizedBox(height: 16),
60
+ Text('模拟结果:${_result!.status.name}'),
61
+ if (_result!.message != null) Text(_result!.message!),
62
+ ],
63
+ ],
64
+ ),
65
+ );
66
+ }
67
+ }