merge: 调整品牌开屏与纯黑原生启动

This commit is contained in:
Zhou Weidong committed 2026-10-22 23:15:36 +08:00
commit b513ac0a9c
27 files changed
+88 -14

No files matched your search

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

@@ -3,7 +3,4 @@
<item>
<bitmap android:gravity="fill" android:src="@drawable/background"/>
</item>
<item>
<bitmap android:gravity="center" android:src="@drawable/splash"/>
</item>
</layer-list>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

@@ -3,7 +3,4 @@
<item>
<bitmap android:gravity="fill" android:src="@drawable/background"/>
</item>
<item>
<bitmap android:gravity="center" android:src="@drawable/splash"/>
</item>
</layer-list>
@@ -8,7 +8,6 @@
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenBackground">#000000</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
</style>
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@color/splash_background</item>
@@ -8,7 +8,6 @@
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenBackground">#000000</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
</style>
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">@color/splash_background</item>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 69 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 69 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 69 B

@@ -38,7 +38,7 @@
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="1400" height="1400"/>
<image name="LaunchImage" width="168" height="185"/>
<image name="LaunchBackground" width="1" height="1"/>
</resources>
</document>
+83 -1
View File
@@ -30,6 +30,9 @@ import 'home_logic.dart';
import 'widgets/home_feed_grid.dart';
import 'widgets/home_grid_header.dart';
const Duration _startupSplashMinimumDuration = Duration(milliseconds: 400);
const Duration _startupSplashFadeDuration = Duration(milliseconds: 200);
class HomePage extends StatefulWidget {
const HomePage({super.key});
@@ -41,15 +44,31 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
bool _resumeRetryInFlight = false;
HomeFeedController? _homeFeed;
bool _recommendSeeded = false;
Timer? _startupSplashMinimumTimer;
bool _catalogSettled = false;
bool _startupSplashVisible = true;
bool _startupSplashFadingOut = false;
bool _startupSplashMinimumElapsed = false;
bool _startupSplashDismissing = false;
bool _startupSplashSettledTriggered = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return;
_startupSplashMinimumTimer = Timer(_startupSplashMinimumDuration, () {
if (!mounted) return;
_startupSplashMinimumElapsed = true;
unawaited(_dismissStartupSplashIfReady());
});
});
}
@override
void dispose() {
_startupSplashMinimumTimer?.cancel();
_homeFeed?.dispose();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
@@ -83,7 +102,41 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
@override
Widget build(BuildContext context) {
final catalog = context.watch<CatalogController>();
return Theme(data: AppTheme.dark(), child: _buildHome(context, catalog));
_catalogSettled = catalog.settled;
if (_startupSplashVisible &&
_catalogSettled &&
!_startupSplashSettledTriggered) {
_startupSplashSettledTriggered = true;
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) unawaited(_dismissStartupSplashIfReady());
});
}
return Theme(
data: AppTheme.dark(),
child: Stack(
fit: StackFit.expand,
children: [
_buildHome(context, catalog),
if (_startupSplashVisible)
_StartupSplashOverlay(fadingOut: _startupSplashFadingOut),
],
),
);
}
Future<void> _dismissStartupSplashIfReady() async {
if (!mounted ||
!_startupSplashVisible ||
_startupSplashDismissing ||
!_startupSplashMinimumElapsed ||
!_catalogSettled) {
return;
}
_startupSplashDismissing = true;
setState(() => _startupSplashFadingOut = true);
await Future<void>.delayed(_startupSplashFadeDuration);
if (!mounted) return;
setState(() => _startupSplashVisible = false);
}
Widget _buildHome(BuildContext context, CatalogController catalog) {
@@ -109,6 +162,35 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
}
}
class _StartupSplashOverlay extends StatelessWidget {
const _StartupSplashOverlay({required this.fadingOut});
final bool fadingOut;
@override
Widget build(BuildContext context) {
return IgnorePointer(
child: AnimatedOpacity(
key: const Key('startup-splash'),
opacity: fadingOut ? 0 : 1,
duration: _startupSplashFadeDuration,
curve: Curves.easeOut,
child: ColoredBox(
color: AppColors.appBackground,
child: Center(
child: Image.asset(
'assets/icon/splash_logo.png',
key: const Key('startup-splash-logo'),
width: 240,
fit: BoxFit.contain,
),
),
),
),
);
}
}
/// 四个根页面共享同一路由并保持各自状态。
class _HomeTabs extends StatefulWidget {
const _HomeTabs({super.key, required this.feedController});
-2
View File
@@ -129,10 +129,8 @@ flutter_launcher_icons:
# dart run flutter_native_splash:create
flutter_native_splash:
color: "#000000"
image: assets/icon/splash_logo.png
android: true
ios: true
web: false
android_12:
color: "#000000"
image: assets/icon/splash_logo_android12.png
+4 -2
View File
@@ -36,7 +36,7 @@ void main() {
AppConfig.ready = Future<void>.value();
});
testWidgets('should_show_home_shell_and_grid_skeleton_while_catalog_loads', (
testWidgets('should_show_brand_splash_and_grid_skeleton_while_loading', (
tester,
) async {
final repository = _PendingCatalogRepository();
@@ -47,12 +47,14 @@ void main() {
expect(find.byKey(const Key('home-grid-header')), findsOneWidget);
expect(find.byKey(const Key('home-grid-skeleton')), findsOneWidget);
expect(find.byKey(const Key('home-tabs-bar')), findsOneWidget);
expect(find.byKey(const Key('startup-splash')), findsNothing);
expect(find.byKey(const Key('startup-splash')), findsOneWidget);
expect(find.byKey(const Key('startup-splash-logo')), findsOneWidget);
expect(find.byType(CircularProgressIndicator), findsNothing);
repository.complete(_feed);
await tester.pumpAndSettle();
expect(find.byKey(const Key('startup-splash')), findsNothing);
expect(find.byKey(const Key('home-grid-skeleton')), findsNothing);
expect(find.byKey(const Key('home-grid')), findsOneWidget);
});