ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Flame 游戏引擎启动画面定制实战:flame_splash_screen 组件使用与源码解析

Flame 游戏引擎启动画面定制实战:flame_splash_screen 组件使用与源码解析 Flame 游戏引擎启动画面定制实战flame_splash_screen 组件使用与源码解析【免费下载链接】flameA Flutter based game engine.项目地址: https://gitcode.com/GitHub_Trending/fl/flameflame_splash_screen是 Flame 官方生态中用于为游戏添加启动画面Splash Screen动画的专用包它内置了火焰 Logo 的三层粒子级淡入淡出动画并支持通过主题、自定义内容与控制器对动画进行深度定制。读完本文你将掌握该组件的安装引入、基础接入、动画播放机制、showBefore/showAfter扩展、主题定制以及基于FlameSplashController的时长与启动时机控制并能结合源码理解其内部工作原理。本文以仓库中的 关联文档 为核心骨架结合 flame_splash_screen 包源码 及其测试与示例展开。一、包定位与核心思路Flame 是一个基于 Flutter 的 2D 游戏引擎而flame_splash_screen为其解决了游戏启动阶段展示品牌动画这一常见需求。它不是一个普通的静态图片页而是一个可编排的多步骤动画播放器整个播放过程被拆分为若干step步骤每个步骤是一个 Widget每个步骤内部按淡入 → 停留 → 淡出三段节奏播放全部步骤播完后触发onFinish回调由你决定进入游戏首页还是其他路由。从 源码 可以看到FlameSplashScreen是一个StatefulWidget其对外可配置的参数全部集中在构造器中参数类型说明onFinishValueChangedBuildContext必填全部动画结束后回调通常在此跳转到游戏初始页面themeFlameSplashTheme必填控制背景、Logo 及外层约束内置dark/white两套showBeforeWidgetBuilder?可选在 Flame Logo 之前额外展示的自定义 Widget如自家工作室 LogoshowAfterWidgetBuilder?可选在 Flame Logo 之后额外展示的自定义 WidgetcontrollerFlameSplashController?可选外部传入控制器以接管时长与启动时机二、安装与引入将flame_splash_screen声明为依赖即可。当前仓库内该包的定义位于 packages/flame_splash_screen/pubspec.yamlname: flame_splash_screen version: 0.3.13 description: Style your flame game with a beautiful splash screen with logo reveal. Simple to use but still customizable. environment: sdk: 3.12.0 4.0.0 flutter: 3.44.0 dependencies: flutter: sdk: flutter meta: ^1.12.0在你的游戏项目的pubspec.yaml中加入依赖后执行flutter pub get然后在 Dart 文件中引入import package:flame_splash_screen/flame_splash_screen.dart;该包运行时只依赖 Flutter SDK 与meta不引入任何重型第三方依赖适合作为游戏冷启动阶段的首屏组件。三、最小可用接入FlameSplashScreen的必填参数只有两个theme与onFinish。最简单的接入方式FlameSplashScreen( theme: FlameSplashTheme.dark, onFinish: (BuildContext context) Navigator.pushNamed(context, /your-game-initial-screen), )将这段代码放在你的路由页面例如SplashScreen的build中即可动画播放期间展示火焰 Logo播放完毕后通过onFinish导航到游戏主界面。动画自动播放的前提组件挂载后会自动开始动画因为内部FlameSplashScreenState.initState中默认创建了FlameSplashController()而该控制器默认autoStart true详见 controller.dart。同时initState会完成步骤编排并将onFinish注入控制器controller.setup(steps.length, () widget.onFinish(context));因此你不需要也不应该手动调用start()除非你主动设置了autoStart: false下文控制器进阶会展开。四、动画播放机制与步骤编排1. 步骤列表的构建在 splash.dart 的_computeSteps中步骤列表按如下顺序组装steps [ if (widget.showBefore ! null) widget.showBefore!, widget.theme.logoBuilder, if (widget.showAfter ! null) widget.showAfter!, ];即自定义前导内容 → 火焰 Logo → 自定义尾部内容中间的 Logo 步骤恒存在。步骤数量决定控制器播放几轮随后_tickStep会按索引逐轮推进见 controller.dart 的_tickStep实现Futurevoid _tickStep(int index) async { stepController.value index; await Futurevoid.delayed(durations.total); final finished index _stepsAmount - 1; if (finished) { _state FlameSplashControllerState.finished; _onFinish(); return; } _tickStep(index 1); }stepController是一个ValueNotifierint负责把当前播放到第几步广播给 UIFlameSplashScreenState.build中通过ValueListenableBuilder监听它并切换当前步骤的 Widget。2. 单步动画的三段式节奏每个步骤对应一个_SplashScreenStep其内部使用AnimationControllerOpacity实现淡入 → 停留 → 淡出// 淡入 controller ..value 0.0 ..duration widget.durations.fadeInDuration; await controller.forward(); await Futurevoid.delayed(widget.durations.waitDuration); // 淡出 controller ..value 1.0 ..duration widget.durations.fadeOutDuration ..reverse();三段时长分别由fadeInDuration、waitDuration、fadeOutDuration控制其默认值定义在FlameSplashController构造器中参数默认值作用fadeInDuration750msLogo 从透明淡入到完全可见waitDuration2s完全可见后的停留时间fadeOutDuration450ms淡出到透明autoStarttrue组件挂载后是否自动开始播放FlameSplashDurations还提供了total便捷属性fadeIn fadeOut wait用于控制器计算单步总时长。五、扩展你的内容showBefore 与 showAfter很多游戏希望启动时先展示自家品牌再展示 Flame 徽标或反之。showBefore/showAfter正是为此设计二者类型均为WidgetBuilder可以返回任意 Widget文本、图片、自绘组件均可。在 Flame Logo 之前展示内容FlameSplashScreen( theme: FlameSplashTheme.dark, showBefore: (BuildContext context) { return Text(To be shown before flame animation); }, onFinish: (BuildContext context) Navigator.pushNamed(context, /your-game-initial-screen), )在 Flame Logo 之后展示内容FlameSplashScreen( theme: FlameSplashTheme.dark, showAfter: (BuildContext context) { return Text(To be shown after flame animation); }, onFinish: (BuildContext context) Navigator.pushNamed(context, /your-game-initial-screen), )两者也可以同时指定此时动画顺序为showBefore内容 → 火焰 Logo →showAfter内容。值得注意的是源码中当showBefore/showAfter/theme.logoBuilder发生变化时didUpdateWidget会在动画未开始时重新计算步骤并重启动画因此这些内容理论上可以热更新。六、主题系统从内置两套到完全自定义1. 内置主题FlameSplashTheme提供两个开箱即用的静态主题见 theme.dart// 白底主题适合浅色应用 static FlameSplashTheme white const FlameSplashTheme( backgroundDecoration: BoxDecoration(color: Color(0xFFFFFFFF)), logoBuilder: _logoBuilder, ); // 黑底主题适合深色应用 static FlameSplashTheme dark const FlameSplashTheme( backgroundDecoration: BoxDecoration(color: Color(0xFF000000)), logoBuilder: _logoBuilder, );两者仅背景色不同Logo 相同。切换主题只需替换theme参数FlameSplashScreen( theme: FlameSplashTheme.white, onFinish: (BuildContext context) Navigator.pushNamed(context, /your-game-initial-screen), )2. 火焰 Logo 的分层渲染内置 Logo 并非单张图片而是三层 PNG 的叠加layer1.png、layer2.png、layer3.png位于包的 assets 目录。AnimatedLogo通过Stack叠加三层其中中间层由Animationdouble控制透明度从而产生火焰呼吸般的脉动效果Stack( fit: StackFit.expand, alignment: Alignment.center, children: [ Image.asset(assets/layer1.png, package: flame_splash_screen), Opacity( opacity: animation.value, child: Image.asset(assets/layer2.png, package: flame_splash_screen), ), Image.asset(assets/layer3.png, package: flame_splash_screen), ], )LogoComposite负责驱动这段 500ms 的正反向循环动画动画完成后反向播放回到起点再正向播放形成无限脉动Logo 整体被限制在 300×300 的宽松约束内并向上偏移 25%配合背景形成居中展示效果。3. 自定义主题FlameSplashTheme的构造器对外开放了三个字段可以完全替换默认外观const FlameSplashTheme({ required this.backgroundDecoration, required this.logoBuilder, this.constraints const BoxConstraints.expand(), });backgroundDecorationBoxDecoration控制 Logo 底层的背景颜色、渐变、图片等logoBuilderWidgetBuilder替换默认的三层火焰 Logo例如换成自家游戏 Logoconstraints外层BoxConstraints默认BoxConstraints.expand()占满可用空间。例如将默认火焰 Logo 换成自定义 Logo 并配以渐变背景FlameSplashScreen( theme: FlameSplashTheme( backgroundDecoration: const BoxDecoration( gradient: LinearGradient( colors: [Color(0xFF1A237E), Color(0xFF0D47A1)], ), ), logoBuilder: (context) Image.asset(assets/my_logo.png), ), onFinish: (context) Navigator.pushNamed(context, /game), )七、控制器进阶时长与启动时机的完全掌控当默认的 750ms 淡入 2s 停留 450ms 淡出节奏不满足需求或希望等资源加载完再播动画时可以传入外部FlameSplashController。1. 创建并传入控制器官方示例 example/lib/main.dart 之外README 给出了标准用法控制器与State同生命周期并在dispose中释放class SplashScreenGameState extends StateSplashScreenGame { FlameSplashController controller; override void initState() { super.initState(); controller FlameSplashController( fadeInDuration: Duration(seconds: 1), fadeOutDuration: Duration(milliseconds: 250), waitDuration: Duration(seconds: 2), autoStart: false, ); } override void dispose() { controller.dispose(); // dispose it when necessary super.dispose(); } override Widget build(BuildContext context) { return Scaffold( body: FlameSplashScreen( showBefore: (context) Text(Before the logo), showAfter: (context) Text(After the logo), theme: FlameSplashTheme.white, onFinish: (context) Navigator.pushNamed(context, /the-game-initial-screen), controller: controller, ), ); } }2. 手动触发 start当autoStart: false时动画不会在组件挂载后自动播放需要在你认为合适的时机例如异步资源加载完毕手动调用// 资源加载完成后 await loadGameAssets(); controller.start();源码在start()中设置了断言保护控制器必须先被FlameSplashScreen挂载setup之后才能启动且已启动的控制器不允许重复start()否则会抛出断言错误。3. 状态机与生命周期控制器内部维护了三个状态FlameSplashControllerState状态含义idle已就绪但尚未开始autoStart: false时处于此状态started正在依次播放各个步骤finished所有步骤播放完毕onFinish已触发从 controller_test.dart 可以看出几个关键行为约束未setup前调用start()抛断言异常setup后若autoStart为 false状态保持idle调用start()后变为started播放完所有步骤后状态为finished且onFinish被调用autoStart: true时setup即自动start随后再调start()会抛断言每个 step 结束后还会继续推进下一 step直到最后一个。dispose()用于释放控制器内部资源。当控制器由外部传入时组件不会替它调用dispose见splash.dart中_externallyControlled分支所以外部传入的控制器必须由你负责释放。八、完整可运行的接入示例综合以上内容一个自定义前后内容 自定义主题 外部控制器 完成后跳转的完整页面如下import package:flame_splash_screen/flame_splash_screen.dart; import package:flutter/material.dart; class SplashScreenGame extends StatefulWidget { const SplashScreenGame({super.key}); override SplashScreenGameState createState() SplashScreenGameState(); } class SplashScreenGameState extends StateSplashScreenGame { late final FlameSplashController _controller; override void initState() { super.initState(); _controller FlameSplashController( fadeInDuration: const Duration(milliseconds: 800), waitDuration: const Duration(seconds: 2), fadeOutDuration: const Duration(milliseconds: 400), autoStart: true, ); } override void dispose() { _controller.dispose(); super.dispose(); } override Widget build(BuildContext context) { return Scaffold( body: FlameSplashScreen( showBefore: (context) const Center(child: Text(MY STUDIO)), showAfter: (context) const Center(child: Text(Loading...)), theme: FlameSplashTheme.dark, controller: _controller, onFinish: (context) Navigator.pushReplacementvoid, void( context, MaterialPageRoute(builder: (context) const GameHomePage()), ), ), ); } }与仓库 示例工程 一致onFinish中应使用pushReplacement或pushNamed完成页面切换避免用户能通过返回键回到启动画面。九、测试保障与质量验证该包围绕两个核心维度提供测试覆盖controller_test.dart验证控制器在autoStart开关下的启动时机、状态流转idle → started → finished、onFinish回调触发时机以及重复启动的断言保护theme_test.dart验证white/dark两套内置主题的backgroundDecoration颜色值0xFFFFFFFF/0xFF000000与默认约束BoxConstraints.expand()。这些测试从行为层面锁定了组件挂载即播、播完必回调、控制器状态严格流转等关键契约你在接入时可以参考它们理解组件的行为边界。十、小结flame_splash_screen用非常克制的 API 设计解决了游戏启动画面这一高频需求两个必填参数即可完成火焰 Logo 动画 完成后跳转的最小闭环showBefore/showAfter与自定义FlameSplashTheme提供了品牌化扩展空间FlameSplashController则把时长、启动时机与生命周期控制权完整交还给开发者。其源码的步骤编排 三段式动画 状态机设计也值得在阅读时细细品味——这本身就是一套优雅的 Flutter 动画编排范式。【免费下载链接】flameA Flutter based game engine.项目地址: https://gitcode.com/GitHub_Trending/fl/flame创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进