已经完成鸿蒙化的Flutter专业动画工具箱animations库实战示例
本文介绍了如何在鸿蒙系统(OpenHarmony)上使用Flutter的animations库实现Material Design过渡动画。主要内容包括: 依赖配置:在pubspec.yaml中添加适配后的animations库,该库提供了开箱即用的Material Design动画组件。
你好!👋 欢迎来到这篇关于 animations 在 HarmonyOS (OpenHarmony) 上使用的实战教程。当前已经完成鸿蒙化适配的Flutter三方库均可在flutter_packages仓库下查看。
本文将手把手带你实现Material Design 过渡动画,让你的应用动效更加流畅优雅!🎬
📦 1. 引入依赖:pubspec.yaml
首先,我们需要在项目的配置文件中引入适配后的 animations 库。
修改文件:pubspec.yaml
操作:在 dependencies 节点下添加 animations 的 git 依赖配置。
dependencies:
flutter:
sdk: flutter
# 👇 新增:引入 animations 鸿蒙适配版本
animations:
git:
url: https://gitcode.com/openharmony-tpc/flutter_packages.git
path: packages/animations
ref: br_animations-v2.0.11_ohos
💡 提示:修改完成后,别忘了运行终端命令
flutter pub get来下载依赖!
🎯 为什么选择 animations 库?
animations 是 Flutter 官方提供的 Material Design 过渡动画库,它封装了多种高质量的过渡效果。
与手写动画代码相比,animations 就像是一个 🎨 专业动画工具箱,让你无需从零编写复杂的动画控制器和过渡逻辑,直接使用现成的动画组件即可!
核心优势:
- ✅ 符合 Material Design 规范:遵循官方设计指南
- ✅ 开箱即用:无需编写动画控制器代码
- ✅ 高性能:经过优化的动画实现
- ✅ 易于定制:支持丰富的配置参数
👶 新手小课堂:animations 是什么?
animations 包提供了一系列预定义的 Material Design 过渡动画组件。
想象一下,你需要实现一个从卡片展开到详情页的动画效果。传统方式需要:
- 创建 AnimationController
- 定义 Tween 动画
- 监听动画状态
- 处理手势交互
- 管理多个动画的协调
而使用 animations 库,只需要一个 OpenContainer 组件就能实现!就像使用 🎬 电影特效模板,点击应用就能获得专业级动画效果!
主要组件:
- 🎭 OpenContainer:容器展开/收起动画
- 🔄 PageTransitionSwitcher:页面切换动画容器
- ↔️ SharedAxisTransition:共享轴过渡动画
- 🌫️ FadeThroughTransition:淡入淡出过渡动画
🎬 2. 核心动画组件详解
2.1 🎭 OpenContainer - 容器变换动画
OpenContainer 实现了从一个"关闭"状态的容器到"打开"状态的全屏页面的过渡动画。
📊 OpenContainer 工作流程:
核心代码实现:
import 'package:animations/animations.dart';
OpenContainer(
transitionType: ContainerTransitionType.fade, // 过渡类型
transitionDuration: const Duration(milliseconds: 500), // 动画时长
closedElevation: 4, // 关闭状态阴影
closedShape: RoundedRectangleBorder( // 关闭状态形状
borderRadius: BorderRadius.circular(12),
),
closedColor: Colors.blue.shade100, // 关闭状态颜色
openColor: Colors.white, // 打开状态颜色
closedBuilder: (context, action) {
// 👇 关闭状态的UI(列表项、卡片等)
return Container(
height: 120,
padding: const EdgeInsets.all(20),
child: const Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.image, size: 40, color: Colors.blue),
SizedBox(height: 8),
Text('点击查看详情'),
],
),
);
},
openBuilder: (context, action) {
// 👇 打开状态的UI(详情页)
return Scaffold(
appBar: AppBar(title: const Text('详情页')),
body: const Center(child: Text('这是详情内容')),
);
},
)
参数说明:
- 🎨 transitionType:过渡类型(
fade或fadeThrough) - ⏱️ transitionDuration:动画持续时间
- 📦 closedBuilder:关闭状态的构建器
- 🖼️ openBuilder:打开状态的构建器
- 🎯 closedElevation:关闭状态的阴影高度
- 🔲 closedShape:关闭状态的形状
2.2 🔄 PageTransitionSwitcher - 页面切换动画
PageTransitionSwitcher 用于在多个子组件之间切换时提供平滑的过渡动画。
📊 PageTransitionSwitcher 切换流程:
核心代码实现:
PageTransitionSwitcher(
duration: const Duration(milliseconds: 300), // 动画时长
reverse: false, // 是否反向播放
transitionBuilder: (child, primaryAnimation, secondaryAnimation) {
// 👇 自定义过渡效果
return SharedAxisTransition(
animation: primaryAnimation,
secondaryAnimation: secondaryAnimation,
transitionType: SharedAxisTransitionType.horizontal,
child: child,
);
},
child: Container(
key: ValueKey<int>(currentIndex), // 👈 关键:通过key触发切换
child: const Text('当前页面内容'),
),
)
关键点:
- 🔑 key:必须使用不同的 key 来触发动画
- 🎨 transitionBuilder:自定义过渡动画构建器
- ⏱️ duration:控制动画时长
- 🔄 reverse:控制动画方向
2.3 ↔️ SharedAxisTransition - 共享轴过渡
SharedAxisTransition 实现沿共享轴的过渡效果,适合层级页面切换。
📊 共享轴动画原理:
核心代码实现:
SharedAxisTransition(
animation: animation, // 进入动画
secondaryAnimation: secondaryAnimation, // 退出动画
transitionType: SharedAxisTransitionType.horizontal, // 轴类型
child: child, // 子组件
)
轴类型说明:
- ↔️ horizontal:水平轴(适合左右切换)
- ↕️ vertical:垂直轴(适合上下切换)
- 🔍 scaled:缩放轴(适合层级切换)
2.4 🌫️ FadeThroughTransition - 淡入淡出过渡
FadeThroughTransition 实现内容淡出后新内容淡入的效果,适合同级页面切换。
📊 淡入淡出动画流程:
核心代码实现:
FadeThroughTransition(
animation: animation, // 进入动画
secondaryAnimation: secondaryAnimation, // 退出动画
child: child, // 子组件
)
适用场景:
- 📑 Tab 页签切换
- 🎯 底部导航栏切换
- 🎨 内容类型切换
- 📊 数据视图切换
💻 3. 完整示例:lib/animations_demo.dart
接下来,我们创建一个完整的演示页面,展示所有动画组件的使用方法。
新建文件:lib/animations_demo.dart
3.1 页面功能概览
我们的演示页面将实现以下功能:
- 🎭 OpenContainer 演示:容器展开到详情页
- 🔄 PageTransitionSwitcher 演示:页面内容切换
- ↔️ SharedAxisTransition 演示:不同轴方向切换
- 🌫️ FadeThroughTransition 演示:淡入淡出切换
3.2 导入依赖
import 'package:flutter/material.dart';
import 'package:animations/animations.dart';
3.3 OpenContainer 实现示例
实现效果:
- 📦 点击卡片展开到详情页
- 🎨 支持两种过渡类型选择
- ⏱️ 自定义动画时长
class _AnimationsDemoPageState extends State<AnimationsDemoPage> {
ContainerTransitionType _transitionType = ContainerTransitionType.fade;
Widget build(BuildContext context) {
return OpenContainer(
transitionType: _transitionType,
transitionDuration: const Duration(milliseconds: 500),
closedElevation: 4,
closedShape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
closedColor: Colors.blue.shade100,
openColor: Colors.white,
closedBuilder: (context, action) {
return Container(
height: 120,
padding: const EdgeInsets.all(20),
child: const Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.image, size: 40, color: Colors.blue),
SizedBox(height: 8),
Text('点击查看详情'),
],
),
);
},
openBuilder: (context, action) {
return Scaffold(
appBar: AppBar(title: const Text('详情页')),
body: const Center(
child: Text('这是详情内容'),
),
);
},
);
}
}

