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.
- package/README.md +9 -0
- package/dist/chunk-BGYZU6TU.js +466 -0
- package/dist/chunk-QGM4M3NI.js +37 -0
- package/dist/factory-LM2CTHPW.js +7 -0
- package/dist/factory-P6ABQFH3.js +7 -0
- package/dist/index.cjs +17766 -3244
- package/dist/index.d.cts +783 -99
- package/dist/index.d.ts +783 -99
- package/dist/index.js +17323 -2942
- package/dist/upgrade_snippets-BJ6CQY5Q.js +9 -0
- package/package.json +3 -3
- package/templates/README.md +12 -0
- package/templates/core_files/auth/auth_middleware.dart.template +33 -0
- package/templates/core_files/auth/auth_service.dart.template +22 -0
- package/templates/core_files/auth/auth_viewmodel_mixin.dart.template +9 -0
- package/templates/core_files/auth/index.dart.template +4 -0
- package/templates/core_files/base/base_service.dart.template +12 -0
- package/templates/core_files/base/index.dart.template +3 -0
- package/templates/core_files/config/agreement_document_page.dart.template +220 -0
- package/templates/core_files/config/app_agreement.dart.template +297 -0
- package/templates/core_files/config/app_config.dart.template +81 -22
- package/templates/core_files/config/app_env.dart.template +107 -0
- package/templates/core_files/config/app_initializer.dart.template +16 -23
- package/templates/core_files/config/index.dart.template +4 -1
- package/templates/core_files/config/privacy_dialog.dart.template +158 -0
- package/templates/core_files/index.dart.template +3 -0
- package/templates/core_files/mixins/page/keep_alive_mixin.dart.template +6 -0
- package/templates/core_files/mixins/page/scroll_controller_mixin.dart.template +7 -0
- package/templates/core_files/mixins/service/request_guard_mixin.dart.template +18 -0
- package/templates/core_files/mixins/viewmodel/debounce_mixin.dart.template +18 -0
- package/templates/core_files/network/app_http.dart.template +19 -4
- package/templates/core_files/network/index.dart.template +4 -0
- package/templates/core_files/network/interceptors/global_params_interceptor.dart.template +77 -0
- package/templates/core_files/network/interceptors/index.dart.template +3 -0
- package/templates/core_files/network/network_monitor.dart.template +18 -0
- package/templates/core_files/network/response_adapter.dart.template +8 -19
- package/templates/core_files/router/app_routes.dart.template +3 -6
- package/templates/core_files/storage/storage_keys.dart.template +6 -0
- package/templates/core_files/theme/app_color_config.dart.template +32 -0
- package/templates/core_files/theme/app_text_size_config.dart.template +22 -0
- package/templates/core_files/theme/app_text_style_config.dart.template +139 -0
- package/templates/core_files/theme/app_theme.dart.template +72 -12
- package/templates/core_files/theme/index.dart.template +3 -0
- package/templates/core_files/utils/loading_util.dart.template +1 -1
- package/templates/examples/eg_list_page.dart.template +1 -2
- package/templates/examples/eg_service.dart.template +27 -4
- package/templates/examples/home_feed_service.dart.template +37 -0
- package/templates/helper_examples/image_picker_example_page.dart.template +289 -0
- package/templates/helper_examples/index.dart.template +4 -0
- package/templates/helper_examples/payment_shell_example_page.dart.template +67 -0
- package/templates/helper_examples/permission_example_page.dart.template +365 -0
- package/templates/helper_examples/webview_example_page.dart.template +44 -0
- package/templates/helpers/image_picker/README.md.template +30 -0
- package/templates/helpers/image_picker/index.dart.template +73 -0
- package/templates/helpers/payment/README.md.template +29 -0
- package/templates/helpers/payment/index.dart.template +66 -0
- package/templates/helpers/permission/README.md.template +30 -0
- package/templates/helpers/permission/index.dart.template +67 -0
- package/templates/helpers/webview/README.md.template +29 -0
- package/templates/helpers/webview/index.dart.template +88 -0
- package/templates/starter_project/.env.dev.template +14 -0
- package/templates/starter_project/.env.prod.example.template +14 -0
- package/templates/starter_project/.env.staging.template +14 -0
- package/templates/starter_project/.vscode/launch.json.template +54 -0
- package/templates/starter_project/.vscode/settings.json.template +14 -0
- package/templates/starter_project/DEVELOPER_GUIDE.md.template +169 -0
- package/templates/starter_project/README.md.template +117 -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
|
@@ -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,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"files.exclude": {
|
|
3
|
+
"**/.dart_tool": true,
|
|
4
|
+
"**/.fvm/flutter_sdk": true,
|
|
5
|
+
"**/.fvm/versions": true,
|
|
6
|
+
"**/.flutter-plugins": true,
|
|
7
|
+
"**/.flutter-plugins-dependencies": true,
|
|
8
|
+
"**/.idea": true,
|
|
9
|
+
"**/.metadata": true,
|
|
10
|
+
"**/*.iml": true,
|
|
11
|
+
"**/build": true,
|
|
12
|
+
"**/pubspec.lock": true
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
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
|
+
| `.vscode/settings.json` | VSCode 工作区显示与 Flutter 相关设置 |
|
|
58
|
+
|
|
59
|
+
生产环境通常这样创建:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
cp .env.prod.example .env.prod
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
关键环境变量:
|
|
66
|
+
|
|
67
|
+
| 变量 | 用途 |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
| `APP_ENV` | 当前环境标识 |
|
|
70
|
+
| `APP_NAME` / `APP_VERSION` | 应用名称与版本 |
|
|
71
|
+
| `API_BASE_URL` | 接口基础地址 |
|
|
72
|
+
| `ENABLE_LOG` | 是否启用调试日志 |
|
|
73
|
+
| `USE_MOCK_DATA` | 是否使用本地样板数据 |
|
|
74
|
+
| `DEMO_LIST_MODE` | 首页样板列表状态:`finite`、`standard`、`empty`、`first_page_error`、`load_more_error` |
|
|
75
|
+
| `ENABLE_AGREEMENT_DIALOG` | 是否启用首次协议弹框 |
|
|
76
|
+
| `AGREEMENT_VERSION` | 协议版本门禁,正文变化后应递增 |
|
|
77
|
+
| `USER_AGREEMENT_URL` / `PRIVACY_POLICY_URL` | 在线协议链接 |
|
|
78
|
+
|
|
79
|
+
### VSCode 工作区显示
|
|
80
|
+
|
|
81
|
+
项目默认会在 `.vscode/settings.json` 中隐藏一批生成噪音:
|
|
82
|
+
|
|
83
|
+
- `.dart_tool`
|
|
84
|
+
- `.fvm/flutter_sdk`
|
|
85
|
+
- `.fvm/versions`
|
|
86
|
+
- `build`
|
|
87
|
+
- `.metadata`
|
|
88
|
+
- `.flutter-plugins*`
|
|
89
|
+
- `.idea`
|
|
90
|
+
- `*.iml`
|
|
91
|
+
- `pubspec.lock`
|
|
92
|
+
|
|
93
|
+
目标是让 Explorer 默认更聚焦业务代码,而不是把生成缓存长期堆在主视野里。
|
|
94
|
+
|
|
95
|
+
如需调整,直接修改 `.vscode/settings.json` 中的 `files.exclude` 即可。当前模板默认把 `pubspec.lock` 也视为“工作区降噪项”,以换取更清爽的 Explorer;但 `android`、`ios`、`lib`、`test` 这类高频目录仍不做模板级强隐藏,避免排查平台问题或原生配置时来回切设置。
|
|
96
|
+
|
|
97
|
+
## 5. 代码规范
|
|
98
|
+
|
|
99
|
+
当前 Lint 配置来自 `analysis_options.yaml`,默认跟随创建配置中的 `stack.lint`。
|
|
100
|
+
|
|
101
|
+
Strict 规则会额外约束:
|
|
102
|
+
|
|
103
|
+
- 单引号
|
|
104
|
+
- import/directive 排序
|
|
105
|
+
- const 构造与 const 字面量
|
|
106
|
+
- 尾随逗号
|
|
107
|
+
- 不必要容器
|
|
108
|
+
- 异步后使用 `BuildContext` 的安全检查
|
|
109
|
+
|
|
110
|
+
生成代码或手写代码都应先通过:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
{{flutterCommand}} analyze
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## 6. 能力扩展
|
|
117
|
+
|
|
118
|
+
{{#if_network}}
|
|
119
|
+
当前项目已启用网络层。网络入口位于 `lib/core/network`,示例服务和页面只作为业务样板,不应把示例模型当成通用 Core。
|
|
120
|
+
{{else}}
|
|
121
|
+
当前项目未启用网络层;网络层不是默认基座。后续需要网络能力时,可以重新通过 Flu-CLI 生成,或手动引入 Dio/AppHttp 相关结构。
|
|
122
|
+
{{/if_network}}
|
|
123
|
+
|
|
124
|
+
首页只保留一个主列表槽位:
|
|
125
|
+
|
|
126
|
+
- 开发和离线验收时,用 `USE_MOCK_DATA=true` 读取本地样板数据。
|
|
127
|
+
- 接入网络示例或真实接口时,把数据源替换到同一个列表槽位,不再额外并排一份 mock 列表。
|
|
128
|
+
- 默认 staging 环境使用 `https://jsonplaceholder.typicode.com/posts` 作为可运行网络样板。
|
|
129
|
+
- `DEMO_LIST_MODE` 只用于验证 starter 状态表达;真实业务上线前应删减或替换为业务自己的状态策略。
|
|
130
|
+
|
|
131
|
+
可选能力边界:
|
|
132
|
+
|
|
133
|
+
- WebView:生成网页承载能力与平台说明
|
|
134
|
+
- Permission:生成权限申请协议
|
|
135
|
+
- Image Picker:生成图片选择协议
|
|
136
|
+
- Payment:仅生成支付协议壳,不内置真实 SDK
|
|
137
|
+
- 示例入口:只追加首页入口和示例页,不代表 Core 必选能力
|
|
138
|
+
|
|
139
|
+
## 7. SDK 与平台
|
|
140
|
+
|
|
141
|
+
{{#if_fvm}}
|
|
142
|
+
当前项目使用 FVM 工作区 SDK。推荐使用 `{{flutterCommand}}` 执行项目命令。
|
|
143
|
+
|
|
144
|
+
标准 FVM 项目默认不在 `.vscode/settings.json` 中写死 `dart.flutterSdkPath` / `dart.sdkPath`,避免同账号多设备同步时把无效绝对路径带到其他机器。
|
|
145
|
+
|
|
146
|
+
`.vscode/launch.json` 会使用 `node-terminal` 显式执行 `fvm flutter run`,不要用普通 Dart Debug 配置隐式选择系统 Flutter。
|
|
147
|
+
{{else}}
|
|
148
|
+
当前项目使用 System Flutter 或用户选择的 Custom Flutter。如果 VSCode 和终端识别的 Flutter 不一致,先检查 PATH、VSCode 用户设置和项目 `.vscode/settings.json`。
|
|
149
|
+
{{/if_fvm}}
|
|
150
|
+
{{#if_harmony}}
|
|
151
|
+
当前项目包含 Harmony/ohos 目标平台。Harmony 等非官方 Flutter SDK 可能存在版本元数据或插件适配差异,`pub get`、`analyze`、原生插件运行结果要以实际 SDK 为准。
|
|
152
|
+
{{/if_harmony}}
|
|
153
|
+
|
|
154
|
+
## 8. 上线前检查
|
|
155
|
+
|
|
156
|
+
上线前至少完成:
|
|
157
|
+
|
|
158
|
+
- 替换 `.env.*` 中的运营主体、隐私邮箱、协议生效日期
|
|
159
|
+
- 协议正文变化时递增 `.env.*` 中的 `AGREEMENT_VERSION`
|
|
160
|
+
- 替换用户协议与隐私政策正文
|
|
161
|
+
- 核对第三方 SDK、权限用途和数据保存期限
|
|
162
|
+
- 执行 `{{flutterCommand}} analyze`
|
|
163
|
+
- 使用目标环境运行一次 `{{flutterRunStagingCommand}}`
|
|
164
|
+
|
|
165
|
+
## 9. 项目升级建议
|
|
166
|
+
|
|
167
|
+
当前项目是单包 Flutter Starter,不默认进入多包 workspace。只有当业务稳定后确实出现共享包、多个 App、跨团队复用或版本治理需求,再考虑升级到 `apps/ + packages/`。
|
|
168
|
+
|
|
169
|
+
模板结构可以演进,但不要为了目录更复杂而迁移。先让 README、环境、路由、协议和代码规范保持可运行、可解释、可维护。
|
|
@@ -0,0 +1,117 @@
|
|
|
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
|
+
- `.vscode/settings.json`:VSCode 工作区显示与 Flutter 相关设置
|
|
48
|
+
|
|
49
|
+
## 首页样板
|
|
50
|
+
|
|
51
|
+
默认首页只保留一个主列表槽位,用来验证刷新、分页、空态、错误态和基础状态表达。
|
|
52
|
+
|
|
53
|
+
- `USE_MOCK_DATA=true`:使用本地样板数据,适合开发和离线验收。
|
|
54
|
+
- `USE_MOCK_DATA=false`:使用真实接口数据填充同一个列表槽位。
|
|
55
|
+
- `DEMO_LIST_MODE=finite`:有限分页,用来验证“没有更多了”的默认表达。
|
|
56
|
+
- `DEMO_LIST_MODE=standard`:标准分页,用来验证持续加载体验。
|
|
57
|
+
- `DEMO_LIST_MODE=empty` / `first_page_error` / `load_more_error`:分别用于验证空态、首屏错误和分页错误。
|
|
58
|
+
|
|
59
|
+
启用网络示例时,不建议再额外堆一份 mock 列表或做双列表 TabBar;应把网络数据接入这个主列表槽位,让首页保持真实项目的默认入口心智。默认 staging 环境使用 `https://jsonplaceholder.typicode.com/posts` 作为可运行网络样板。
|
|
60
|
+
|
|
61
|
+
## 运行与调试
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
{{flutterRunDevCommand}}
|
|
65
|
+
{{flutterRunStagingCommand}}
|
|
66
|
+
{{flutterBuildReleaseCommand}}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
VSCode 调试面板中可直接选择:
|
|
70
|
+
|
|
71
|
+
- `{{projectName}} (dev)`
|
|
72
|
+
- `{{projectName}} (staging)`
|
|
73
|
+
- `{{projectName}} (release)`
|
|
74
|
+
|
|
75
|
+
项目默认带一份 `.vscode/settings.json`,会隐藏 `.dart_tool`、`build`、`.metadata`、FVM SDK 缓存和 `pubspec.lock` 等生成噪音,让工作区更聚焦业务代码。
|
|
76
|
+
|
|
77
|
+
如果你希望调整显示范围,可以直接修改:
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"files.exclude": {
|
|
82
|
+
"**/.dart_tool": true,
|
|
83
|
+
"**/build": true
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
建议默认只隐藏生成目录和明显缓存项,不要直接把 `android`、`ios`、`lib`、`test` 这类高频目录一起隐藏成团队默认规则;这些文件或目录在排查平台问题、依赖锁定或原生配置时仍然经常需要查看。
|
|
89
|
+
|
|
90
|
+
如果你的团队希望像业务实战项目那样进一步精简 Explorer,也可以继续在 `.vscode/settings.json` 里追加自己的规则;但更推荐先从“隐藏生成噪音”开始,而不是默认把常用源码目录一起藏掉。
|
|
91
|
+
|
|
92
|
+
## SDK 说明
|
|
93
|
+
|
|
94
|
+
{{#if_fvm}}
|
|
95
|
+
- 当前项目使用 FVM 工作区 SDK。
|
|
96
|
+
- 标准 FVM 项目默认不写死 `dart.flutterSdkPath` / `dart.sdkPath`,避免多设备同步污染。
|
|
97
|
+
- VSCode 调试入口会通过 `node-terminal` 显式执行 `fvm flutter run`,避免 Dart 扩展隐式切到系统 Flutter。
|
|
98
|
+
{{else}}
|
|
99
|
+
- 当前项目使用 System Flutter 或用户选择的 Custom Flutter。
|
|
100
|
+
{{/if_fvm}}
|
|
101
|
+
{{#if_harmony}}
|
|
102
|
+
- 当前项目包含 Harmony/ohos 目标平台;原生插件和 SDK 能力仍需按实际 Harmony Flutter 版本确认。
|
|
103
|
+
{{/if_harmony}}
|
|
104
|
+
|
|
105
|
+
## 首次协议与上线前配置
|
|
106
|
+
|
|
107
|
+
项目默认包含首次协议弹框与本地协议页面。上线前至少需要检查:
|
|
108
|
+
|
|
109
|
+
- `.env.*` 中的 `APP_OPERATOR_NAME`
|
|
110
|
+
- `.env.*` 中的 `PRIVACY_CONTACT_EMAIL`
|
|
111
|
+
- `.env.*` 中的 `AGREEMENT_EFFECTIVE_DATE`
|
|
112
|
+
- `.env.*` 中的 `AGREEMENT_VERSION`,协议正文变化后必须递增
|
|
113
|
+
- 用户协议、隐私政策正文与第三方 SDK 说明
|
|
114
|
+
|
|
115
|
+
## 继续开发
|
|
116
|
+
|
|
117
|
+
更多关于目录职责、能力扩展、网络层、资源配置、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
|
+
}
|