flu-cli 0.0.5 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/CLI.md +349 -0
  2. package/README.md +59 -276
  3. package/config/dev.config.js +56 -0
  4. package/config/templates.js +147 -0
  5. package/index.js +128 -81
  6. package/lib/commands/add.js +472 -0
  7. package/lib/commands/cache.js +99 -0
  8. package/lib/commands/completion.js +94 -0
  9. package/lib/commands/generate.js +26 -0
  10. package/lib/commands/newClack.js +396 -0
  11. package/lib/commands/snippets.js +39 -0
  12. package/lib/commands/templates.js +84 -0
  13. package/lib/generators/component_generator.js +93 -0
  14. package/lib/generators/model_generator.js +303 -0
  15. package/lib/generators/module_generator.js +141 -0
  16. package/lib/generators/page_generator.js +322 -0
  17. package/lib/generators/project_generator.js +96 -0
  18. package/lib/generators/service_generator.js +408 -0
  19. package/lib/generators/state_manager_generator.js +402 -0
  20. package/lib/generators/viewmodel_generator.js +115 -0
  21. package/lib/generators/widget_generator.js +104 -0
  22. package/lib/templates/templateCopier.js +296 -0
  23. package/lib/templates/templateManager.js +191 -0
  24. package/lib/utils/config.js +99 -0
  25. package/lib/utils/flutterHelper.js +85 -0
  26. package/lib/utils/index_updater.js +69 -0
  27. package/lib/utils/logger.js +57 -0
  28. package/lib/utils/project_detector.js +227 -0
  29. package/lib/utils/snippet_loader.js +32 -0
  30. package/lib/utils/string_helper.js +56 -0
  31. package/lib/utils/templateSelectorEnquirer.js +200 -0
  32. package/package.json +31 -6
  33. package/release.sh +107 -0
  34. package/scripts/e2e-state-tests.js +116 -0
  35. package/scripts/sync-base-to-templates.js +108 -0
  36. package/scripts/workspace-clone-all.sh +101 -0
  37. package/scripts/workspace-status-all.sh +112 -0
  38. package/templates/README.md +138 -0
  39. package/templates/base_files/base_list_page.dart.template +174 -0
  40. package/templates/base_files/base_list_viewmodel.dart.template +134 -0
  41. package/templates/base_files/base_page.dart.template +251 -0
  42. package/templates/base_files/base_viewmodel.dart.template +77 -0
  43. package/templates/base_files/theme/status_views_theme.dart.template +46 -0
  44. package/templates/snippets/dart.code-snippets +487 -0
  45. package/lib/createProject.js +0 -220
  46. package/lib/flutterProjectCreator.js +0 -80
  47. package/lib/libCopier.js +0 -368
  48. package/lib/userInteraction.js +0 -274
  49. package/lib/utils.js +0 -200
  50. package/publish.sh +0 -29
