Flutter技术实现:将启动页静态图片替换为动画视频
问题描述
我当前使用的Flutter启动页代码如下,想要把其中的Logo图片替换成动画视频。之前修改代码后出现黑屏问题,求帮忙修改代码实现视频启动页。
原代码:
import 'package:cirilla/constants/assets.dart'; import 'package:cirilla/constants/constants.dart'; import 'package:flutter/material.dart'; import 'package:ui/painter/zoom_painter.dart'; import 'widgets/zoom_animation.dart'; class SplashScreen extends StatefulWidget { const SplashScreen({Key? key, this.color, this.loading}) : super(key: key); final Color? color; final bool? loading; @override State<SplashScreen> createState() => _SplashScreenState(); } class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderStateMixin { Size size = Size.zero; late AnimationController _controller; late ZoomAnimation _animation; AnimationStatus _status = AnimationStatus.forward; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(milliseconds: 2500), vsync: this, ) ..addStatusListener((AnimationStatus status) { setState(() { _status = status; }); }) ..addListener(() { setState(() {}); }); _animation = ZoomAnimation(_controller); } @override void didChangeDependencies() { setState(() { size = MediaQuery.of(context).size; }); super.didChangeDependencies(); } @override void didUpdateWidget(covariant SplashScreen oldWidget) { super.didUpdateWidget(oldWidget); if (!widget.loading! && _controller.status != AnimationStatus.forward) { _controller.forward(); } } @override Widget build(BuildContext context) { if (_status == AnimationStatus.completed) return Container(); return Stack(children: [ SizedBox( width: double.infinity, height: double.infinity, child: CustomPaint( painter: ZoomPainter(color: widget.color!, zoomSize: _animation.zoomSize.value * size.width), ), ), Padding( padding: const EdgeInsets.only(bottom: itemPaddingExtraLarge), child: Align( alignment: Alignment.center, child: Opacity( opacity: _animation.textOpacity.value, child: Image.asset(Assets.logo, width: 200, height: 200, fit: BoxFit.fitWidth), ), ), ) ]); } @override void dispose() { super.dispose(); _controller.dispose(); } }
解决方案
要实现视频启动页并解决黑屏问题,我们需要引入video_player包处理视频播放,同时结合原有动画逻辑。以下是修改后的完整代码:
第一步:添加依赖
在pubspec.yaml中添加视频播放依赖:
dependencies: video_player: ^2.8.1 # 使用最新稳定版本
第二步:修改启动页代码
import 'package:cirilla/constants/assets.dart'; import 'package:cirilla/constants/constants.dart'; import 'package:flutter/material.dart'; import 'package:ui/painter/zoom_painter.dart'; import 'package:video_player/video_player.dart'; // 新增视频播放包导入 import 'widgets/zoom_animation.dart'; class SplashScreen extends StatefulWidget { const SplashScreen({Key? key, this.color, this.loading}) : super(key: key); final Color? color; final bool? loading; @override State<SplashScreen> createState() => _SplashScreenState(); } class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderStateMixin { Size size = Size.zero; late AnimationController _controller; late ZoomAnimation _animation; AnimationStatus _status = AnimationStatus.forward; late VideoPlayerController _videoController; // 视频控制器 bool _videoReady = false; // 标记视频是否初始化完成 @override void initState() { super.initState(); // 初始化视频控制器 _videoController = VideoPlayerController.asset(Assets.splashVideo) // 替换为你的视频资源路径 ..initialize().then((_) { setState(() { _videoReady = true; _videoController.play(); // 监听视频播放完成,同步触发缩放动画 _videoController.addListener(() { if (_videoController.value.position >= _videoController.value.duration) { if (_controller.status != AnimationStatus.completed) { _controller.forward(); } } }); }); }); _controller = AnimationController( duration: const Duration(milliseconds: 2500), vsync: this, ) ..addStatusListener((AnimationStatus status) { setState(() { _status = status; // 动画完成后释放视频资源 if (status == AnimationStatus.completed) { _videoController.dispose(); } }); }) ..addListener(() { setState(() {}); }); _animation = ZoomAnimation(_controller); } @override void didChangeDependencies() { setState(() { size = MediaQuery.of(context).size; }); super.didChangeDependencies(); } @override void didUpdateWidget(covariant SplashScreen oldWidget) { super.didUpdateWidget(oldWidget); if (!widget.loading! && _controller.status != AnimationStatus.forward) { _controller.forward(); } } @override Widget build(BuildContext context) { if (_status == AnimationStatus.completed) return Container(); return Stack(children: [ SizedBox( width: double.infinity, height: double.infinity, child: CustomPaint( painter: ZoomPainter(color: widget.color!, zoomSize: _animation.zoomSize.value * size.width), ), ), Padding( padding: const EdgeInsets.only(bottom: itemPaddingExtraLarge), child: Align( alignment: Alignment.center, child: Opacity( opacity: _animation.textOpacity.value, child: _videoReady ? AspectRatio( aspectRatio: _videoController.value.aspectRatio, child: VideoPlayer(_videoController), ) : Container( width: 200, height: 200, color: Colors.black12, // 视频加载时的占位,避免黑屏 ), ), ), ) ]); } @override void dispose() { _videoController.dispose(); // 释放视频控制器 _controller.dispose(); super.dispose(); } }
关键修改说明
- 视频初始化处理:通过
_videoReady标记视频是否加载完成,避免未初始化时显示黑屏 - 占位容器:视频加载过程中显示灰色占位,解决启动瞬间黑屏问题
- 资源同步:监听视频播放完成事件,触发原有缩放动画;动画结束后释放视频资源,避免内存泄漏
- 路径配置:确保
Assets.splashVideo指向正确的本地视频路径,需在pubspec.yaml中声明资源:
assets: - assets/videos/your_splash_video.mp4
额外注意事项
- 视频格式优先选择MP4,确保跨平台兼容
- 可根据需求调整视频容器的宽高或AspectRatio,适配启动页布局
- 测试时需确保视频资源路径正确,避免因资源缺失导致黑屏
内容的提问来源于stack exchange,提问作者Ahmed Sadek
相关产品推荐
相关产品推荐

