feat(mobile-app): 开屏页改为随机静态图片模式

- 禁用帧动画,改为显示随机静态图片(3张中随机选1张)
- 显示3秒后自动跳转,保留跳过按钮
- 帧动画代码保留备用,可通过 _useStaticImage 开关切换
- 新增 splash_static 目录存放静态图片

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-09 08:19:25 -08:00
parent fabfbb73fe
commit 414fe95d04
5 changed files with 82 additions and 15 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

View File

@ -1,3 +1,4 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
@ -10,7 +11,7 @@ import '../../../../routes/app_router.dart';
import '../providers/auth_provider.dart';
/// -
///
/// 3
class SplashPage extends ConsumerStatefulWidget {
const SplashPage({super.key});
@ -19,6 +20,22 @@ class SplashPage extends ConsumerStatefulWidget {
}
class _SplashPageState extends ConsumerState<SplashPage> {
// ========== ==========
/// 使false = 使
static const bool _useStaticImage = true;
///
static const int _staticDisplaySeconds = 3;
///
static const int _staticImageCount = 3;
/// (1-based)
late int _staticImageIndex;
// ========== ==========
///
static const int _totalFrames = 36;
@ -28,25 +45,55 @@ class _SplashPageState extends ConsumerState<SplashPage> {
/// (0-based)
int _currentFrameIndex = 0;
///
final List<ImageProvider> _frameProviders = [];
// ========== ==========
///
bool _showSkipButton = false;
///
bool _isNavigating = false;
///
/// /
bool _isPlaying = true;
///
final List<ImageProvider> _frameProviders = [];
@override
void initState() {
super.initState();
_initializeFrames();
if (_useStaticImage) {
_initializeStaticImage();
} else {
_initializeFrames();
}
}
///
///
void _initializeStaticImage() {
// (1 _staticImageCount)
_staticImageIndex = Random().nextInt(_staticImageCount) + 1;
debugPrint('[SplashPage] 静态图片模式:显示 splash_$_staticImageIndex.jpg');
// 3
Future.delayed(Duration(seconds: _staticDisplaySeconds), () {
if (mounted && _isPlaying) {
_navigateToNextPage();
}
});
// 1
Future.delayed(const Duration(seconds: 1), () {
if (mounted) {
setState(() {
_showSkipButton = true;
});
}
});
}
///
void _initializeFrames() {
// ImageProvider
for (int i = 1; i <= _totalFrames; i++) {
@ -71,11 +118,20 @@ class _SplashPageState extends ConsumerState<SplashPage> {
@override
void didChangeDependencies() {
super.didChangeDependencies();
//
_precacheInitialFrames();
if (_useStaticImage) {
//
precacheImage(
AssetImage('assets/images/splash_static/splash_$_staticImageIndex.jpg'),
context,
);
} else {
//
_precacheInitialFrames();
}
}
///
///
void _precacheInitialFrames() {
// 5
final framesToPrecache = _totalFrames < 5 ? _totalFrames : 5;
@ -84,7 +140,7 @@ class _SplashPageState extends ConsumerState<SplashPage> {
}
}
///
///
Future<void> _playAnimation() async {
final frameDuration = Duration(milliseconds: 1000 ~/ _frameRate);
@ -110,7 +166,7 @@ class _SplashPageState extends ConsumerState<SplashPage> {
}
}
///
/// /
void _skipAnimation() {
_isPlaying = false;
_navigateToNextPage();
@ -222,8 +278,8 @@ class _SplashPageState extends ConsumerState<SplashPage> {
body: Stack(
fit: StackFit.expand,
children: [
//
_buildFrameAnimation(),
// /
_useStaticImage ? _buildStaticImage() : _buildFrameAnimation(),
//
if (_showSkipButton)
@ -237,7 +293,17 @@ class _SplashPageState extends ConsumerState<SplashPage> {
);
}
///
///
Widget _buildStaticImage() {
return SizedBox.expand(
child: Image.asset(
'assets/images/splash_static/splash_$_staticImageIndex.jpg',
fit: BoxFit.cover,
),
);
}
///
Widget _buildFrameAnimation() {
return SizedBox.expand(
child: Image(

View File

@ -109,6 +109,7 @@ flutter:
- assets/images/avatars/
- assets/images/illustrations/
- assets/images/splash_frames/
- assets/images/splash_static/
- assets/icons/
- assets/icons/nav/
- assets/icons/tokens/