diff --git a/android/app/src/main/res/drawable-hdpi/android12splash.png b/android/app/src/main/res/drawable-hdpi/android12splash.png
new file mode 100644
index 0000000..b83ff04
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/android12splash.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/splash.png b/android/app/src/main/res/drawable-hdpi/splash.png
new file mode 100644
index 0000000..f7ad471
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/splash.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/android12splash.png b/android/app/src/main/res/drawable-mdpi/android12splash.png
new file mode 100644
index 0000000..fdcfb53
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/android12splash.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/splash.png b/android/app/src/main/res/drawable-mdpi/splash.png
new file mode 100644
index 0000000..b2ebfd8
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/splash.png differ
diff --git a/android/app/src/main/res/drawable-night-hdpi/android12splash.png b/android/app/src/main/res/drawable-night-hdpi/android12splash.png
new file mode 100644
index 0000000..b83ff04
Binary files /dev/null and b/android/app/src/main/res/drawable-night-hdpi/android12splash.png differ
diff --git a/android/app/src/main/res/drawable-night-mdpi/android12splash.png b/android/app/src/main/res/drawable-night-mdpi/android12splash.png
new file mode 100644
index 0000000..fdcfb53
Binary files /dev/null and b/android/app/src/main/res/drawable-night-mdpi/android12splash.png differ
diff --git a/android/app/src/main/res/drawable-night-xhdpi/android12splash.png b/android/app/src/main/res/drawable-night-xhdpi/android12splash.png
new file mode 100644
index 0000000..902356d
Binary files /dev/null and b/android/app/src/main/res/drawable-night-xhdpi/android12splash.png differ
diff --git a/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png b/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png
new file mode 100644
index 0000000..b111cdb
Binary files /dev/null and b/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png differ
diff --git a/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png b/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png
new file mode 100644
index 0000000..54c03f6
Binary files /dev/null and b/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png differ
diff --git a/android/app/src/main/res/drawable-v21/background.png b/android/app/src/main/res/drawable-v21/background.png
new file mode 100644
index 0000000..3f0a992
Binary files /dev/null and b/android/app/src/main/res/drawable-v21/background.png differ
diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml
index d933a92..3cc4948 100644
--- a/android/app/src/main/res/drawable-v21/launch_background.xml
+++ b/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -1,4 +1,9 @@
-
+ -
+
+
+ -
+
+
diff --git a/android/app/src/main/res/drawable-xhdpi/android12splash.png b/android/app/src/main/res/drawable-xhdpi/android12splash.png
new file mode 100644
index 0000000..902356d
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/android12splash.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/splash.png b/android/app/src/main/res/drawable-xhdpi/splash.png
new file mode 100644
index 0000000..b01e86c
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/splash.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/android12splash.png b/android/app/src/main/res/drawable-xxhdpi/android12splash.png
new file mode 100644
index 0000000..b111cdb
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/android12splash.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/splash.png b/android/app/src/main/res/drawable-xxhdpi/splash.png
new file mode 100644
index 0000000..5143af8
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/splash.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/android12splash.png b/android/app/src/main/res/drawable-xxxhdpi/android12splash.png
new file mode 100644
index 0000000..54c03f6
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/android12splash.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/splash.png b/android/app/src/main/res/drawable-xxxhdpi/splash.png
new file mode 100644
index 0000000..d255ff4
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/splash.png differ
diff --git a/android/app/src/main/res/drawable/background.png b/android/app/src/main/res/drawable/background.png
new file mode 100644
index 0000000..3f0a992
Binary files /dev/null and b/android/app/src/main/res/drawable/background.png differ
diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml
index d933a92..3cc4948 100644
--- a/android/app/src/main/res/drawable/launch_background.xml
+++ b/android/app/src/main/res/drawable/launch_background.xml
@@ -1,4 +1,9 @@
-
+ -
+
+
+ -
+
+
diff --git a/android/app/src/main/res/values-night-v31/styles.xml b/android/app/src/main/res/values-night-v31/styles.xml
index 8041da7..e4f4e80 100644
--- a/android/app/src/main/res/values-night-v31/styles.xml
+++ b/android/app/src/main/res/values-night-v31/styles.xml
@@ -5,6 +5,9 @@
+
diff --git a/android/app/src/main/res/values-v31/styles.xml b/android/app/src/main/res/values-v31/styles.xml
index 84e6f89..a7d85a0 100644
--- a/android/app/src/main/res/values-v31/styles.xml
+++ b/android/app/src/main/res/values-v31/styles.xml
@@ -5,6 +5,9 @@
+
diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml
index 527fd56..c6431cb 100644
--- a/android/app/src/main/res/values/colors.xml
+++ b/android/app/src/main/res/values/colors.xml
@@ -2,6 +2,6 @@
- #FFF8F0
+ #1F1B16
#FFF1E5
diff --git a/assets/icon/splash_logo.png b/assets/icon/splash_logo.png
new file mode 100644
index 0000000..d8dbc36
Binary files /dev/null and b/assets/icon/splash_logo.png differ
diff --git a/assets/icon/splash_logo_android12.png b/assets/icon/splash_logo_android12.png
new file mode 100644
index 0000000..c3a14e3
Binary files /dev/null and b/assets/icon/splash_logo_android12.png differ
diff --git a/assets/icon/splash_mark.png b/assets/icon/splash_mark.png
deleted file mode 100644
index 121cacd..0000000
Binary files a/assets/icon/splash_mark.png and /dev/null differ
diff --git a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json
new file mode 100644
index 0000000..9f447e1
--- /dev/null
+++ b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "background.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png
new file mode 100644
index 0000000..3f0a992
Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 0000000..00cabce
--- /dev/null
+++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "filename" : "LaunchImage.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "LaunchImage@2x.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "LaunchImage@3x.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 0000000..b2ebfd8
Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 0000000..b01e86c
Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 0000000..5143af8
Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ
diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard
index 4897787..5a37630 100644
--- a/ios/Runner/Base.lproj/LaunchScreen.storyboard
+++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -9,9 +9,27 @@
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -19,4 +37,8 @@
+
+
+
+
diff --git a/lib/core/theme/app_colors.dart b/lib/core/theme/app_colors.dart
index 46df55a..e8961a9 100644
--- a/lib/core/theme/app_colors.dart
+++ b/lib/core/theme/app_colors.dart
@@ -36,6 +36,9 @@ abstract final class AppColors {
/// 页面背景。
static const ink = Color(0xFFFFF8F0);
+ /// 原生开屏与冷启动过渡底色。
+ static const splashInk = Color(0xFF1F1B16);
+
/// 页面顶部暖色渐变起点。
static const surfaceTop = Color(0xFFFFE9D6);
diff --git a/lib/pages/home/home_view.dart b/lib/pages/home/home_view.dart
index 66908e1..3be91ba 100644
--- a/lib/pages/home/home_view.dart
+++ b/lib/pages/home/home_view.dart
@@ -13,6 +13,7 @@ import '../../core/data/services/catalog_controller.dart';
import '../../core/data/services/catalog_retry.dart';
import '../../core/data/services/category_controller.dart';
import '../../core/platform/haptics.dart';
+import '../../core/theme/app_colors.dart';
import '../../core/theme/app_theme.dart';
import '../../core/widgets/app_bottom_nav.dart';
import '../../core/widgets/catalog_debug_info.dart';
@@ -24,7 +25,6 @@ import '../discover/discover_view.dart';
import '../create/create_view.dart';
import '../mine/mine_types.dart';
import '../player/player_view.dart';
-import '../splash/splash_page.dart';
import '../mine/mine_view.dart';
import 'home_binding.dart';
import 'home_logic.dart';
@@ -87,7 +87,6 @@ class _HomePageState extends State with WidgetsBindingObserver {
@override
Widget build(BuildContext context) {
final catalog = context.watch();
- if (!catalog.settled) return const SplashPage();
final l10n = AppLocalizations.of(context)!;
return Theme(
data: AppTheme.light(),
@@ -101,9 +100,14 @@ class _HomePageState extends State with WidgetsBindingObserver {
AppLocalizations l10n,
) {
if (catalog.loading) {
- return const Scaffold(
- backgroundColor: Colors.transparent,
- body: Center(child: CircularProgressIndicator()),
+ return AnnotatedRegion(
+ value: SystemUiOverlayStyle.light,
+ child: const Scaffold(
+ backgroundColor: AppColors.splashInk,
+ body: Center(
+ child: CircularProgressIndicator(color: AppColors.blue),
+ ),
+ ),
);
}
if (catalog.error != null) {
diff --git a/lib/pages/splash/splash_page.dart b/lib/pages/splash/splash_page.dart
deleted file mode 100644
index 9064d41..0000000
--- a/lib/pages/splash/splash_page.dart
+++ /dev/null
@@ -1,212 +0,0 @@
-// Author: fruit.com.cn
-
-import 'dart:ui';
-
-import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
-
-import '../../core/theme/app_colors.dart';
-
-const Color _markGlow = Color(0x33FF7A1A);
-const double _markSize = 112;
-const double _markRadius = 24;
-const double _logoGlowSize = 144;
-const double _logoGlowBlur = 40;
-const double _loaderSize = 22;
-const double _loaderStroke = 2;
-const String _markAsset = 'assets/icon/splash_mark.png';
-const Duration _introDuration = Duration(milliseconds: 1200);
-
-/// 品牌开屏:浅暖底上依次呈现水果标记、FruitShorts 与中文标语。
-class SplashPage extends StatefulWidget {
- const SplashPage({super.key});
-
- @override
- State createState() => _SplashPageState();
-}
-
-class _SplashPageState extends State
- with SingleTickerProviderStateMixin {
- late final AnimationController _controller;
- late final Animation _logoScale;
- late final Animation _logoOpacity;
- late final Animation _brandReveal;
- late final Animation _taglineReveal;
- late final Animation _loadingReveal;
-
- @override
- void initState() {
- super.initState();
- _controller = AnimationController(vsync: this, duration: _introDuration)
- ..forward();
- _logoScale = Tween(begin: 0.86, end: 1).animate(
- CurvedAnimation(
- parent: _controller,
- curve: const Interval(0, 0.42, curve: Curves.easeOutBack),
- ),
- );
- _logoOpacity = Tween(begin: 0, end: 1).animate(
- CurvedAnimation(
- parent: _controller,
- curve: const Interval(0, 0.42, curve: Curves.easeOut),
- ),
- );
- _brandReveal = _reveal(0.125, 0.542);
- _taglineReveal = _reveal(0.25, 0.667);
- _loadingReveal = _reveal(0.417, 0.833);
- }
-
- Animation _reveal(double begin, double end) {
- return Tween(begin: 0, end: 1).animate(
- CurvedAnimation(
- parent: _controller,
- curve: Interval(begin, end, curve: Curves.easeOutCubic),
- ),
- );
- }
-
- @override
- void dispose() {
- _controller.dispose();
- super.dispose();
- }
-
- @override
- Widget build(BuildContext context) {
- return AnnotatedRegion(
- value: SystemUiOverlayStyle.dark,
- child: ColoredBox(
- key: const Key('splash-screen'),
- color: AppColors.ink,
- child: Stack(
- fit: StackFit.expand,
- children: [
- Center(
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- _logo(),
- const SizedBox(height: 24),
- _brand(),
- const SizedBox(height: 6),
- _tagline(),
- ],
- ),
- ),
- Align(
- alignment: Alignment.bottomCenter,
- child: Padding(
- padding: const EdgeInsets.only(bottom: 48),
- child: FadeTransition(
- key: const Key('splash-loading-fade'),
- opacity: _loadingReveal,
- child: const SizedBox.square(
- dimension: _loaderSize,
- child: CircularProgressIndicator(
- key: Key('splash-loading'),
- strokeWidth: _loaderStroke,
- color: AppColors.blue,
- ),
- ),
- ),
- ),
- ),
- ],
- ),
- ),
- );
- }
-
- Widget _logo() {
- return FadeTransition(
- key: const Key('splash-logo-fade'),
- opacity: _logoOpacity,
- child: ScaleTransition(
- scale: _logoScale,
- child: SizedBox.square(
- dimension: _logoGlowSize,
- child: Stack(
- alignment: Alignment.center,
- children: [
- ImageFiltered(
- key: const Key('splash-logo-glow'),
- imageFilter: ImageFilter.blur(
- sigmaX: _logoGlowBlur,
- sigmaY: _logoGlowBlur,
- ),
- child: Container(
- width: _markSize,
- height: _markSize,
- decoration: const BoxDecoration(
- shape: BoxShape.circle,
- gradient: RadialGradient(
- colors: [_markGlow, Color(0x00FF7A1A)],
- ),
- ),
- ),
- ),
- ClipRRect(
- borderRadius: BorderRadius.circular(_markRadius),
- child: Image.asset(
- _markAsset,
- key: const Key('splash-logo-mark'),
- width: _markSize,
- height: _markSize,
- filterQuality: FilterQuality.medium,
- gaplessPlayback: true,
- ),
- ),
- ],
- ),
- ),
- ),
- );
- }
-
- Widget _brand() {
- return FadeTransition(
- key: const Key('splash-brand-fade'),
- opacity: _brandReveal,
- child: AnimatedBuilder(
- animation: _brandReveal,
- builder: (context, child) => Transform.translate(
- offset: Offset(0, 12 * (1 - _brandReveal.value)),
- child: child,
- ),
- child: const Text(
- 'FruitShorts',
- key: Key('splash-brand'),
- style: TextStyle(
- color: AppColors.onSurface,
- fontSize: 28,
- fontWeight: FontWeight.w900,
- fontFamily: 'Outfit',
- ),
- ),
- ),
- );
- }
-
- Widget _tagline() {
- return FadeTransition(
- key: const Key('splash-tagline-fade'),
- opacity: _taglineReveal,
- child: AnimatedBuilder(
- animation: _taglineReveal,
- builder: (context, child) => Transform.translate(
- offset: Offset(0, 8 * (1 - _taglineReveal.value)),
- child: child,
- ),
- child: const Text(
- '水果短剧',
- key: Key('splash-tagline'),
- style: TextStyle(
- color: AppColors.muted,
- fontSize: 13,
- letterSpacing: 4,
- ),
- ),
- ),
- );
- }
-}
diff --git a/pubspec.yaml b/pubspec.yaml
index b64bbfe..902ace3 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -80,8 +80,6 @@ flutter:
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
- assets:
- - assets/icon/splash_mark.png
# To add assets to your application, add an assets section, like this:
# assets:
@@ -125,12 +123,11 @@ flutter_launcher_icons:
# 修改以下开屏配置后,必须重新执行:
# dart run flutter_native_splash:create
flutter_native_splash:
- color: "#FFF8F0"
- color_dark: "#FFF8F0"
+ color: "#1F1B16"
+ image: assets/icon/splash_logo.png
android: true
ios: true
web: false
android_12:
- color: "#FFF8F0"
- color_dark: "#FFF8F0"
- icon_background_color: "#FFF8F0"
+ color: "#1F1B16"
+ image: assets/icon/splash_logo_android12.png
diff --git a/test/splash/splash_gate_test.dart b/test/splash/splash_gate_test.dart
deleted file mode 100644
index f25245a..0000000
--- a/test/splash/splash_gate_test.dart
+++ /dev/null
@@ -1,114 +0,0 @@
-// Author: fruit.com.cn
-
-import 'dart:async';
-
-import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:fruitshorts/core/data/models/drama.dart';
-import 'package:fruitshorts/core/data/models/home_feed.dart';
-import 'package:fruitshorts/core/data/repositories/catalog_repository.dart';
-import 'package:fruitshorts/core/data/services/catalog_controller.dart';
-import 'package:fruitshorts/core/theme/app_colors.dart';
-import 'package:fruitshorts/l10n/app_localizations.dart';
-import 'package:fruitshorts/pages/home/home_view.dart';
-import 'package:fruitshorts/pages/splash/splash_page.dart';
-import 'package:provider/provider.dart';
-
-void main() {
- testWidgets('should_keep_splash_with_loading_until_home_feed_settles', (
- tester,
- ) async {
- final repository = _PendingCatalog();
- await tester.pumpWidget(
- MaterialApp(
- localizationsDelegates: AppLocalizations.localizationsDelegates,
- supportedLocales: AppLocalizations.supportedLocales,
- home: ChangeNotifierProvider(
- create: (_) => CatalogController(repository)..load(),
- child: const HomePage(),
- ),
- ),
- );
-
- expect(find.byKey(const Key('splash-screen')), findsOneWidget);
- expect(find.byKey(const Key('splash-loading')), findsOneWidget);
- expect(find.byKey(const Key('home-empty')), findsNothing);
- final clip = tester.widget(find.byType(ClipRRect));
- expect(clip.borderRadius, BorderRadius.circular(24));
-
- repository.pending.complete(const HomeFeed());
- await tester.pump();
- await tester.pump();
-
- expect(find.byKey(const Key('splash-screen')), findsNothing);
- expect(find.byKey(const Key('home-empty')), findsOneWidget);
- });
-
- testWidgets('should_animate_brand_copy_and_loading_to_full_opacity', (
- tester,
- ) async {
- await tester.pumpWidget(const MaterialApp(home: SplashPage()));
-
- expect(find.byKey(const Key('splash-screen')), findsOneWidget);
- expect(find.byKey(const Key('splash-brand')), findsOneWidget);
- expect(find.byKey(const Key('splash-tagline')), findsOneWidget);
- expect(find.byKey(const Key('splash-loading')), findsOneWidget);
- final mark = tester.widget(
- find.byKey(const Key('splash-logo-mark')),
- );
- expect((mark.image as AssetImage).assetName, 'assets/icon/splash_mark.png');
- expect(mark.width, 112);
- expect(mark.height, 112);
- final glow = tester.widget(
- find.descendant(
- of: find.byKey(const Key('splash-logo-glow')),
- matching: find.byType(Container),
- ),
- );
- final glowGradient = (glow.decoration as BoxDecoration).gradient;
- expect(
- (glowGradient as RadialGradient).colors.first,
- const Color(0x33FF7A1A),
- );
- expect(
- tester.widget(find.byKey(const Key('splash-screen'))).color,
- AppColors.ink,
- );
- expect(
- tester.widget(find.byKey(const Key('splash-brand'))).style?.color,
- AppColors.onSurface,
- );
- expect(
- tester.widget(find.byKey(const Key('splash-tagline'))).style?.color,
- AppColors.muted,
- );
- final overlay = tester.widget>(
- find.byType(AnnotatedRegion).first,
- );
- expect(overlay.value.statusBarIconBrightness, Brightness.dark);
-
- await tester.pump(const Duration(milliseconds: 1200));
-
- double opacity(String key) =>
- tester.widget(find.byKey(Key(key))).opacity.value;
- expect(opacity('splash-logo-fade'), 1);
- expect(opacity('splash-brand-fade'), 1);
- expect(opacity('splash-tagline-fade'), 1);
- expect(opacity('splash-loading-fade'), 1);
- });
-}
-
-class _PendingCatalog implements CatalogRepository {
- final Completer pending = Completer();
-
- @override
- Future fetchHomeFeed() => pending.future;
-
- @override
- Future fetchDiscoverFeed() => pending.future;
-
- @override
- Future fetchDrama(String dramaId) =>
- Future.error(StateError('unused'));
-}