3.4 PageTransitionSwitcher 实现示例
实现效果:
- 🔄 支持前后翻页
- ↔️ 水平轴滑动效果
- 🎯 自动处理反向动画
class _PageTransitionDemo extends StatefulWidget {
State<_PageTransitionDemo> createState() => _PageTransitionDemoState();
}
class _PageTransitionDemoState extends State<_PageTransitionDemo> {
int _currentIndex = 0;
bool _isReverse = false;
void _changePage(int delta) {
setState(() {
_isReverse = delta < 0; // 👈 控制动画方向
_currentIndex = (_currentIndex + delta) % pages.length;
});
}
Widget build(BuildContext context) {
return PageTransitionSwitcher(
duration: const Duration(milliseconds: 300),
reverse: _isReverse,
transitionBuilder: (child, primaryAnimation, secondaryAnimation) {
return SharedAxisTransition(
animation: primaryAnimation,
secondaryAnimation: secondaryAnimation,
transitionType: SharedAxisTransitionType.horizontal,
child: child,
);
},
child: Container(
key: ValueKey<int>(_currentIndex), // 👈 关键:触发切换
child: pages[_currentIndex],
),
);
}
}

3.5 SharedAxisTransition 实现示例
实现效果:
- ↔️ 支持横向/纵向切换
- 🎨 动态切换轴方向
- ✨ 平滑的进出动画
class _SharedAxisDemo extends StatefulWidget {
State<_SharedAxisDemo> createState() => _SharedAxisDemoState();
}
class _SharedAxisDemoState extends State<_SharedAxisDemo> {
SharedAxisTransitionType _transitionType =
SharedAxisTransitionType.horizontal;
int _currentIndex = 0;
Widget build(BuildContext context) {
return Column(
children: [
// 轴方向选择器
SegmentedButton<SharedAxisTransitionType>(
segments: const [
ButtonSegment(
value: SharedAxisTransitionType.horizontal,
label: Text('横向'),
),
ButtonSegment(
value: SharedAxisTransitionType.vertical,
label: Text('纵向'),
),
],
selected: {_transitionType},
onSelectionChanged: (newSelection) {
setState(() {
_transitionType = newSelection.first;
});
},
),
// 动画容器
PageTransitionSwitcher(
transitionBuilder: (child, primaryAnimation, secondaryAnimation) {
return SharedAxisTransition(
animation: primaryAnimation,
secondaryAnimation: secondaryAnimation,
transitionType: _transitionType,
child: child,
);
},
child: Container(
key: ValueKey<int>(_currentIndex),
child: Text('内容 ${_currentIndex + 1}'),
),
),
],
);
}
}

