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.
Files changed (117) 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 +17765 -3244
  7. package/dist/index.d.cts +783 -99
  8. package/dist/index.d.ts +783 -99
  9. package/dist/index.js +17322 -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/DEVELOPER_GUIDE.md.template +150 -0
  66. package/templates/starter_project/README.md.template +99 -0
  67. package/templates/starter_project/analysis_options.yaml.template +28 -0
  68. package/templates/starter_project/lib/app.dart.template +22 -0
  69. package/templates/starter_project/lib/main.dart.template +34 -0
  70. package/templates/starter_project/lib/pages/splash_page.dart.template +154 -0
  71. package/templates/template_clean/lib/features/home/data/datasources/index.dart +1 -0
  72. package/templates/template_clean/lib/features/home/data/models/index.dart +1 -0
  73. package/templates/template_clean/lib/features/home/domain/index.dart +1 -0
  74. package/templates/template_clean/lib/features/home/presentation/pages/home_page.dart +290 -0
  75. package/templates/template_clean/lib/features/home/presentation/pages/index.dart +2 -0
  76. package/templates/template_clean/lib/features/home/presentation/pages/splash_page.dart +154 -0
  77. package/templates/template_clean/lib/features/home/presentation/viewmodels/home_viewmodel.dart +17 -0
  78. package/templates/template_clean/lib/features/index.dart +2 -0
  79. package/templates/template_clean/lib/features/user/data/datasources/home_feed_service.dart +37 -0
  80. package/templates/template_clean/lib/features/user/data/datasources/index.dart +4 -0
  81. package/templates/template_clean/lib/features/user/data/models/index.dart +3 -0
  82. package/templates/template_clean/lib/features/user/data/models/user.dart +15 -0
  83. package/templates/template_clean/lib/features/user/domain/index.dart +1 -0
  84. package/templates/template_clean/lib/features/user/presentation/pages/index.dart +1 -0
  85. package/templates/template_clean/lib/features/user/presentation/pages/user_list_page.dart +27 -0
  86. package/templates/template_clean/lib/features/user/presentation/viewmodels/user_list_viewmodel.dart +88 -0
  87. package/templates/template_clean/lib/features/user/presentation/widgets/user_item_card.dart +24 -0
  88. package/templates/template_clean/lib/shared/extensions/index.dart +1 -0
  89. package/templates/template_clean/lib/shared/widgets/index.dart +1 -0
  90. package/templates/template_lite/lib/models/index.dart +1 -0
  91. package/templates/template_lite/lib/pages/home_page.dart +290 -0
  92. package/templates/template_lite/lib/pages/index.dart +3 -0
  93. package/templates/template_lite/lib/pages/splash_page.dart +154 -0
  94. package/templates/template_lite/lib/pages/user_list_page.dart +29 -0
  95. package/templates/template_lite/lib/services/home_feed_service.dart +37 -0
  96. package/templates/template_lite/lib/services/index.dart +5 -0
  97. package/templates/template_lite/lib/utils/index.dart +1 -0
  98. package/templates/template_lite/lib/viewmodels/home_viewmodel.dart +34 -0
  99. package/templates/template_lite/lib/viewmodels/index.dart +2 -0
  100. package/templates/template_lite/lib/viewmodels/user_list_viewmodel.dart +103 -0
  101. package/templates/template_lite/lib/widgets/index.dart +1 -0
  102. package/templates/template_lite/lib/widgets/user_item_widget.dart +57 -0
  103. package/templates/template_modular/lib/features/home/index.dart +2 -0
  104. package/templates/template_modular/lib/features/home/models/index.dart +1 -0
  105. package/templates/template_modular/lib/features/home/pages/home_page.dart +290 -0
  106. package/templates/template_modular/lib/features/home/pages/index.dart +2 -0
  107. package/templates/template_modular/lib/features/home/pages/splash_page.dart +154 -0
  108. package/templates/template_modular/lib/features/home/services/index.dart +1 -0
  109. package/templates/template_modular/lib/features/home/viewmodels/home_viewmodel.dart +17 -0
  110. package/templates/template_modular/lib/features/index.dart +2 -0
  111. package/templates/template_modular/lib/features/user/index.dart +6 -0
  112. package/templates/template_modular/lib/features/user/pages/user_list_page.dart +26 -0
  113. package/templates/template_modular/lib/features/user/services/home_feed_service.dart +37 -0
  114. package/templates/template_modular/lib/features/user/viewmodels/user_list_viewmodel.dart +103 -0
  115. package/templates/template_modular/lib/features/user/widgets/user_item_widget.dart +24 -0
  116. package/templates/template_modular/lib/shared/utils/index.dart +1 -0
  117. package/templates/template_modular/lib/shared/widgets/index.dart +1 -0
