Flutter for OpenHarmony 第三方库实战:使用 confetti 构建抽奖结果庆祝页面
欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net
项目效果
本文实现的是一个基于 Flutter for OpenHarmony 的抽奖结果庆祝页面。项目中使用 Flutter 第三方库 confetti 实现彩纸庆祝动画,当用户点击“开始抽奖”按钮后,页面会随机生成奖品,并播放庆祝动画。
最终运行效果如下:


页面主要包含以下内容:
- 顶部标题栏;
- 抽奖活动说明卡片;
- 当前抽中奖品展示;
- 彩纸庆祝动画;
- 奖品列表;
- 开始抽奖按钮;
- 重置结果按钮;
- 第三方库使用说明;
- 页面整体采用 Flutter Material 风格布局。
本文重点是演示如何在 Flutter for OpenHarmony 项目中使用 Flutter 第三方库 confetti。项目代码写在 lib/main.dart 中,依赖配置写在 pubspec.yaml 中,符合 Flutter for OpenHarmony 第三方库实践方向。
前言
在移动应用开发中,动画反馈可以提升用户操作后的体验。例如抽奖成功、任务完成、签到成功、成就解锁、订单支付成功等场景,都可以使用庆祝动画增强页面表现力。
如果只是显示一行文字:
恭喜中奖
功能上当然没错,但视觉反馈比较弱。用户点完按钮后看到一行干巴巴的文字,就像生日蛋糕上没有蜡烛,能吃,但少了点仪式感。
本文选择使用 Flutter 第三方库 confetti 实现彩纸庆祝动画。它可以在页面中喷射彩纸粒子,并支持控制粒子数量、方向、速度、重力和颜色。
本项目以“抽奖结果庆祝页面”为例,使用 confetti 实现中奖后的庆祝效果,并结合 Flutter 页面状态更新实现随机抽奖功能。
一、项目目标
本次实践主要实现以下目标:
- 创建 Flutter for OpenHarmony 项目;
- 在
pubspec.yaml中添加第三方库confetti; - 使用
flutter pub get获取依赖; - 在
lib/main.dart中引入confetti; - 使用
ConfettiController控制动画播放; - 使用
ConfettiWidget展示彩纸动画; - 使用 Flutter Material 组件构建抽奖页面;
- 实现随机抽奖功能;
- 实现抽奖结果展示;
- 实现重置抽奖结果;
- 将应用运行到 OpenHarmony 设备或模拟器中。
二、技术栈
| 类型 | 内容 |
|---|---|
| 开发方向 | Flutter for OpenHarmony |
| 开发语言 | Dart |
| UI 框架 | Flutter |
| 第三方库 | confetti |
| 功能场景 | 彩纸动画 / 抽奖结果 / 成功反馈 |
| 核心组件 | ConfettiController / ConfettiWidget |
| 项目入口 | lib/main.dart |
| 依赖配置 | pubspec.yaml |
| 运行平台 | OpenHarmony 设备或模拟器 |
三、为什么选择 confetti
在实际应用中,庆祝动画可以用于很多场景,例如:
- 抽奖中奖;
- 签到成功;
- 任务完成;
- 学习目标达成;
- 支付成功;
- 订单提交成功;
- 游戏通关;
- 成就解锁;
- 活动页奖励发放。
如果完全自己使用 Flutter 原生动画绘制彩纸,需要处理粒子位置、运动方向、重力、随机颜色、生命周期和刷新逻辑。能写,但为了几片彩纸写一套粒子系统,多少有点像为了吃泡面去修炼农业文明。
confetti 已经封装好了彩纸动画组件,可以让开发者快速实现庆祝效果。
在本项目中,confetti 主要完成以下工作:
- 播放彩纸庆祝动画;
- 控制动画开始和停止;
- 设置彩纸喷射方向;
- 设置彩纸数量;
- 设置彩纸颜色;
- 增强抽奖结果的视觉反馈。
四、创建 Flutter for OpenHarmony 项目
在已经配置好 Flutter for OpenHarmony 开发环境的前提下,可以创建一个 Flutter 项目。
示例项目名称:
flutter create lottery_confetti_demo
进入项目目录:
cd lottery_confetti_demo
项目创建完成后,主要关注两个文件:
lottery_confetti_demo
├── pubspec.yaml
└── lib
└── main.dart
其中:
| 文件 | 作用 |
|---|---|
| pubspec.yaml | 配置 Flutter 项目依赖 |
| lib/main.dart | 编写 Flutter 页面和业务逻辑 |
五、添加 confetti 第三方库
打开项目根目录下的 pubspec.yaml 文件,在 dependencies 中添加 confetti。
示例配置如下:
dependencies:
flutter:
sdk: flutter
confetti: ^0.8.0
完整结构大致如下:
name: lottery_confetti_demo
description: A Flutter for OpenHarmony confetti demo.
publish_to: 'none'
version: 1.0.0+1
environment:
sdk: '>=3.4.0 <4.0.0'
dependencies:
flutter:
sdk: flutter
confetti: ^0.8.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
添加完成后,在终端执行:
flutter pub get
执行成功后,就可以在 Dart 代码中使用 confetti 了。
六、项目结构
本项目主要修改 lib/main.dart 文件:
lib
└── main.dart
本项目不需要编写 OpenHarmony 原生 ArkTS 页面,也不需要修改 Index.ets。
因为这是 Flutter for OpenHarmony 项目,页面主体应该是 Flutter 代码。审核重点会看:
- 是否使用
pubspec.yaml添加 Flutter 第三方库; - 是否在 Dart 文件中
import package; - 是否在
lib/main.dart中实际调用第三方库; - 是否属于 Flutter for OpenHarmony 项目。
看到 pubspec.yaml、lib/main.dart、import 'package:confetti/confetti.dart';,这才是正确方向。看到 Index.ets,审核大概率又会把文章打回,世界就是这么直接。
七、核心实现思路
本项目的核心流程如下:
- 在
pubspec.yaml中添加confetti; - 在
main.dart中引入第三方库; - 创建
ConfettiController控制彩纸动画; - 在页面中使用
ConfettiWidget显示彩纸; - 定义奖品数据模型;
- 点击按钮后随机选择奖品;
- 更新当前中奖结果;
- 调用
ConfettiController.play()播放动画; - 点击重置按钮后清空抽奖结果。
第三方库引入代码如下:
import 'package:confetti/confetti.dart';
创建控制器代码如下:
late final ConfettiController _confettiController;
播放动画核心代码如下:
_confettiController.play();
彩纸组件核心代码如下:
ConfettiWidget(
confettiController: _confettiController,
blastDirectionality: BlastDirectionality.explosive,
shouldLoop: false,
)
八、main.dart 完整代码
打开文件:
lib/main.dart
将其中内容替换为下面代码:
import 'dart:math';
import 'package:confetti/confetti.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const LotteryConfettiApp());
}
class LotteryConfettiApp extends StatelessWidget {
const LotteryConfettiApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Lottery Confetti Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.pink,
brightness: Brightness.light,
),
useMaterial3: true,
),
home: const LotteryHomePage(),
);
}
}
class PrizeItem {
const PrizeItem({
required this.name,
required this.level,
required this.description,
required this.icon,
required this.color,
});
final String name;
final String level;
final String description;
final IconData icon;
final Color color;
}
class LotteryHomePage extends StatefulWidget {
const LotteryHomePage({super.key});
State<LotteryHomePage> createState() => _LotteryHomePageState();
}
class _LotteryHomePageState extends State<LotteryHomePage> {
late final ConfettiController _confettiController;
final Random _random = Random();
final List<PrizeItem> _prizes = const [
PrizeItem(
name: '蓝牙耳机',
level: '一等奖',
description: '适合通勤、学习和运动使用的实用奖品。',
icon: Icons.headphones,
color: Colors.deepPurple,
),
PrizeItem(
name: '键盘鼠标套装',
level: '二等奖',
description: '适合学习和办公场景,码字效率直接拉满。',
icon: Icons.keyboard,
color: Colors.blue,
),
PrizeItem(
name: '咖啡兑换券',
level: '三等奖',
description: '适合熬夜学习和赶作业时维持基本人类形态。',
icon: Icons.coffee,
color: Colors.brown,
),
PrizeItem(
name: '学习笔记本',
level: '幸运奖',
description: '记录计划、灵感和那些迟早会被拖延的任务。',
icon: Icons.menu_book,
color: Colors.teal,
),
PrizeItem(
name: '随机小礼品',
level: '参与奖',
description: '虽然不是大奖,但至少不是空手而归。',
icon: Icons.card_giftcard,
color: Colors.orange,
),
];
PrizeItem? _currentPrize;
int _drawCount = 0;
void initState() {
super.initState();
_confettiController = ConfettiController(
duration: const Duration(seconds: 2),
);
}
void dispose() {
_confettiController.dispose();
super.dispose();
}
String get _resultText {
if (_currentPrize == null) {
return '点击按钮开始抽奖';
}
return '恭喜抽中:${_currentPrize!.name}';
}
void _startLottery() {
final int index = _random.nextInt(_prizes.length);
setState(() {
_currentPrize = _prizes[index];
_drawCount++;
});
_confettiController.stop();
_confettiController.play();
}
void _resetLottery() {
setState(() {
_currentPrize = null;
_drawCount = 0;
});
_confettiController.stop();
}
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
return Scaffold(
appBar: AppBar(
title: const Text('抽奖庆祝页面'),
centerTitle: true,
),
body: Stack(
alignment: Alignment.topCenter,
children: [
SafeArea(
child: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildIntroCard(theme),
const SizedBox(height: 16),
_buildPrizeResultCard(theme),
const SizedBox(height: 16),
_buildActionCard(theme),
const SizedBox(height: 16),
_buildPrizeListCard(theme),
const SizedBox(height: 16),
_buildLibraryCard(theme),
],
),
),
ConfettiWidget(
confettiController: _confettiController,
blastDirectionality: BlastDirectionality.explosive,
shouldLoop: false,
emissionFrequency: 0.08,
numberOfParticles: 28,
maxBlastForce: 26,
minBlastForce: 8,
gravity: 0.18,
colors: const [
Colors.pink,
Colors.orange,
Colors.blue,
Colors.green,
Colors.purple,
Colors.amber,
],
),
],
),
);
}
Widget _buildIntroCard(ThemeData theme) {
return Card(
elevation: 3,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(22),
),
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
children: [
Container(
width: 76,
height: 76,
decoration: BoxDecoration(
color: theme.colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(24),
),
child: Icon(
Icons.celebration,
size: 42,
color: theme.colorScheme.onPrimaryContainer,
),
),
const SizedBox(height: 18),
Text(
'Flutter for OpenHarmony',
style: theme.textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
'使用 confetti 构建抽奖后的彩纸庆祝动画效果',
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
height: 1.5,
),
textAlign: TextAlign.center,
),
],
),
),
);
}
Widget _buildPrizeResultCard(ThemeData theme) {
final PrizeItem? prize = _currentPrize;
return Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18),
),
child: Padding(
padding: const EdgeInsets.all(22),
child: Column(
children: [
Text(
'抽奖结果',
style: theme.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 20),
AnimatedContainer(
duration: const Duration(milliseconds: 300),
width: 112,
height: 112,
decoration: BoxDecoration(
color: prize == null
? theme.colorScheme.surfaceContainerHighest
: prize.color.withOpacity(0.16),
borderRadius: BorderRadius.circular(32),
),
child: Icon(
prize == null ? Icons.help_outline : prize.icon,
size: 58,
color: prize == null
? theme.colorScheme.onSurfaceVariant
: prize.color,
),
),
const SizedBox(height: 18),
Text(
_resultText,
style: theme.textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
color: prize == null
? theme.colorScheme.onSurface
: prize.color,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
prize == null ? '当前还没有抽奖结果' : prize.level,
style: theme.textTheme.titleMedium?.copyWith(
color: theme.colorScheme.primary,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 12),
Text(
prize == null
? '点击下方按钮后,系统会随机抽取一个奖品,并播放彩纸动画。'
: prize.description,
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
height: 1.5,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 14),
Text(
'已抽奖 $_drawCount 次',
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
],
),
),
);
}
Widget _buildActionCard(ThemeData theme) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18),
),
child: Padding(
padding: const EdgeInsets.all(20),
child: Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: _startLottery,
icon: const Icon(Icons.casino),
label: const Text('开始抽奖'),
),
),
const SizedBox(width: 12),
Expanded(
child: OutlinedButton.icon(
onPressed: _resetLottery,
icon: const Icon(Icons.refresh),
label: const Text('重置结果'),
),
),
],
),
),
);
}
Widget _buildPrizeListCard(ThemeData theme) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18),
),
child: Padding(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'奖品列表',
style: theme.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
..._prizes.map((prize) {
final bool selected = _currentPrize?.name == prize.name;
return Container(
margin: const EdgeInsets.only(bottom: 10),
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: selected
? prize.color.withOpacity(0.12)
: theme.colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: selected ? prize.color : Colors.transparent,
width: 1.2,
),
),
child: Row(
children: [
Icon(
prize.icon,
color: prize.color,
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
prize.name,
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
prize.level,
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
],
),
),
if (selected)
Icon(
Icons.check_circle,
color: prize.color,
),
],
),
);
}),
],
),
),
);
}
Widget _buildLibraryCard(ThemeData theme) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18),
),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'第三方库说明',
style: theme.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
_buildInfoRow(
theme,
title: '库名称',
value: 'confetti',
),
_buildInfoRow(
theme,
title: '配置文件',
value: 'pubspec.yaml',
),
_buildInfoRow(
theme,
title: '导入方式',
value: "import 'package:confetti/confetti.dart';",
),
_buildInfoRow(
theme,
title: '核心组件',
value: 'ConfettiController / ConfettiWidget',
),
_buildInfoRow(
theme,
title: '应用场景',
value: '中奖结果、任务完成、成就解锁、签到成功、支付成功',
),
],
),
),
);
}
Widget _buildInfoRow(
ThemeData theme, {
required String title,
required String value,
}) {
return Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 82,
child: Text(
title,
style: theme.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
),
Expanded(
child: Text(
value,
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
),
],
),
);
}
}
九、代码实现说明
1. 引入 confetti 第三方库
代码开头引入第三方库:
import 'package:confetti/confetti.dart';
这说明项目确实使用了 Flutter 第三方库,而不是 OpenHarmony 原生库。
本项目中主要使用两个对象:
ConfettiController
ConfettiWidget
其中:
| 对象 | 作用 |
|---|---|
| ConfettiController | 控制彩纸动画播放、停止和释放 |
| ConfettiWidget | 在页面中显示彩纸动画 |
2. 创建 ConfettiController
页面中定义控制器:
late final ConfettiController _confettiController;
在 initState() 中初始化:
_confettiController = ConfettiController(
duration: const Duration(seconds: 2),
);
其中 duration 表示彩纸动画的播放时间。
3. 释放 ConfettiController
因为 ConfettiController 是动画控制器,所以页面销毁时需要释放:
void dispose() {
_confettiController.dispose();
super.dispose();
}
如果不释放控制器,可能造成资源没有正确回收。程序不是垃圾场,控制器用完就该处理掉,别什么都往内存里扔。
4. 使用 ConfettiWidget 显示彩纸动画
页面中使用 ConfettiWidget 展示彩纸:
ConfettiWidget(
confettiController: _confettiController,
blastDirectionality: BlastDirectionality.explosive,
shouldLoop: false,
)
其中:
| 参数 | 作用 |
|---|---|
| confettiController | 绑定动画控制器 |
| blastDirectionality | 设置彩纸喷射方向 |
| shouldLoop | 是否循环播放 |
本项目使用:
BlastDirectionality.explosive
表示彩纸会向多个方向爆发式喷射。
5. 设置彩纸动画参数
本项目中还设置了多个动画参数:
emissionFrequency: 0.08,
numberOfParticles: 28,
maxBlastForce: 26,
minBlastForce: 8,
gravity: 0.18,
参数说明如下:
| 参数 | 作用 |
|---|---|
| emissionFrequency | 粒子发射频率 |
| numberOfParticles | 每次发射的彩纸数量 |
| maxBlastForce | 最大喷射力度 |
| minBlastForce | 最小喷射力度 |
| gravity | 彩纸下落速度 |
这些参数可以调整彩纸动画的强弱和速度。
6. 设置彩纸颜色
彩纸颜色通过 colors 参数设置:
colors: const [
Colors.pink,
Colors.orange,
Colors.blue,
Colors.green,
Colors.purple,
Colors.amber,
],
如果不设置颜色,组件也可以使用默认随机颜色。
这里手动设置颜色,是为了让页面风格更统一。
7. 实现随机抽奖
抽奖时使用 Random 生成随机下标:
final int index = _random.nextInt(_prizes.length);
然后根据随机下标获取奖品:
_currentPrize = _prizes[index];
这样每次点击“开始抽奖”按钮,都会随机得到一个奖品。
8. 点击按钮播放彩纸动画
点击抽奖按钮后执行:
void _startLottery() {
final int index = _random.nextInt(_prizes.length);
setState(() {
_currentPrize = _prizes[index];
_drawCount++;
});
_confettiController.stop();
_confettiController.play();
}
这里先更新抽奖结果,再播放彩纸动画。
先调用:
_confettiController.stop();
再调用:
_confettiController.play();
这样可以保证连续点击按钮时,彩纸动画能重新开始播放。
9. 使用 Stack 叠放页面和动画
页面主体使用 Stack:
Stack(
alignment: Alignment.topCenter,
children: [
SafeArea(...),
ConfettiWidget(...),
],
)
这样可以让彩纸动画显示在页面上方,而不会影响下方卡片布局。
如果不用 Stack,彩纸组件可能会占用普通布局空间,页面结构会比较奇怪。彩纸应该飘在页面上,不应该像普通卡片一样排队站好,连彩纸都这么规矩就太悲惨了。
十、运行项目
完成代码后,在终端执行:
flutter pub get
然后连接 OpenHarmony 设备或启动 OpenHarmony 模拟器。
查看设备:
flutter devices
运行项目:
flutter run
如果环境配置正确,应用会运行到 OpenHarmony 设备或模拟器中。
运行成功后,页面会显示“抽奖庆祝页面”。点击“开始抽奖”按钮后,页面会随机显示中奖结果,并播放彩纸庆祝动画。
十一、开发中遇到的问题
1. confetti 依赖没有生效
如果代码中出现找不到 confetti 的问题,可以检查 pubspec.yaml 中是否添加了:
confetti: ^0.8.0
然后重新执行:
flutter pub get
如果还是不行,可以重启编辑器。编辑器偶尔像没睡醒,依赖明明装好了,它还非要装作没看见,经典软件行为。
2. import 导入报错
如果下面代码报错:
import 'package:confetti/confetti.dart';
通常有几种原因:
pubspec.yaml中没有添加依赖;- 没有执行
flutter pub get; - YAML 缩进错误;
- 包名写错;
- 编辑器没有刷新依赖。
其中 YAML 缩进最容易出问题。配置文件对空格的敏感程度,简直像在审讯键盘。
3. 点击按钮后没有彩纸动画
如果点击按钮后没有动画,可以检查:
- 是否创建了
ConfettiController; - 是否在页面中添加了
ConfettiWidget; ConfettiWidget是否绑定了正确控制器;- 是否调用了
_confettiController.play(); ConfettiWidget是否被其他组件遮挡;- 动画颜色是否和背景太接近。
本项目中使用 Stack 把 ConfettiWidget 放在页面上层,可以避免动画被普通组件挤走。
4. 彩纸动画只播放一次
如果彩纸动画只播放一次,连续点击后不重新播放,可以在播放前先调用:
_confettiController.stop();
_confettiController.play();
这样可以让动画重新开始。
5. 页面销毁时报错
如果页面销毁时出现控制器相关问题,可以检查是否在 dispose() 中释放:
_confettiController.dispose();
动画控制器用完必须释放,不然就像把灯一直开着还假装没人付电费。
6. 彩纸数量太多导致卡顿
如果动画播放时页面卡顿,可以减少以下参数:
numberOfParticles: 28,
emissionFrequency: 0.08,
彩纸数量越多,计算量越大。动画不是越多越高级,太多只会让设备开始怀疑人生。
7. 运行不到 OpenHarmony 设备
如果项目无法运行到 OpenHarmony 设备或模拟器,可以检查:
- Flutter for OpenHarmony 环境是否配置完成;
- 设备是否连接成功;
flutter devices是否能识别设备;- 是否执行了
flutter pub get; - 是否选择了正确的运行设备;
- 项目是否为 Flutter 项目,而不是原生鸿蒙项目。
如果 flutter devices 都识别不到设备,那就先处理环境问题,不要盯着彩纸代码看半小时。彩纸很无辜,至少这次大概率是。
十二、本文和原生鸿蒙项目的区别
本文是 Flutter for OpenHarmony 第三方库实践,不是 OpenHarmony 原生 ArkTS 项目。
主要区别如下:
| 对比项 | 本文写法 | 原生鸿蒙写法 |
|---|---|---|
| UI 技术 | Flutter | ArkUI |
| 主要语言 | Dart | ArkTS |
| 页面入口 | lib/main.dart | Index.ets |
| 依赖配置 | pubspec.yaml | oh-package.json5 |
| 依赖安装 | flutter pub get | ohpm install |
| 第三方库 | confetti | OpenHarmony 原生库 |
| 页面组件 | MaterialApp / Scaffold / ConfettiWidget | @Entry / @Component |
因此本文符合 Flutter for OpenHarmony 第三方库实践方向。
十三、总结
本篇完成了一个基于 confetti 的 Flutter for OpenHarmony 抽奖结果庆祝页面。项目通过 Flutter 第三方库实现彩纸庆祝动画,并结合随机抽奖逻辑展示中奖结果。
通过本次实践,我主要完成了以下内容:
- 创建 Flutter for OpenHarmony 项目;
- 在
pubspec.yaml中添加confetti依赖; - 使用
flutter pub get获取第三方库; - 在
lib/main.dart中引入confetti; - 使用
ConfettiController控制动画播放; - 使用
ConfettiWidget展示彩纸动画; - 使用
Random实现随机抽奖; - 使用
setState()更新中奖结果; - 使用
Stack叠放页面内容和彩纸动画; - 实现开始抽奖和重置结果功能;
- 将项目运行到 OpenHarmony 设备或模拟器中。
这个项目虽然只是一个基础抽奖页面,但完整展示了 Flutter for OpenHarmony 项目中第三方库的使用流程。
后续可以在这个基础上继续扩展,例如:
- 添加真实奖品图片;
- 添加抽奖次数限制;
- 添加中奖概率配置;
- 添加抽奖记录;
- 添加本地数据保存;
- 添加用户登录信息;
- 添加活动倒计时;
- 添加音效反馈;
- 添加分享中奖结果;
- 添加更复杂的粒子形状。
整体来看,confetti 可以帮助 Flutter 开发者快速实现庆祝动画。通过这个项目,可以理解 Flutter for OpenHarmony 中第三方库依赖配置、动画控制器使用和页面状态更新之间的基本关系。
更多推荐
所有评论(0)