3.6 FadeThroughTransition 实现示例
实现效果:
- 🌫️ 平滑的淡入淡出
- 🎯 适合 Tab 切换
- ✨ 优雅的内容替换
class _FadeThroughDemo extends StatefulWidget {
State<_FadeThroughDemo> createState() => _FadeThroughDemoState();
}
class _FadeThroughDemoState extends State<_FadeThroughDemo> {
int _currentIndex = 0;
Widget build(BuildContext context) {
return PageTransitionSwitcher(
duration: const Duration(milliseconds: 400),
transitionBuilder: (child, primaryAnimation, secondaryAnimation) {
return FadeThroughTransition(
animation: primaryAnimation,
secondaryAnimation: secondaryAnimation,
child: child,
);
},
child: Container(
key: ValueKey<int>(_currentIndex),
child: tabs[_currentIndex],
),
);
}
}

🚀 4. 配置应用入口:lib/main.dart
最后,我们在应用的主页添加入口,跳转到演示页面。
修改文件:lib/main.dart
操作:
- 导入演示页面文件
- 在按钮列表中添加跳转按钮
// 1. 导入头文件
import 'animations_demo.dart';
// 2. 在 build 方法中添加跳转按钮
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const AnimationsDemoPage(),
),
);
},
child: const Text('Go to Animations Demo'),
),
⚠️ 5. 常见错误与解决方案
❌ 错误 1:PageTransitionSwitcher 不触发动画
😱 错误现象:
- 切换内容时没有动画效果
- 页面直接跳变
🔍 原因分析:
- ❌ 子组件没有设置不同的
key - ❌ child 的引用没有改变
✅ 解决方案:
// ❌ 错误:没有使用 key
PageTransitionSwitcher(
child: Container(
child: Text('内容 $_index'),
),
)
// ✅ 正确:使用不同的 key
PageTransitionSwitcher(
child: Container(
key: ValueKey<int>(_index), // 👈 关键!
child: Text('内容 $_index'),
),
)
❌ 错误 2:OpenContainer 点击无响应
😱 错误现象:
- 点击 OpenContainer 没有任何反应
- 无法打开详情页
🔍 原因分析:
- ❌
closedBuilder中的 widget 拦截了点击事件 - ❌ 使用了
GestureDetector等手势组件
✅ 解决方案:
// ❌ 错误:内部使用了 GestureDetector
OpenContainer(
closedBuilder: (context, action) {
return GestureDetector(
onTap: () {}, // 这会拦截 OpenContainer 的点击
child: Container(...),
);
},
openBuilder: (context, action) => DetailPage(),
)
// ✅ 正确:不要在内部使用手势组件
OpenContainer(
closedBuilder: (context, action) {
return Container(...), // 直接返回内容
},
openBuilder: (context, action) => DetailPage(),
)
❌ 错误 3:动画卡顿或不流畅
😱 错误现象:
- 动画播放时出现卡顿
- 动画帧率不稳定
🔍 原因分析:
- ❌ 动画时长设置过短或过长
- ❌ builder 中进行了耗时操作
- ❌ 同时播放多个复杂动画
✅ 解决方案:
1. 合理设置动画时长:
// ❌ 过短:动画太快,看不清
duration: const Duration(milliseconds: 100)
// ✅ 合适:流畅且舒适
duration: const Duration(milliseconds: 300)
// ❌ 过长:动画拖沓
duration: const Duration(milliseconds: 1000)
动画时长参考:
- ⚡ 快速切换:200-250ms(Tab、底部导航)
- 🎯 标准动画:300-400ms(页面切换、卡片展开)
- 🎬 复杂动画:500-600ms(全屏过渡、容器变换)
2. 优化 builder 性能:
// ❌ 错误:在 builder 中进行耗时操作
openBuilder: (context, action) {
final data = heavyComputation(); // 耗时操作
return DetailPage(data: data);
}
// ✅ 正确:提前计算好数据
openBuilder: (context, action) {
return DetailPage(data: preComputedData);
}
❌ 错误 4:SharedAxisTransition 方向错误
😱 错误现象:
- 动画方向与预期不符
- 横向切换变成了纵向
🔍 原因分析:
- ❌
transitionType设置错误 - ❌
reverse参数使用不当
✅ 解决方案:
// 明确指定轴方向
SharedAxisTransition(
animation: animation,
secondaryAnimation: secondaryAnimation,
transitionType: SharedAxisTransitionType.horizontal, // 👈 明确指定
child: child,
)
方向选择建议:
- ↔️ horizontal:左右切换(适合:相册、轮播、步骤)
- ↕️ vertical:上下切换(适合:列表详情、弹出面板)
- 🔍 scaled:缩放切换(适合:层级导航、弹窗)
❌ 错误 5:FadeThroughTransition 闪烁
😱 错误现象:
- 切换时出现明显闪烁
- 中间出现空白帧
🔍 原因分析:
- ❌ 两个子组件高度不一致
- ❌ 动画时长设置不当
✅ 解决方案:
// ✅ 确保容器高度一致
PageTransitionSwitcher(
transitionBuilder: (child, animation, secondaryAnimation) {
return FadeThroughTransition(
animation: animation,
secondaryAnimation: secondaryAnimation,
child: child,
);
},
child: SizedBox(
height: 200, // 👈 固定高度
key: ValueKey<int>(_index),
child: content,
),
)
📋 6. 动画组件对比表
| 动画组件 | 适用场景 | 特点 | 推荐场景 |
|---|---|---|---|
| 🎭 OpenContainer | 容器→详情页 | 展开/收起过渡 | 列表项详情、卡片展开 |
| 🔄 PageTransitionSwitcher | 页面内容切换 | 自定义过渡容器 | 多页面切换、内容替换 |
| ↔️ SharedAxisTransition | 层级页面切换 | 共享轴滑动 | 导航切换、步骤流程 |
| 🌫️ FadeThroughTransition | 同级内容切换 | 淡入淡出 | Tab切换、数据视图切换 |
选择建议:
- 📱 列表→详情 → 使用
OpenContainer - 🔄 页面切换 → 使用
SharedAxisTransition - 🎯 Tab切换 → 使用
FadeThroughTransition - 🎨 自定义过渡 → 使用
PageTransitionSwitcher+ 自定义动画
🎨 7. 性能优化建议
⚡ 动画性能优化
1. 避免在动画中重建复杂组件
// ❌ 不好:每次动画都重建列表
PageTransitionSwitcher(
child: ListView.builder(
key: ValueKey(_index),
itemCount: 1000,
itemBuilder: (context, index) => ExpensiveWidget(),
),
)
// ✅ 更好:使用 RepaintBoundary 隔离
PageTransitionSwitcher(
child: RepaintBoundary(
key: ValueKey(_index),
child: CachedListView(),
),
)
2. 合理使用动画时长
// 根据动画复杂度调整时长
const Duration shortDuration = Duration(milliseconds: 200); // 简单切换
const Duration mediumDuration = Duration(milliseconds: 300); // 标准动画
const Duration longDuration = Duration(milliseconds: 500); // 复杂过渡
3. 减少动画层级
// ❌ 避免:嵌套多层动画
OpenContainer(
openBuilder: (context, action) {
return PageTransitionSwitcher( // 嵌套动画
child: SharedAxisTransition(...),
);
},
)
// ✅ 推荐:使用单一动画
OpenContainer(
openBuilder: (context, action) => DetailPage(),
)
🎉 结语
通过以上步骤,我们完成了 animations 在鸿蒙系统上的完整集成!🚀
📝 本教程涵盖内容:
- ✅ 引入依赖配置
- ✅ 四大核心动画组件详解
- ✅ OpenContainer 容器变换动画
- ✅ PageTransitionSwitcher 页面切换动画
- ✅ SharedAxisTransition 共享轴动画
- ✅ FadeThroughTransition 淡入淡出动画
- ✅ 常见错误解决方案
- ✅ 性能优化建议
✨ 核心要点回顾:
- 🎯 选对组件:根据场景选择合适的动画组件
- ⏱️ 合理时长:动画时长要符合用户预期(200-500ms)
- 🔑 正确使用 key:PageTransitionSwitcher 必须使用不同的 key
- 🎨 性能优化:避免在动画中进行耗时操作
- 📱 Material Design:遵循官方设计规范
🚀 进阶学习方向:
- 🎬 结合 Hero 动画实现共享元素过渡
- 🎨 自定义过渡动画效果
- ⚡ 优化大列表动画性能
- 🎭 实现复杂的多步骤过渡动画
- 📦 封装通用的动画组件库
📚 完整动画流程:
🌟 Material Design 动画原则:
- ⚡ 快速响应:动画应该快速启动(<100ms)
- 🎯 明确目的:动画要有明确的开始和结束状态
- 🎨 自然流畅:使用缓动曲线,避免线性动画
- 📱 保持一致:相同类型的交互使用相同的动画
- 🎭 有意义:动画应该帮助用户理解界面变化
快去运行你的鸿蒙应用试试吧!Happy Coding! 💻⚡️
更多推荐

所有评论(0)