@@ -0,0 +1,67 @@
1
+ import 'package:permission_handler/permission_handler.dart' as permission_handler;
2
+
3
+ /// 项目常用权限枚举。
4
+ enum AppPermission {
5
+ camera,
6
+ photos,
7
+ microphone,
8
+ location,
9
+ notification,
10
+ storage,
11
+ custom,
12
+ }
13
+
14
+ /// 权限申请结果。
15
+ enum PermissionGrantResult {
16
+ granted,
17
+ denied,
18
+ permanentlyDenied,
19
+ restricted,
20
+ }
21
+
22
+ class PermissionHelper {
23
+ const PermissionHelper();
24
+
25
+ /// 申请权限。
26
+ Future<PermissionGrantResult> request(AppPermission permission) async {
27
+ final status = await _toPluginPermission(permission).request();
28
+ return _fromPluginStatus(status);
29
+ }
30
+
31
+ /// 查询权限状态。
32
+ Future<PermissionGrantResult> check(AppPermission permission) async {
33
+ final status = await _toPluginPermission(permission).status;
34
+ return _fromPluginStatus(status);
35
+ }
36
+
37
+ /// 打开系统设置页,适用于 permanentlyDenied 后的引导跳转。
38
+ Future<bool> openSettings() {
39
+ return permission_handler.openAppSettings();
40
+ }
41
+
42
+ permission_handler.Permission _toPluginPermission(AppPermission permission) {
43
+ switch (permission) {
44
+ case AppPermission.camera:
45
+ return permission_handler.Permission.camera;
46
+ case AppPermission.photos:
47
+ return permission_handler.Permission.photos;
48
+ case AppPermission.microphone:
49
+ return permission_handler.Permission.microphone;
50
+ case AppPermission.location:
51
+ return permission_handler.Permission.locationWhenInUse;
52
+ case AppPermission.notification:
53
+ return permission_handler.Permission.notification;
54
+ case AppPermission.storage:
55
+ return permission_handler.Permission.storage;
56
+ case AppPermission.custom:
57
+ return permission_handler.Permission.camera;
58
+ }
59
+ }
60
+
61
+ PermissionGrantResult _fromPluginStatus(permission_handler.PermissionStatus status) {
62
+ if (status.isGranted || status.isLimited) return PermissionGrantResult.granted;
63
+ if (status.isPermanentlyDenied) return PermissionGrantResult.permanentlyDenied;
64
+ if (status.isRestricted) return PermissionGrantResult.restricted;
65
+ return PermissionGrantResult.denied;
66
+ }
67
+ }
@@ -0,0 +1,29 @@
1
+ # WebView Helper
2
+
3
+ WebView Helper 提供项目级网页打开入口,生成后已包含 `webview_flutter` 默认实现和可运行页面。
4
+
5
+ ## 生成定位
6
+
7
+ - 落点:`lib/helpers/webview`
8
+ - 边界:不属于 Core,不从 `lib/core/index.dart` 导出
9
+ - 普通 Flutter 依赖:`webview_flutter`
10
+ - 最小闭环:`WebviewHelper().open(context, request)` 会打开内置 `WebviewPage`
11
+ - 示例入口:只有选择 `webviewBasic` 时,首页才展示 WebView 示例入口
12
+
13
+ ## 扩展位置
14
+
15
+ - 登录态 header:扩展 `WebviewRequest.headers`
16
+ - 外部浏览器策略:扩展 `WebviewHelper.open`
17
+ - 页面导航拦截:扩展 `WebviewPage` 中的 `NavigationDelegate`
18
+
19
+ {{#if_harmony}}
20
+ ## Harmony 注意
21
+
22
+ 当前项目包含 ohos 平台。CLI 会先写入普通 Flutter WebView 依赖以保证 Android/iOS/Web 可用;运行 Harmony 前,请按当前 Harmony Flutter SDK 的三方插件规范替换插件源、path 和 ref。
23
+
24
+ 鸿蒙插件适配参考:
25
+
26
+ - Flutter packages 适配仓库:https://gitcode.com/openharmony-tpc/flutter_packages
27
+ - Plus plugins 适配仓库:https://gitcode.com/openharmony-sig/flutter_plus_plugins
28
+ - 指导参考:https://gitee.com/openharmony-sig/flutter_plus_plugins/blob/master/GUIDE_DOCUMENT.md
29
+ {{/if_harmony}}
@@ -0,0 +1,88 @@
1
+ import 'package:flutter/material.dart';
2
+ import 'package:webview_flutter/webview_flutter.dart';
3
+
4
+ /// WebView 打开参数。
5
+ class WebviewRequest {
6
+ const WebviewRequest({
7
+ required this.url,
8
+ this.title,
9
+ this.headers = const <String, String>{},
10
+ this.openInExternalBrowser = false,
11
+ });
12
+
13
+ final String url;
14
+ final String? title;
15
+ final Map<String, String> headers;
16
+ final bool openInExternalBrowser;
17
+ }
18
+
19
+ /// WebView helper 入口。
20
+ ///
21
+ /// 默认实现使用 webview_flutter 打开内置页面,生成后即可运行;业务侧仍可在
22
+ /// 这里扩展外部浏览器、登录态 header 或自定义导航策略。
23
+ class WebviewHelper {
24
+ const WebviewHelper();
25
+
26
+ /// 打开 WebView 页面。
27
+ Future<void> open(BuildContext context, WebviewRequest request) {
28
+ return Navigator.of(context).push(
29
+ MaterialPageRoute<void>(
30
+ builder: (_) => WebviewPage(request: request),
31
+ ),
32
+ );
33
+ }
34
+ }
35
+
36
+ /// 可直接运行的 WebView 页面。
37
+ class WebviewPage extends StatefulWidget {
38
+ const WebviewPage({
39
+ super.key,
40
+ required this.request,
41
+ });
42
+
43
+ final WebviewRequest request;
44
+
45
+ @override
46
+ State<WebviewPage> createState() => _WebviewPageState();
47
+ }
48
+
49
+ class _WebviewPageState extends State<WebviewPage> {
50
+ late final WebViewController _controller;
51
+ var _progress = 0;
52
+
53
+ @override
54
+ void initState() {
55
+ super.initState();
56
+ _controller = WebViewController()
57
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
58
+ ..setNavigationDelegate(
59
+ NavigationDelegate(
60
+ onProgress: (progress) {
61
+ if (!mounted) return;
62
+ setState(() => _progress = progress);
63
+ },
64
+ ),
65
+ )
66
+ ..loadRequest(
67
+ Uri.parse(widget.request.url),
68
+ headers: widget.request.headers,
69
+ );
70
+ }
71
+
72
+ @override
73
+ Widget build(BuildContext context) {
74
+ final title = widget.request.title ?? widget.request.url;
75
+ return Scaffold(
76
+ appBar: AppBar(
77
+ title: Text(title, maxLines: 1, overflow: TextOverflow.ellipsis),
78
+ ),
79
+ body: Column(
80
+ children: [
81
+ if (_progress < 100)
82
+ LinearProgressIndicator(value: _progress <= 0 ? null : _progress / 100),
83
+ Expanded(child: WebViewWidget(controller: _controller)),
84
+ ],
85
+ ),
86
+ );
87
+ }
88
+ }
@@ -0,0 +1,14 @@
1
+ APP_ENV=dev
2
+ APP_NAME={{projectDisplayName}}
3
+ APP_VERSION=1.0.0
4
+ APP_OPERATOR_NAME={{projectDisplayName}} Team
5
+ PRIVACY_CONTACT_EMAIL=privacy@example.com
6
+ AGREEMENT_EFFECTIVE_DATE=2026-01-01
7
+ API_BASE_URL=https://example.com
8
+ ENABLE_LOG=true
9
+ USE_MOCK_DATA=true
10
+ DEMO_LIST_MODE=finite
11
+ ENABLE_AGREEMENT_DIALOG=true
12
+ AGREEMENT_VERSION=1.0.0
13
+ USER_AGREEMENT_URL=https://example.com/agreement
14
+ PRIVACY_POLICY_URL=https://example.com/privacy
@@ -0,0 +1,14 @@
1
+ APP_ENV=prod
2
+ APP_NAME={{projectDisplayName}}
3
+ APP_VERSION=1.0.0
4
+ APP_OPERATOR_NAME={{projectDisplayName}} Team
5
+ PRIVACY_CONTACT_EMAIL=privacy@example.com
6
+ AGREEMENT_EFFECTIVE_DATE=2026-01-01
7
+ API_BASE_URL=https://api.example.com
8
+ ENABLE_LOG=false
9
+ USE_MOCK_DATA=false
10
+ DEMO_LIST_MODE=standard
11
+ ENABLE_AGREEMENT_DIALOG=true
12
+ AGREEMENT_VERSION=1.0.0
13
+ USER_AGREEMENT_URL=https://example.com/agreement
14
+ PRIVACY_POLICY_URL=https://example.com/privacy
@@ -0,0 +1,14 @@
1
+ APP_ENV=staging
2
+ APP_NAME={{projectDisplayName}}
3
+ APP_VERSION=1.0.0
4
+ APP_OPERATOR_NAME={{projectDisplayName}} Team
5
+ PRIVACY_CONTACT_EMAIL=privacy@example.com
6
+ AGREEMENT_EFFECTIVE_DATE=2026-01-01
7
+ API_BASE_URL=https://jsonplaceholder.typicode.com
8
+ ENABLE_LOG=true
9
+ USE_MOCK_DATA=false
10
+ DEMO_LIST_MODE=load_more_error
11
+ ENABLE_AGREEMENT_DIALOG=true
12
+ AGREEMENT_VERSION=1.0.0
13
+ USER_AGREEMENT_URL=https://example.com/agreement
14
+ PRIVACY_POLICY_URL=https://example.com/privacy
@@ -0,0 +1,54 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {{#if_fvm}}
5
+ {
6
+ "name": "{{projectName}} (FVM dev)",
7
+ "request": "launch",
8
+ "type": "node-terminal",
9
+ "cwd": "${workspaceFolder}",
10
+ "command": "fvm flutter run --dart-define-from-file=.env.dev"
11
+ },
12
+ {
13
+ "name": "{{projectName}} (FVM staging)",
14
+ "request": "launch",
15
+ "type": "node-terminal",
16
+ "cwd": "${workspaceFolder}",
17
+ "command": "fvm flutter run --dart-define-from-file=.env.staging"
18
+ },
19
+ {
20
+ "name": "{{projectName}} (FVM release)",
21
+ "request": "launch",
22
+ "type": "node-terminal",
23
+ "cwd": "${workspaceFolder}",
24
+ "command": "fvm flutter run --release --dart-define-from-file=.env.prod"
25
+ }
26
+ {{else}}
27
+ {
28
+ "name": "{{projectName}} (dev)",
29
+ "request": "launch",
30
+ "type": "dart",
31
+ "cwd": "${workspaceFolder}",
32
+ "program": "lib/main.dart",
33
+ "toolArgs": ["--dart-define-from-file=.env.dev"]
34
+ },
35
+ {
36
+ "name": "{{projectName}} (staging)",
37
+ "request": "launch",
38
+ "type": "dart",
39
+ "cwd": "${workspaceFolder}",
40
+ "program": "lib/main.dart",
41
+ "toolArgs": ["--dart-define-from-file=.env.staging"]
42
+ },
43
+ {
44
+ "name": "{{projectName}} (release)",
45
+ "request": "launch",
46
+ "type": "dart",
47
+ "cwd": "${workspaceFolder}",
48
+ "program": "lib/main.dart",
49
+ "flutterMode": "release",
50
+ "toolArgs": ["--dart-define-from-file=.env.prod"]
51
+ }
52
+ {{/if_fvm}}
53
+ ]
54
+ }
@@ -0,0 +1,150 @@
1
+ # {{projectDisplayName}} 开发指南
2
+
3
+ 本文档面向接手当前项目的开发者,说明从哪里改代码、如何扩展能力、哪些配置上线前必须替换。模板介绍只作为目录理解背景,不作为主要内容。
4
+
5
+ ## 1. 当前项目画像
6
+
7
+ - 模板结构:`{{templateLabel}}`
8
+ - 状态管理:`{{stateManager}}`
9
+ - 网络层:{{networkStatus}}
10
+ - Lint:{{lintDescription}}
11
+
12
+ {{templateDescription}}
13
+
14
+ ## 2. 开发入口
15
+
16
+ 1. 先运行 `{{flutterPubGetCommand}}`。
17
+ 2. 用 `{{flutterRunDevCommand}}` 启动开发环境。
18
+ 3. 从 `lib/app.dart` 看应用壳、路由和协议弹框。
19
+ 4. 从当前模板的页面目录开始写业务页面。
20
+
21
+ 常用命令:
22
+
23
+ ```bash
24
+ {{flutterPubGetCommand}}
25
+ {{flutterRunDevCommand}}
26
+ {{flutterRunStagingCommand}}
27
+ {{flutterBuildReleaseCommand}}
28
+ ```
29
+
30
+ ## 3. 目录职责
31
+
32
+ {{structureSummary}}
33
+
34
+ ### Lite
35
+
36
+ 页面、ViewModel、Service、Model 按类型平铺,适合小团队和早期业务。新增页面时优先放在 `lib/pages`,对应状态和服务放到 `lib/viewmodels` / `lib/services`。
37
+
38
+ ### Modular
39
+
40
+ 业务按 `features/<feature>` 收口,适合多页面和多业务模块。新增业务时优先新建 feature,并在 feature 内维护页面、状态、服务和模型。
41
+
42
+ ### Clean
43
+
44
+ 业务按 `data / domain / presentation` 分层,适合长期维护和多人协作。新增能力时先确认 domain 边界,再补 data 实现和 presentation 页面。
45
+
46
+ ## 4. 配置文件
47
+
48
+ 项目默认生成:
49
+
50
+ | 文件 | 用途 |
51
+ | --- | --- |
52
+ | `.env.dev` | 本地开发 |
53
+ | `.env.staging` | 预发布或测试环境 |
54
+ | `.env.prod.example` | 生产配置模板 |
55
+ | `analysis_options.yaml` | Dart/Flutter 代码规范 |
56
+ | `.vscode/launch.json` | IDE 调试入口 |
57
+
58
+ 生产环境通常这样创建:
59
+
60
+ ```bash
61
+ cp .env.prod.example .env.prod
62
+ ```
63
+
64
+ 关键环境变量:
65
+
66
+ | 变量 | 用途 |
67
+ | --- | --- |
68
+ | `APP_ENV` | 当前环境标识 |
69
+ | `APP_NAME` / `APP_VERSION` | 应用名称与版本 |
70
+ | `API_BASE_URL` | 接口基础地址 |
71
+ | `ENABLE_LOG` | 是否启用调试日志 |
72
+ | `USE_MOCK_DATA` | 是否使用本地样板数据 |
73
+ | `DEMO_LIST_MODE` | 首页样板列表状态:`finite`、`standard`、`empty`、`first_page_error`、`load_more_error` |
74
+ | `ENABLE_AGREEMENT_DIALOG` | 是否启用首次协议弹框 |
75
+ | `AGREEMENT_VERSION` | 协议版本门禁,正文变化后应递增 |
76
+ | `USER_AGREEMENT_URL` / `PRIVACY_POLICY_URL` | 在线协议链接 |
77
+
78
+ ## 5. 代码规范
79
+
80
+ 当前 Lint 配置来自 `analysis_options.yaml`,默认跟随创建配置中的 `stack.lint`。
81
+
82
+ Strict 规则会额外约束:
83
+
84
+ - 单引号
85
+ - import/directive 排序
86
+ - const 构造与 const 字面量
87
+ - 尾随逗号
88
+ - 不必要容器
89
+ - 异步后使用 `BuildContext` 的安全检查
90
+
91
+ 生成代码或手写代码都应先通过:
92
+
93
+ ```bash
94
+ {{flutterCommand}} analyze
95
+ ```
96
+
97
+ ## 6. 能力扩展
98
+
99
+ {{#if_network}}
100
+ 当前项目已启用网络层。网络入口位于 `lib/core/network`,示例服务和页面只作为业务样板,不应把示例模型当成通用 Core。
101
+ {{else}}
102
+ 当前项目未启用网络层;网络层不是默认基座。后续需要网络能力时,可以重新通过 Flu-CLI 生成,或手动引入 Dio/AppHttp 相关结构。
103
+ {{/if_network}}
104
+
105
+ 首页只保留一个主列表槽位:
106
+
107
+ - 开发和离线验收时,用 `USE_MOCK_DATA=true` 读取本地样板数据。
108
+ - 接入网络示例或真实接口时,把数据源替换到同一个列表槽位,不再额外并排一份 mock 列表。
109
+ - 默认 staging 环境使用 `https://jsonplaceholder.typicode.com/posts` 作为可运行网络样板。
110
+ - `DEMO_LIST_MODE` 只用于验证 starter 状态表达;真实业务上线前应删减或替换为业务自己的状态策略。
111
+
112
+ 可选能力边界:
113
+
114
+ - WebView:生成网页承载能力与平台说明
115
+ - Permission:生成权限申请协议
116
+ - Image Picker:生成图片选择协议
117
+ - Payment:仅生成支付协议壳,不内置真实 SDK
118
+ - 示例入口:只追加首页入口和示例页,不代表 Core 必选能力
119
+
120
+ ## 7. SDK 与平台
121
+
122
+ {{#if_fvm}}
123
+ 当前项目使用 FVM 工作区 SDK。推荐使用 `{{flutterCommand}}` 执行项目命令。
124
+
125
+ 标准 FVM 项目默认不在 `.vscode/settings.json` 中写死 `dart.flutterSdkPath` / `dart.sdkPath`,避免同账号多设备同步时把无效绝对路径带到其他机器。
126
+
127
+ `.vscode/launch.json` 会使用 `node-terminal` 显式执行 `fvm flutter run`,不要用普通 Dart Debug 配置隐式选择系统 Flutter。
128
+ {{else}}
129
+ 当前项目使用 System Flutter 或用户选择的 Custom Flutter。如果 VSCode 和终端识别的 Flutter 不一致,先检查 PATH、VSCode 用户设置和项目 `.vscode/settings.json`。
130
+ {{/if_fvm}}
131
+ {{#if_harmony}}
132
+ 当前项目包含 Harmony/ohos 目标平台。Harmony 等非官方 Flutter SDK 可能存在版本元数据或插件适配差异,`pub get`、`analyze`、原生插件运行结果要以实际 SDK 为准。
133
+ {{/if_harmony}}
134
+
135
+ ## 8. 上线前检查
136
+
137
+ 上线前至少完成:
138
+
139
+ - 替换 `.env.*` 中的运营主体、隐私邮箱、协议生效日期
140
+ - 协议正文变化时递增 `.env.*` 中的 `AGREEMENT_VERSION`
141
+ - 替换用户协议与隐私政策正文
142
+ - 核对第三方 SDK、权限用途和数据保存期限
143
+ - 执行 `{{flutterCommand}} analyze`
144
+ - 使用目标环境运行一次 `{{flutterRunStagingCommand}}`
145
+
146
+ ## 9. 项目升级建议
147
+
148
+ 当前项目是单包 Flutter Starter,不默认进入多包 workspace。只有当业务稳定后确实出现共享包、多个 App、跨团队复用或版本治理需求,再考虑升级到 `apps/ + packages/`。
149
+
150
+ 模板结构可以演进,但不要为了目录更复杂而迁移。先让 README、环境、路由、协议和代码规范保持可运行、可解释、可维护。
@@ -0,0 +1,99 @@
1
+ # {{projectDisplayName}}
2
+
3
+ 这是使用 Flu-CLI 创建的 Flutter 项目。本文档只说明当前项目如何安装、运行、配置和继续开发;模板差异只在需要理解目录组织时保留。
4
+
5
+ ## 快速开始
6
+
7
+ ```bash
8
+ {{flutterPubGetCommand}}
9
+ {{flutterRunDevCommand}}
10
+ ```
11
+
12
+ 常用环境:
13
+
14
+ - 开发:`.env.dev`
15
+ - 预发布:`.env.staging`
16
+ - 生产模板:`.env.prod.example`,首次使用时复制为 `.env.prod`
17
+
18
+ ```bash
19
+ cp .env.prod.example .env.prod
20
+ ```
21
+
22
+ ## 当前项目配置
23
+
24
+ | 项目项 | 当前值 |
25
+ | --- | --- |
26
+ | 项目名 | `{{projectName}}` |
27
+ | Dart 包名 | `{{dartPackageName}}` |
28
+ | 应用包名 | `{{packageName}}` |
29
+ | 模板结构 | `{{templateLabel}}` |
30
+ | 状态管理 | `{{stateManager}}` |
31
+ | 网络层 | {{networkStatus}} |
32
+ | Mock 数据 | 由 `.env.*` 中的 `USE_MOCK_DATA` 控制 |
33
+ | 首页样板 | 由 `.env.*` 中的 `DEMO_LIST_MODE` 控制样板状态 |
34
+ | Lint | {{lintDescription}} |
35
+
36
+ ## 目录入口
37
+
38
+ {{structureSummary}}
39
+
40
+ 优先从这些文件开始看:
41
+
42
+ - `lib/main.dart`:应用启动入口,先执行 `AppInitializer.init()`
43
+ - `lib/app.dart`:`MaterialApp`、路由和首次协议弹框挂载点
44
+ - `lib/core/`:路由、主题、配置、工具、协议弹框和通用能力
45
+ - `.env.*`:环境变量与协议信息
46
+ - `.vscode/launch.json`:IDE 调试入口
47
+
48
+ ## 首页样板
49
+
50
+ 默认首页只保留一个主列表槽位,用来验证刷新、分页、空态、错误态和基础状态表达。
51
+
52
+ - `USE_MOCK_DATA=true`:使用本地样板数据,适合开发和离线验收。
53
+ - `USE_MOCK_DATA=false`:使用真实接口数据填充同一个列表槽位。
54
+ - `DEMO_LIST_MODE=finite`:有限分页,用来验证“没有更多了”的默认表达。
55
+ - `DEMO_LIST_MODE=standard`:标准分页,用来验证持续加载体验。
56
+ - `DEMO_LIST_MODE=empty` / `first_page_error` / `load_more_error`:分别用于验证空态、首屏错误和分页错误。
57
+
58
+ 启用网络示例时,不建议再额外堆一份 mock 列表或做双列表 TabBar;应把网络数据接入这个主列表槽位,让首页保持真实项目的默认入口心智。默认 staging 环境使用 `https://jsonplaceholder.typicode.com/posts` 作为可运行网络样板。
59
+
60
+ ## 运行与调试
61
+
62
+ ```bash
63
+ {{flutterRunDevCommand}}
64
+ {{flutterRunStagingCommand}}
65
+ {{flutterBuildReleaseCommand}}
66
+ ```
67
+
68
+ VSCode 调试面板中可直接选择:
69
+
70
+ - `{{projectName}} (dev)`
71
+ - `{{projectName}} (staging)`
72
+ - `{{projectName}} (release)`
73
+
74
+ ## SDK 说明
75
+
76
+ {{#if_fvm}}
77
+ - 当前项目使用 FVM 工作区 SDK。
78
+ - 标准 FVM 项目默认不写死 `dart.flutterSdkPath` / `dart.sdkPath`,避免多设备同步污染。
79
+ - VSCode 调试入口会通过 `node-terminal` 显式执行 `fvm flutter run`,避免 Dart 扩展隐式切到系统 Flutter。
80
+ {{else}}
81
+ - 当前项目使用 System Flutter 或用户选择的 Custom Flutter。
82
+ {{/if_fvm}}
83
+ {{#if_harmony}}
84
+ - 当前项目包含 Harmony/ohos 目标平台;原生插件和 SDK 能力仍需按实际 Harmony Flutter 版本确认。
85
+ {{/if_harmony}}
86
+
87
+ ## 首次协议与上线前配置
88
+
89
+ 项目默认包含首次协议弹框与本地协议页面。上线前至少需要检查:
90
+
91
+ - `.env.*` 中的 `APP_OPERATOR_NAME`
92
+ - `.env.*` 中的 `PRIVACY_CONTACT_EMAIL`
93
+ - `.env.*` 中的 `AGREEMENT_EFFECTIVE_DATE`
94
+ - `.env.*` 中的 `AGREEMENT_VERSION`,协议正文变化后必须递增
95
+ - 用户协议、隐私政策正文与第三方 SDK 说明
96
+
97
+ ## 继续开发
98
+
99
+ 更多关于目录职责、能力扩展、网络层、资源配置、SDK 绑定和项目升级的说明,请看 [DEVELOPER_GUIDE.md](./DEVELOPER_GUIDE.md)。
@@ -0,0 +1,28 @@
1
+ # Flu CLI 生成的项目级 Dart/Flutter 静态检查配置。
2
+ # 默认保持 strict 规则,确保生成代码和后续扩展在提交前就暴露 import、const、
3
+ # 尾随逗号、异步 BuildContext 等常见问题;如需放宽规则,请优先在下方 rules 中
4
+ # 按团队约定逐项调整,不建议直接删除本文件。
5
+ include: package:flutter_lints/flutter.yaml
6
+
7
+ analyzer:
8
+ exclude:
9
+ - "**/*.g.dart"
10
+ - "**/*.freezed.dart"
11
+ - build/**
12
+
13
+ linter:
14
+ rules:
15
+ {{#if_strict_lint}}
16
+ prefer_single_quotes: true
17
+ directives_ordering: true
18
+ prefer_const_constructors: true
19
+ prefer_const_literals_to_create_immutables: true
20
+ require_trailing_commas: true
21
+ prefer_final_fields: true
22
+ prefer_final_locals: false
23
+ sized_box_for_whitespace: true
24
+ avoid_unnecessary_containers: true
25
+ use_build_context_synchronously: true
26
+ {{else}}
27
+ prefer_single_quotes: false
28
+ {{/if_strict_lint}}
@@ -0,0 +1,22 @@
1
+ import 'package:flutter/material.dart';
2
+ import 'core/index.dart';
3
+
4
+ class App extends StatelessWidget {
5
+ const App({super.key});
6
+
7
+ @override
8
+ Widget build(BuildContext context) {
9
+ return MaterialApp(
10
+ navigatorKey: NavigatorUtil.navigatorKey,
11
+ scaffoldMessengerKey: NavigatorUtil.scaffoldMessengerKey,
12
+ title: AppConstants.appName,
13
+ theme: AppTheme.light(),
14
+ darkTheme: AppTheme.dark(),
15
+ themeMode: ThemeMode.system,
16
+ initialRoute:
17
+ AppConfig.I.shouldShowSplash ? AppRoutes.splash : AppRoutes.home,
18
+ routes: AppRoutes.routes,
19
+ debugShowCheckedModeBanner: false,
20
+ );
21
+ }
22
+ }
@@ -0,0 +1,34 @@
1
+ import 'dart:async';
2
+
3
+ import 'package:flutter/foundation.dart';
4
+ import 'package:flutter/material.dart';
5
+
6
+ import 'app.dart';
7
+ import 'core/index.dart';
8
+
9
+ void main() {
10
+ runZonedGuarded(
11
+ () async => _bootstrap(),
12
+ (error, stackTrace) {
13
+ if (kDebugMode) {
14
+ debugPrint(
15
+ FlutterErrorDetails(exception: error, stack: stackTrace).toString(),
16
+ );
17
+ }
18
+ },
19
+ );
20
+ }
21
+
22
+ Future<void> _bootstrap() async {
23
+ WidgetsFlutterBinding.ensureInitialized();
24
+ FlutterError.onError = (details) {
25
+ FlutterError.presentError(details);
26
+ Zone.current.handleUncaughtError(
27
+ details.exception,
28
+ details.stack ?? StackTrace.current,
29
+ );
30
+ };
31
+
32
+ await AppInitializer.init();
33
+ runApp(const App());
34
+ }