在鸿蒙平台上做 Flutter 应用,进度加载组件是绕不开的基础组件。CircularProgressIndicator 虽小,但用好了能提升用户体验,用错了反而会出各种诡异问题。本文基于 Flutter HarmonyOS 6.0 和 nova12u 真机验证,从基础模式、参数配置、动画驱动到与 ArkTS 原生组件的对比,完整梳理该组件的正确姿势。
核心参数快速上手
CircularProgressIndicator 的关键参数如下:
- value:传入 null 为不确定模式(无限旋转),传入 0.0~1.0 为确定模式(圆弧按比例填充)。
- strokeWidth:圆弧粗细,默认 4.0 逻辑像素。
- color:圆弧颜色,默认取自主题 ColorScheme.primary。
- backgroundColor:底圈颜色,仅在确定模式下有意义。
- strokeCap:圆弧端头形状,StrokeCap.round 可让端头变圆。
- semanticsLabel:无障碍标签。
不确定模式 vs 确定模式
不确定模式适合网络请求、页面初始化等耗时未知的场景。代码示例:- CircularProgressIndicator(),
- CircularProgressIndicator(color: Colors.blue, strokeWidth: 4.0),
- CircularProgressIndicator(color: Colors.red, strokeWidth: 6.0),
复制代码
确定模式必须配合 AnimationController 驱动,否则进度静态不动。我习惯在 StatefulWidget 中混入 TickerProviderStateMixin 来管理动画控制器:- late final AnimationController _progressCtrl;
- @override
- void initState() {
- super.initState();
- _progressCtrl = AnimationController(
- vsync: this,
- duration: const Duration(seconds: 3),
- );
- _progressCtrl.addListener(() => setState(() {}));
- }
复制代码 注意 dispose 中务必释放控制器,否则控制台会报 Ticker 泄漏警告。AnimationController 的 value 范围是 0.0~1.0,与 CircularProgressIndicator 的 value 天然对齐,无需额外映射。
样式对比与容器约束
不同参数组合效果差异明显:- const CircularProgressIndicator(),
- const CircularProgressIndicator(strokeWidth: 12),
- CircularProgressIndicator(
- value: 0.7,
- strokeWidth: 6,
- color: Colors.blue,
- backgroundColor: Colors.blue.shade50,
- ),
复制代码
踩坑点:strokeWidth 不能超过容器最短边的一半。内部绘制时圆弧半径 = (size.shortestSide - strokeWidth) / 2,若 strokeWidth 大于 shortestSide,半径变为负数,圆弧边缘被截断。最佳实践是让 strokeWidth 不超过容器最短边的 1/3。
百分比文字与 Stack 布局
将百分比文字叠在进度圈中间,推荐用 Stack 配合 SizedBox 约束容器尺寸:- SizedBox(
- width: 120,
- height: 120,
- child: Stack(
- alignment: Alignment.center,
- children: [
- CircularProgressIndicator(
- value: _customValue,
- strokeWidth: 10,
- color: Colors.teal,
- backgroundColor: Colors.teal.shade100,
- ),
- Text(
- '${(_customValue * 100).toInt()}%',
- style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
- ),
- ],
- ),
- ),
- Slider(
- value: _customValue,
- onChanged: (v) => setState(() => _customValue = v),
- ),
复制代码 若不给 Stack 外层设固定尺寸,CircularProgressIndicator 会撑满父容器,文字移到卡片正中央而非进度圈中心。
动画曲线与自定义玩法
默认 AnimationController 使用线性插值,感觉偏硬。可改用 CurvedAnimation 实现缓入缓出效果:- final animation = CurvedAnimation(
- parent: _progressCtrl,
- curve: Curves.easeInOut,
- );
- // 使用 animation.value 替代 _progressCtrl.value
复制代码
还可利用 AnimatedBuilder 在进度变化时同时改变颜色:- AnimatedBuilder(
- animation: _progressCtrl,
- builder: (context, child) {
- return CircularProgressIndicator(
- value: _progressCtrl.value,
- color: Color.lerp(Colors.blue, Colors.red, _progressCtrl.value),
- strokeWidth: 6,
- );
- },
- )
复制代码
与 ArkTS 原生组件对比
鸿蒙 ArkTS 提供了 LoadingProgress(不确定模式)和 Progress(确定模式)两个独立组件。对比感受:
1. Flutter 将两种模式整合于同一组件,通过 value 区分,调用更统一;ArkTS 拆成两个组件,语义更清晰但需记忆两个类型名。
2. 自定义能力上 Flutter 更灵活,可调节 strokeCap、backgroundColor,还可配合 AnimatedBuilder 实现复杂过渡;ArkTS 的 Progress 自定义度较低,复杂效果需用 Canvas 自绘。
3. 动画流畅度方面,在 nova12u 上实测 Flutter 旋转更丝滑,ArkTS 的 LoadingProgress 在页面切换时偶有卡顿。
4. 布局方式不同:Flutter 的 CircularProgressIndicator 默认占满可用空间,需用 SizedBox 约束;ArkTS 的组件默认有固定尺寸。
5. ArkTS 的 Progress 内置动画过渡(设 value 后自动补间),Flutter 需手动驱动 AnimationController。
其他注意事项
- strokeCap 在不确定模式下无效,因为闭合环没有端头。确定模式下若 value 接近 1.0(如 0.99),round 会导致起点终点重叠处凸起,建议 value 到 1.0 时隐藏进度圈或显示完成图标。
- Theme 影响:Flutter 3.10+ 默认启用 Material 3,CircularProgressIndicator 使用 indicatorColor 和 trackColor 语义 Token,样式与 Material 2 不同。建议显式传 color 和 backgroundColor,以保证跨主题一致性:- CircularProgressIndicator(
- color: Theme.of(context).colorScheme.primary,
- backgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest,
- )
复制代码 - 避免重复点击:在动画未完成时再次触发 forward() 会导致进度条跳动。可先判是否已完成,完成则 reverse(),否则从头开始。
总结
CircularProgressIndicator 看似简单,但要做得精致需注意容器尺寸约束、动画驱动方式、底层圈选择、跨主题适配等细节。对比 ArkTS 原生组件,Flutter 提供更高的灵活性和流畅度,适合追求统一多端体验的鸿蒙应用开发。一步到位的方法是在项目里封装一个通用加载组件,统一管理动画控制器和状态,避免每次手动编写重复代码。 |