@@ -0,0 +1,77 @@
1
+ import 'package:flutter/foundation.dart';
2
+
3
+ /// 页面状态定义
4
+ enum ViewState { idle, loading, success, error }
5
+
6
+ class BaseViewModel extends ChangeNotifier {
7
+ ViewState _state = ViewState.idle;
8
+ String? _errorMessage;
9
+ bool _isRefreshing = false;
10
+ // ==================== Getters ====================
11
+ /// 当前视图状态
12
+ ViewState get state => _state;
13
+
14
+ /// 错误信息
15
+ String? get errorMessage => _errorMessage;
16
+
17
+ /// 是否正在加载
18
+ bool get isLoading => _state == ViewState.loading;
19
+
20
+ /// 是否加载失败
21
+ bool get isError => _state == ViewState.error;
22
+
23
+ /// 是否加载成功
24
+ bool get isSuccess => _state == ViewState.success;
25
+
26
+ /// 是否空闲状态
27
+ bool get isIdle => _state == ViewState.idle;
28
+
29
+ /// 是否正在刷新
30
+
31
+ bool get isRefreshing => _isRefreshing;
32
+ // ==================== 状态设置 ====================
33
+ /// 设置视图状态
34
+ ///
35
+ /// [state] 新的状态
36
+ /// [error] 错误信息(仅在 error 状态时有效)
37
+ void setState(ViewState state, {String? error}) {
38
+ _state = state;
39
+ _errorMessage = error;
40
+ notifyListeners();
41
+ }
42
+
43
+ /// 设置刷新状态
44
+ void setRefreshing(bool refreshing) {
45
+ _isRefreshing = refreshing;
46
+ notifyListeners();
47
+ }
48
+
49
+ @override
50
+ void notifyListeners() {
51
+ super.notifyListeners();
52
+ }
53
+
54
+ /// 通知数据变化(函数级注释)
55
+ ///
56
+ /// - 仅用于局部刷新,避免整页重建
57
+ void notifyDataChange() {
58
+ notifyListeners();
59
+ }
60
+
61
+ // ==================== 生命周期 ====================
62
+ /// 初始化钩子(函数级注释)
63
+ Future<void> onInit() async {
64
+ // 子类可以重写此方法进行初始化
65
+ }
66
+
67
+ /// 刷新钩子(函数级注释)
68
+ Future<void> refreshData() async {
69
+ // 子类可以重写此方法实现刷新逻辑
70
+ }
71
+
72
+ /// 释放资源(函数级注释)
73
+ @override
74
+ void dispose() {
75
+ super.dispose();
76
+ }
77
+ }
@@ -0,0 +1,46 @@
1
+ import 'package:flutter/material.dart';
2
+
3
+ /// 状态占位页全局配置(函数级注释)
4
+ ///
5
+ /// - 统一的 ThemeExtension,用于 Loading/Error/Empty/LoadMore 视图
6
+ class StatusViewsTheme extends ThemeExtension<StatusViewsTheme> {
7
+ final Widget Function(BuildContext ctx)? loadingBuilder;
8
+ final Widget Function(BuildContext ctx, String? msg)? errorBuilder;
9
+ final Widget Function(BuildContext ctx)? emptyBuilder;
10
+ final Color? refreshIndicatorColor;
11
+ final Widget Function(BuildContext ctx, bool hasMore)? loadMoreBuilder;
12
+
13
+ const StatusViewsTheme({
14
+ this.loadingBuilder,
15
+ this.errorBuilder,
16
+ this.emptyBuilder,
17
+ this.refreshIndicatorColor,
18
+ this.loadMoreBuilder,
19
+ });
20
+
21
+ @override
22
+ StatusViewsTheme copyWith({
23
+ Widget Function(BuildContext)? loadingBuilder,
24
+ Widget Function(BuildContext, String?)? errorBuilder,
25
+ Widget Function(BuildContext)? emptyBuilder,
26
+ Color? refreshIndicatorColor,
27
+ Widget Function(BuildContext, bool)? loadMoreBuilder,
28
+ }) {
29
+ return StatusViewsTheme(
30
+ loadingBuilder: loadingBuilder ?? this.loadingBuilder,
31
+ errorBuilder: errorBuilder ?? this.errorBuilder,
32
+ emptyBuilder: emptyBuilder ?? this.emptyBuilder,
33
+ refreshIndicatorColor:
34
+ refreshIndicatorColor ?? this.refreshIndicatorColor,
35
+ loadMoreBuilder: loadMoreBuilder ?? this.loadMoreBuilder,
36
+ );
37
+ }
38
+
39
+ @override
40
+ ThemeExtension<StatusViewsTheme> lerp(
41
+ ThemeExtension<StatusViewsTheme>? other,
42
+ double t,
43
+ ) {
44
+ return this;
45
+ }
46
+ }
@@ -0,0 +1,487 @@
1
+ {
2
+ "InkWell": {
3
+ "prefix": "ik",
4
+ "body": [
5
+ "InkWell(",
6
+ "\tonTap: () {",
7
+ "",
8
+ "\t},",
9
+ "\tchild: body,",
10
+ ");"
11
+ ]
12
+ },
13
+ "by": {
14
+ "prefix": "by",
15
+ "body": [
16
+ "Widget body = Container();",
17
+ "return body;",
18
+ ]
19
+ },
20
+ "状态页 分层模版": {
21
+ "prefix": "stLay",
22
+ "description": "Flutter StatefulWidget 分层结构框架",
23
+ "body": [
24
+ "\t// ------------------------------",
25
+ "\t// 2. 内部状态(仅组件自身需要的临时状态)",
26
+ "\t// ------------------------------",
27
+ "",
28
+ "\t// ------------------------------",
29
+ "\t// 3. 状态更新方法(仅处理内部状态)",
30
+ "\t// ------------------------------",
31
+ "",
32
+ "\t// ------------------------------",
33
+ "\t// 4. UI构建(核心:根据参数和内部状态渲染)",
34
+ "\t// ------------------------------",
35
+ "\t@override",
36
+ "\tWidget build(BuildContext context) {",
37
+ "\t\tfinal Widget body = _buildBody();",
38
+ "\t\treturn body;",
39
+ "\t}",
40
+ "",
41
+ "\t// 主体布局",
42
+ "\tWidget _buildBody() {",
43
+ "\t\tfinal Widget body = Container();",
44
+ "\t\treturn body;",
45
+ "\t}",
46
+ "",
47
+ "\t// ------------------------------",
48
+ "\t// 5. 事件处理(内部交互+转发外部回调)",
49
+ "\t// ------------------------------",
50
+ "",
51
+ "\t// ------------------------------",
52
+ "\t// 6. 辅助方法(UI计算、数据转换等)",
53
+ "\t// ------------------------------",
54
+ "",
55
+ "\t// ------------------------------",
56
+ "\t// 7. 生命周期方法(仅必要时使用)",
57
+ "\t// ------------------------------",
58
+ "",
59
+ "\t// ------------------------------",
60
+ "\t// 8. 资源释放",
61
+ "\t// ------------------------------",
62
+ "\t@override",
63
+ "\tvoid dispose() {",
64
+ "\t\tsuper.dispose();",
65
+ "\t}"
66
+ ]
67
+ },
68
+ "无状态 分层模版": {
69
+ "prefix": "lessLay",
70
+ "description": "Flutter StatelessWidget 分层结构框架",
71
+ "body": [
72
+ "",
73
+ "\t// ------------------------------",
74
+ "\t// 2. UI构建(核心:根据参数渲染)",
75
+ "\t// ------------------------------",
76
+ "\t@override",
77
+ "\tWidget build(BuildContext context) {",
78
+ "\t\tfinal Widget body = _buildBody();",
79
+ "\t\treturn body;",
80
+ "\t}",
81
+ "\t// 主容器(拆分UI结构)",
82
+ "\tWidget _buildBody() {",
83
+ "\t\tfinal Widget body = Container();",
84
+ "\t\treturn body;",
85
+ "\t}",
86
+ "",
87
+ "\t// ------------------------------",
88
+ "\t// 3. 辅助方法(UI计算/转换)",
89
+ "\t// ------------------------------",
90
+ "",
91
+ ]
92
+ },
93
+ "flu.stPage": {
94
+ "prefix": "stPage",
95
+ "description": "Stateful 页面(使用 BasePage)",
96
+ "body": [
97
+ "import 'package:flutter/material.dart';",
98
+ "import '../base/base_page.dart';",
99
+ "import '{{vm_import}}';",
100
+ "",
101
+ "class {{Name}}Page extends BasePage<{{Name}}ViewModel> {",
102
+ " const {{Name}}Page({super.key});",
103
+ "",
104
+ " @override",
105
+ " State<{{Name}}Page> createState() => _{{Name}}PageState();",
106
+ "}",
107
+ "",
108
+ "class _{{Name}}PageState extends BasePageState<{{Name}}ViewModel, {{Name}}Page> {",
109
+ " // ==================== UI 配置 ====================",
110
+ " @override",
111
+ " String get title => '{{title}}';",
112
+ "",
113
+ " // ==================== ViewModel ====================",
114
+ " @override",
115
+ " {{Name}}ViewModel createViewModel() => {{Name}}ViewModel();",
116
+ "",
117
+ " // ==================== UI 构建 ====================",
118
+ " @override",
119
+ " Widget buildContent(BuildContext context) {",
120
+ " return Center(",
121
+ " child: Text('{{Name}} Page'),",
122
+ " );",
123
+ " }",
124
+ "}",
125
+ ""
126
+ ]
127
+ },
128
+ "flu.lessPage": {
129
+ "prefix": "lessPage",
130
+ "description": "Stateless 页面(三段分层 + 增量包裹)",
131
+ "body": [
132
+ "import 'package:flutter/material.dart';",
133
+ "",
134
+ "class {{Name}}Page extends StatelessWidget {",
135
+ " // ------------------------------",
136
+ " // 1. 外部配置参数(构造函数)",
137
+ " // ------------------------------",
138
+ " /// 构造函数",
139
+ " const {{Name}}Page({super.key});",
140
+ "",
141
+ " // ------------------------------",
142
+ " // 2. UI构建(核心:根据参数渲染)",
143
+ " // ------------------------------",
144
+ " @override",
145
+ " Widget build(BuildContext context) {",
146
+ " final appBar = _buildAppBar(context);",
147
+ " final body = _buildBody(context);",
148
+ " return Scaffold(appBar: appBar, body: body);",
149
+ " }",
150
+ "",
151
+ " /// 主容器(style: incremental-wrapping)",
152
+ " Widget _buildBody(BuildContext context) {",
153
+ " Widget w = const SizedBox.shrink();",
154
+ " w = Padding(padding: const EdgeInsets.all(16), child: w);",
155
+ " return w;",
156
+ " }",
157
+ "",
158
+ " AppBar _buildAppBar(BuildContext context) {",
159
+ " return AppBar(title: const Text('{{title}}'));",
160
+ " }",
161
+ "",
162
+ " // ------------------------------",
163
+ " // 3. 辅助方法(UI计算/转换)",
164
+ " // ------------------------------",
165
+ " String _formatTitle(String text) => text;",
166
+ "}",
167
+ ""
168
+ ]
169
+ },
170
+ "flu.stWidget": {
171
+ "prefix": "stWidget",
172
+ "description": "Stateful 组件(七段分层 + 增量包裹)",
173
+ "body": [
174
+ "import 'package:flutter/material.dart';",
175
+ "",
176
+ "class {{Name}}Widget extends StatefulWidget {",
177
+ " // ------------------------------",
178
+ " // 1. 外部配置参数(构造函数)",
179
+ " // ------------------------------",
180
+ " /// 构造函数",
181
+ " const {{Name}}Widget({super.key});",
182
+ "",
183
+ " @override",
184
+ " State<{{Name}}Widget> createState() => _{{Name}}WidgetState();",
185
+ "}",
186
+ "",
187
+ "class _{{Name}}WidgetState extends State<{{Name}}Widget> {",
188
+ " // ------------------------------",
189
+ " // 2. 内部状态(仅组件自身需要的临时状态)",
190
+ " // ------------------------------",
191
+ " int _count = 0;",
192
+ "",
193
+ " // ------------------------------",
194
+ " // 3. 状态更新方法(仅处理内部状态)",
195
+ " // ------------------------------",
196
+ " void _inc() { setState(() { _count++; }); }",
197
+ "",
198
+ " // ------------------------------",
199
+ " // 4. UI构建(核心:根据参数和内部状态渲染)",
200
+ " // ------------------------------",
201
+ " @override",
202
+ " Widget build(BuildContext context) {",
203
+ " Widget w = Text('count: ' + _count.toString());",
204
+ " w = Padding(padding: const EdgeInsets.all(12), child: w);",
205
+ " return w;",
206
+ " }",
207
+ "",
208
+ " // ------------------------------",
209
+ " // 5. 事件处理(内部交互+转发外部回调)",
210
+ " // ------------------------------",
211
+ " void _onPressed() { _inc(); }",
212
+ "",
213
+ " // ------------------------------",
214
+ " // 5.2 转发外部回调(如点击事件)",
215
+ " // ------------------------------",
216
+ " void _emitPressed() { /* TODO */ }",
217
+ "",
218
+ " // ------------------------------",
219
+ " // 6. 辅助方法(UI计算、数据转换等)",
220
+ " // ------------------------------",
221
+ " String _format(String s) => s;",
222
+ "",
223
+ " // ------------------------------",
224
+ " // 7. 生命周期方法(仅必要时使用)",
225
+ " // ------------------------------",
226
+ " @override",
227
+ " void initState() {",
228
+ " super.initState();",
229
+ " }",
230
+ "",
231
+ " @override",
232
+ " void dispose() {",
233
+ " super.dispose();",
234
+ " }",
235
+ "}",
236
+ ""
237
+ ]
238
+ },
239
+ "flu.lessWidget": {
240
+ "prefix": "lessWidget",
241
+ "description": "Stateless 组件(三段分层 + 增量包裹)",
242
+ "body": [
243
+ "import 'package:flutter/material.dart';",
244
+ "",
245
+ "class {{Name}}Widget extends StatelessWidget {",
246
+ " // ------------------------------",
247
+ " // 1. 外部配置参数(构造函数)",
248
+ " // ------------------------------",
249
+ " /// 构造函数",
250
+ " const {{Name}}Widget({super.key});",
251
+ "",
252
+ " // ------------------------------",
253
+ " // 2. UI构建(核心:根据参数渲染)",
254
+ " // ------------------------------",
255
+ " @override",
256
+ " Widget build(BuildContext context) {",
257
+ " Widget w = const SizedBox.shrink();",
258
+ " w = Padding(padding: const EdgeInsets.all(12), child: w);",
259
+ " return w;",
260
+ " }",
261
+ "",
262
+ " // ------------------------------",
263
+ " // 3. 辅助方法(UI计算/转换)",
264
+ " // ------------------------------",
265
+ " String _format(String s) => s;",
266
+ "}",
267
+ ""
268
+ ]
269
+ },
270
+ "flu.component": {
271
+ "prefix": "component",
272
+ "description": "通用组件(模块内或共享)",
273
+ "body": [
274
+ "import 'package:flutter/material.dart';",
275
+ "",
276
+ "class {{Name}}Component extends StatelessWidget {",
277
+ " /// 构造函数",
278
+ " const {{Name}}Component({super.key});",
279
+ "",
280
+ " @override",
281
+ " Widget build(BuildContext context) {",
282
+ " return Container(",
283
+ " padding: const EdgeInsets.all(16),",
284
+ " child: Column(",
285
+ " crossAxisAlignment: CrossAxisAlignment.start,",
286
+ " children: [",
287
+ " Text(",
288
+ " '{{Name}}Component',",
289
+ " style: Theme.of(context).textTheme.titleLarge,",
290
+ " ),",
291
+ " const SizedBox(height: 8),",
292
+ " const Text('Component content goes here'),",
293
+ " ],",
294
+ " ),",
295
+ " );",
296
+ " }",
297
+ "}",
298
+ ""
299
+ ]
300
+ },
301
+ "flu.viewmodel": {
302
+ "prefix": "viewmodel",
303
+ "description": "ViewModel(使用 BaseViewModel)",
304
+ "body": [
305
+ "import '../base/base_viewmodel.dart';",
306
+ "",
307
+ "class {{Name}}ViewModel extends BaseViewModel {",
308
+ " /// 初始化",
309
+ " @override",
310
+ " Future<void> onInit() async {",
311
+ " await super.onInit();",
312
+ " // TODO: 初始化逻辑",
313
+ " }",
314
+ "",
315
+ " /// 刷新数据",
316
+ " @override",
317
+ " Future<void> refreshData() async {",
318
+ " // TODO: 刷新逻辑",
319
+ " }",
320
+ "}",
321
+ ""
322
+ ]
323
+ },
324
+ "flu.service.api": {
325
+ "prefix": "apiService",
326
+ "description": "API Service(http 示例)",
327
+ "body": [
328
+ "import 'dart:convert';",
329
+ "import 'package:http/http.dart' as http;",
330
+ "",
331
+ "class {{Name}}Service {",
332
+ " final String baseUrl = 'https://api.example.com';",
333
+ "",
334
+ " Future<Map<String, dynamic>> getData(String id) async {",
335
+ " try {",
336
+ " final response = await http.get(",
337
+ " Uri.parse('$baseUrl/{{snake_name}}/$id'),",
338
+ " headers: {'Content-Type': 'application/json'},",
339
+ " );",
340
+ " if (response.statusCode == 200) {",
341
+ " return json.decode(response.body);",
342
+ " } else {",
343
+ " throw Exception('Failed: ${response.statusCode}');",
344
+ " }",
345
+ " } catch (e) {",
346
+ " throw Exception('Network error: ' + e.toString());",
347
+ " }",
348
+ " }",
349
+ "}",
350
+ ""
351
+ ]
352
+ },
353
+ "flu.service.auth": {
354
+ "prefix": "authService",
355
+ "description": "Auth Service(登录/登出示例)",
356
+ "body": [
357
+ "import 'dart:convert';",
358
+ "import 'package:http/http.dart' as http;",
359
+ "import 'package:shared_preferences/shared_preferences.dart';",
360
+ "",
361
+ "class {{Name}}Service {",
362
+ " final String baseUrl = 'https://api.example.com';",
363
+ " static const String _tokenKey = 'auth_token';",
364
+ "",
365
+ " Future<Map<String, dynamic>> login(String username, String password) async {",
366
+ " try {",
367
+ " final response = await http.post(",
368
+ " Uri.parse('$baseUrl/auth/login'),",
369
+ " headers: {'Content-Type': 'application/json'},",
370
+ " body: json.encode({'username': username, 'password': password}),",
371
+ " );",
372
+ " if (response.statusCode == 200) {",
373
+ " final data = json.decode(response.body);",
374
+ " return data;",
375
+ " } else {",
376
+ " throw Exception('Login failed: ${response.statusCode}');",
377
+ " }",
378
+ " } catch (e) {",
379
+ " throw Exception('Login error: ' + e.toString());",
380
+ " }",
381
+ " }",
382
+ "}",
383
+ ""
384
+ ]
385
+ },
386
+ "flu.service.storage": {
387
+ "prefix": "storageService",
388
+ "description": "Storage Service(SharedPreferences 示例)",
389
+ "body": [
390
+ "import 'package:shared_preferences/shared_preferences.dart';",
391
+ "import 'dart:convert';",
392
+ "",
393
+ "class {{Name}}Service {",
394
+ " static const String _keyPrefix = '{{snake_name}}_';",
395
+ "",
396
+ " Future<bool> saveString(String key, String value) async {",
397
+ " final prefs = await SharedPreferences.getInstance();",
398
+ " return await prefs.setString(_keyPrefix + key, value);",
399
+ " }",
400
+ "}",
401
+ ""
402
+ ]
403
+ },
404
+ "flu.model": {
405
+ "prefix": "model",
406
+ "description": "基础 Model(JSON 序列化)",
407
+ "body": [
408
+ "class {{Name}}Model {",
409
+ " final String id;",
410
+ " final String name;",
411
+ " final DateTime createdAt;",
412
+ "",
413
+ " {{Name}}Model({",
414
+ " required this.id,",
415
+ " required this.name,",
416
+ " required this.createdAt,",
417
+ " });",
418
+ "",
419
+ " factory {{Name}}Model.fromJson(Map<String, dynamic> json) {",
420
+ " return {{Name}}Model(",
421
+ " id: json['id'] as String,",
422
+ " name: json['name'] as String,",
423
+ " createdAt: DateTime.parse(json['created_at'] as String),",
424
+ " );",
425
+ " }",
426
+ "",
427
+ " Map<String, dynamic> toJson() {",
428
+ " return {",
429
+ " 'id': id,",
430
+ " 'name': name,",
431
+ " 'created_at': createdAt.toIso8601String(),",
432
+ " };",
433
+ " }",
434
+ "}",
435
+ ""
436
+ ]
437
+ },
438
+ "flu.listPage": {
439
+ "prefix": "listPage",
440
+ "description": "List Page (BaseListPage)",
441
+ "body": [
442
+ "import 'package:flutter/material.dart';",
443
+ "import '../base/base_list_page.dart';",
444
+ "import '{{vm_import}}';",
445
+ "import '../models/{{snake_name}}_model.dart';",
446
+ "",
447
+ "class {{Name}}Page extends BaseListPage<{{ModelName}}, {{Name}}ViewModel> {",
448
+ " const {{Name}}Page({super.key});",
449
+ "",
450
+ " @override",
451
+ " State<{{Name}}Page> createState() => _{{Name}}PageState();",
452
+ "}",
453
+ "",
454
+ "class _{{Name}}PageState extends BaseListPageState<{{ModelName}}, {{Name}}ViewModel, {{Name}}Page> {",
455
+ " @override",
456
+ " String get title => '{{title}}';",
457
+ "",
458
+ " @override",
459
+ " {{Name}}ViewModel createViewModel() => {{Name}}ViewModel();",
460
+ "",
461
+ " @override",
462
+ " Widget buildItem(BuildContext context, {{ModelName}} item, int index) {",
463
+ " return ListTile(",
464
+ " title: Text(item.toString()),",
465
+ " );",
466
+ " }",
467
+ "}"
468
+ ]
469
+ },
470
+ "flu.listViewModel": {
471
+ "prefix": "listViewModel",
472
+ "description": "List ViewModel (BaseListViewModel)",
473
+ "body": [
474
+ "import '../base/base_list_viewmodel.dart';",
475
+ "import '../models/{{snake_name}}_model.dart';",
476
+ "",
477
+ "class {{Name}}ViewModel extends BaseListViewModel<{{ModelName}}> {",
478
+ " @override",
479
+ " Future<List<{{ModelName}}>> fetchPage({required int page, required int pageSize}) async {",
480
+ " // TODO: Load data from service",
481
+ " await Future.delayed(const Duration(seconds: 1));",
482
+ " return [];",
483
+ " }",
484
+ "}"
485
+ ]
486
+ }
487
+ }