From dad37faecb0250b3b432be0fa7570cb1b68349b7 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 20 Mar 2026 10:14:43 +0800 Subject: [PATCH] =?UTF-8?q?feat(launch=5Foverlay):=20=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E5=93=81=E7=89=8C=20Logo=20=E5=92=8C=20AppName=20=E6=9B=BF?= =?UTF-8?q?=E6=8D=A2=E9=80=9A=E7=94=A8=E5=9B=BE=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ShellEnvironment 新增 splashImage 字段支持品牌启动图标 - LaunchOverlay 使用 splash.png 替换 Icons.language_rounded - 标题改为品牌名称(appName),移除副标题 - 加载指示器改为 CircularProgressIndicator 不定态转圈 - 使用 accentColor 主题色 - generate_app.dart 自动注册 assets/branding/ 并生成 splashImage 配置 - quanxue pubspec.yaml 注册 assets/branding/ 目录 --- apps/quanxue/lib/main.dart | 1 + apps/quanxue/pubspec.yaml | 6 +- .../lib/src/config/shell_environment.dart | 4 + .../lib/src/ui/launch_overlay.dart | 103 +++++++++++------- tool/generate_app.dart | 20 ++++ 5 files changed, 90 insertions(+), 44 deletions(-) diff --git a/apps/quanxue/lib/main.dart b/apps/quanxue/lib/main.dart index 4de210c..a31e9c0 100644 --- a/apps/quanxue/lib/main.dart +++ b/apps/quanxue/lib/main.dart @@ -10,6 +10,7 @@ void main() { backgroundColor: const Color(0xFFFFFFFF), textColor: const Color(0xFF1F2937), mutedTextColor: const Color(0xFF6B7280), + splashImage: const AssetImage('assets/branding/splash.png'), initialUrl: "http://192.168.2.57:8080/test_bridge.html", ), ); diff --git a/apps/quanxue/pubspec.yaml b/apps/quanxue/pubspec.yaml index c5c5545..9fff0e2 100644 --- a/apps/quanxue/pubspec.yaml +++ b/apps/quanxue/pubspec.yaml @@ -61,10 +61,8 @@ flutter: # the material Icons class. uses-material-design: true - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg + assets: + - assets/branding/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/to/resolution-aware-images diff --git a/packages/web_shell_core/lib/src/config/shell_environment.dart b/packages/web_shell_core/lib/src/config/shell_environment.dart index e3fbd81..3f33596 100644 --- a/packages/web_shell_core/lib/src/config/shell_environment.dart +++ b/packages/web_shell_core/lib/src/config/shell_environment.dart @@ -11,6 +11,7 @@ class ShellEnvironment { required this.backgroundColor, required this.textColor, required this.mutedTextColor, + this.splashImage, this.initialUrl, }); @@ -32,6 +33,9 @@ class ShellEnvironment { /// 品牌次要文字色 final Color mutedTextColor; + /// 可选的品牌启动页图标;为空时使用默认图标。 + final ImageProvider? splashImage; + /// 可选的初始地址;为空时使用默认地址。 final String? initialUrl; } diff --git a/packages/web_shell_core/lib/src/ui/launch_overlay.dart b/packages/web_shell_core/lib/src/ui/launch_overlay.dart index 2cd78b5..768678a 100644 --- a/packages/web_shell_core/lib/src/ui/launch_overlay.dart +++ b/packages/web_shell_core/lib/src/ui/launch_overlay.dart @@ -17,64 +17,86 @@ class LaunchOverlay extends StatelessWidget { @override Widget build(BuildContext context) { + final splashImage = _env.splashImage; + return ColoredBox( color: _shellBackgroundColor, child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ - Container( - width: 88, - height: 88, - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [const Color(0xFF66E59A), _shellAccentColor], - begin: Alignment.topLeft, - end: Alignment.bottomRight, + // ── 品牌 Logo ── + if (splashImage != null) + Container( + width: 88, + height: 88, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(24), + boxShadow: [ + BoxShadow( + color: _shellAccentColor.withValues(alpha: 0.2), + blurRadius: 20, + offset: const Offset(0, 6), + ), + ], ), - borderRadius: BorderRadius.circular(24), - boxShadow: [ - BoxShadow( - color: _shellAccentColor.withValues(alpha: 0.3), - blurRadius: 20, - offset: const Offset(0, 6), + child: ClipRRect( + borderRadius: BorderRadius.circular(24), + child: Image( + image: splashImage, + width: 88, + height: 88, + fit: BoxFit.contain, ), - ], + ), + ) + else + Container( + width: 88, + height: 88, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + const Color(0xFF66E59A), + _shellAccentColor, + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + borderRadius: BorderRadius.circular(24), + boxShadow: [ + BoxShadow( + color: _shellAccentColor.withValues(alpha: 0.3), + blurRadius: 20, + offset: const Offset(0, 6), + ), + ], + ), + alignment: Alignment.center, + child: const Icon( + Icons.language_rounded, + size: 42, + color: Colors.white, + ), ), - alignment: Alignment.center, - child: const Icon( - Icons.language_rounded, - size: 42, - color: Colors.white, - ), - ), const SizedBox(height: 24), + // ── 品牌名称 ── Text( - '页面加载中', + _env.appName, style: TextStyle( fontSize: 24, fontWeight: FontWeight.w800, color: _shellTextColor, ), ), - const SizedBox(height: 10), - Text( - '正在为你启动 H5 页面', - style: TextStyle(color: _shellMutedTextColor, fontSize: 14), - ), const SizedBox(height: 36), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 64), - child: ClipRRect( - borderRadius: BorderRadius.circular(6), - child: LinearProgressIndicator( - minHeight: 6, - value: hasMeasuredProgress ? progress / 100 : null, - backgroundColor: const Color(0xFFE7F3EB), - valueColor: AlwaysStoppedAnimation( - _shellAccentColor, - ), - ), + // ── 不定态转圈 ── + SizedBox( + width: 32, + height: 32, + child: CircularProgressIndicator( + strokeWidth: 3, + valueColor: AlwaysStoppedAnimation(_shellAccentColor), ), ), ], @@ -83,3 +105,4 @@ class LaunchOverlay extends StatelessWidget { ); } } + diff --git a/tool/generate_app.dart b/tool/generate_app.dart index 46f4866..5aca0fe 100644 --- a/tool/generate_app.dart +++ b/tool/generate_app.dart @@ -78,6 +78,9 @@ Future main(List args) async { // 7. 生成图标与启动页配置 await _generateBrandingAssets(brand, appDir, config); + // 8. 在 pubspec.yaml 中注册 Flutter assets + await _registerFlutterAssets(appDir); + print('\x1B[32m✔ 应用 $brand 已生成到 $appDir!\x1B[0m'); print('\x1B[34m构建应用请执行:\x1B[0m'); print(' cd $appDir && flutter build apk'); @@ -201,6 +204,7 @@ void main() { backgroundColor: const Color($bgColor), textColor: const Color($textColor), mutedTextColor: const Color($mutedTextColor), + splashImage: const AssetImage('assets/branding/splash.png'), ), ); } @@ -337,3 +341,19 @@ flutter_native_splash: } print('\x1B[32m✔ 启动页已生成。\x1B[0m'); } + +Future _registerFlutterAssets(String appDir) async { + print('\x1B[34m[信息] 正在注册 Flutter assets...\x1B[0m'); + final File pubspecFile = File('$appDir/pubspec.yaml'); + String content = await pubspecFile.readAsString(); + + // 查找 flutter: 段并添加 assets 声明 + if (!content.contains('assets:')) { + content = content.replaceFirst( + 'flutter:\n', + 'flutter:\n assets:\n - assets/branding/\n', + ); + await pubspecFile.writeAsString(content); + } + print('\x1B[32m✔ Flutter assets 已注册。\x1B[0m'); +}