启动动画和登录页面的动画
This commit is contained in:
114
lib/features/auth/presentation/pages/splash_page.dart
Normal file
114
lib/features/auth/presentation/pages/splash_page.dart
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
import '../../../../core/router/route_paths.dart';
|
||||||
|
import '../bloc/auth_cubit.dart';
|
||||||
|
import '../bloc/auth_state.dart';
|
||||||
|
|
||||||
|
/// 启动页:展示 App Logo 并循环播放「呼吸缩放」动画。
|
||||||
|
///
|
||||||
|
/// 该页面作为 App 的第一个路由,衔接原生启动屏(静态 Logo)。
|
||||||
|
/// 动画会一直循环,直到 [AuthCubit.appStarted] 完成、登录态确定
|
||||||
|
/// ([AuthAuthenticated] / [AuthUnauthenticated])这一「就绪信号」到来,
|
||||||
|
/// 才停止动画并跳转到首页或登录页。
|
||||||
|
class SplashPage extends StatefulWidget {
|
||||||
|
const SplashPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SplashPage> createState() => _SplashPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SplashPageState extends State<SplashPage>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
/// 呼吸动画控制器(循环往复)
|
||||||
|
late final AnimationController _breathController;
|
||||||
|
|
||||||
|
/// 就绪信号是否已到达
|
||||||
|
bool _ready = false;
|
||||||
|
|
||||||
|
/// 就绪后的目标登录态
|
||||||
|
AuthState? _resolvedState;
|
||||||
|
|
||||||
|
/// 防止重复跳转
|
||||||
|
bool _navigated = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_breathController = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: const Duration(milliseconds: 900),
|
||||||
|
)..repeat(reverse: true);
|
||||||
|
|
||||||
|
// 处理进入 splash 时登录态已经确定的情况(appStarted 先于首帧完成)
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
if (mounted) _onReady(context.read<AuthCubit>().state);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_breathController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 记录就绪信号,并尝试跳转
|
||||||
|
void _onReady(AuthState state) {
|
||||||
|
if (state is AuthAuthenticated || state is AuthUnauthenticated) {
|
||||||
|
_resolvedState = state;
|
||||||
|
_ready = true;
|
||||||
|
_maybeNavigate();
|
||||||
|
}
|
||||||
|
// AuthInitial:尚未就绪,继续停留在启动页播放动画
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 就绪信号到达即离开启动页,不额外增加打开时间
|
||||||
|
void _maybeNavigate() {
|
||||||
|
if (_navigated || !mounted) return;
|
||||||
|
if (!_ready || _resolvedState == null) return;
|
||||||
|
|
||||||
|
_navigated = true;
|
||||||
|
_breathController.stop();
|
||||||
|
if (_resolvedState is AuthAuthenticated) {
|
||||||
|
context.go(RoutePaths.home);
|
||||||
|
} else {
|
||||||
|
context.go(RoutePaths.login);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocListener<AuthCubit, AuthState>(
|
||||||
|
listenWhen: (prev, cur) =>
|
||||||
|
cur is AuthAuthenticated || cur is AuthUnauthenticated,
|
||||||
|
listener: (context, state) => _onReady(state),
|
||||||
|
child: Scaffold(
|
||||||
|
// 与原生启动屏白底保持一致,避免切换时闪色
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
body: Center(
|
||||||
|
child: AnimatedBuilder(
|
||||||
|
animation: _breathController,
|
||||||
|
builder: (context, child) {
|
||||||
|
// easeInOut 让呼吸更柔和;scale 从 1.0 起始以贴合原生屏尺寸
|
||||||
|
final t = Curves.easeInOut.transform(_breathController.value);
|
||||||
|
final scale = 1.0 - 0.22 * t; // 1.0 -> 0.78,幅度更大更明显
|
||||||
|
final opacity = 1.0 - 0.55 * t; // 1.0 -> 0.45
|
||||||
|
return Opacity(
|
||||||
|
opacity: opacity,
|
||||||
|
child: Transform.scale(scale: scale, child: child),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
// 透明底应用图标,避免白底上出现浅灰"阴影"底块
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/images/app_logo_gray_transparent.png',
|
||||||
|
width: 180,
|
||||||
|
height: 180,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user