You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

额外注意事项

  1. 视频格式优先选择MP4,确保跨平台兼容
  2. 可根据需求调整视频容器的宽高或AspectRatio,适配启动页布局
  3. 测试时需确保视频资源路径正确,避免因资源缺失导致黑屏

内容的提问来源于stack exchange,提问作者Ahmed Sadek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 06:31:15