briankariuki/number_flow_flutter
GitHub: briankariuki/number_flow_flutter
一款 Flutter 动画数字组件,提供逐位滚动过渡、多格式化和丰富的动态视觉效果。
Stars: 7 | Forks: 0
# number_flow_flutter

为 Flutter 提供精美的动画数字。当值发生变化时,每个数字都会像里程表一样滚动
到新位置 —— 具备区域感知格式化、随数字增加或减少的平滑
进入/退出过渡、可选的 iOS 风格弹簧效果,以及基于速度的动态模糊。
`NumberFlow` 处理数字(计数器、货币、百分比、紧凑和
科学计数法);`TimeFlow` 处理时钟、倒计时和秒表。
## 功能
- **逐位滚动** —— 仅发生变化的数字会进行动画;其余数字
保持不动。
- **区域感知格式化** —— 支持十进制、货币、百分比、紧凑 (`1.2K`)、
科学 (`1.2×10³`) 和工程计数法,具备正确的分组、
数字系统以及 RTL。
- **平滑重排** —— 当数字增加或减少时,数字会滑动到新列,并且容器会调整大小;
进入的数字滚入,退出的数字淡出。
- **曲线或弹簧** —— 默认使用前置缓动曲线,或可选择启用
具有过冲和中断滚动时速度交接的类 iOS 弹簧效果。
- **动态模糊** —— 滚动时(垂直和/或水平)可选基于速度的模糊,
完全可调。
- **Scrub 模式** —— 从 `ValueListenable`(滑块、
图表 scrubber、手势)驱动显示,具有高刷新率且无需每次 tick 重新构建。
- **无障碍** —— 暴露纯文本语义标签;遵循平台
减弱动态效果设置。
## 安装说明
添加到您的 `pubspec.yaml`:
```
dependencies:
number_flow_flutter: ^0.0.1
```
然后:
```
import 'package:number_flow_flutter/number_flow_flutter.dart';
```
## 快速开始
```
NumberFlow(value: 1234)
```
就是这样 —— 每当重新构建时 `value` 发生变化,数字就会动画过渡到新的
数字。
## NumberFlow
### 格式化
传递 `NumberFlowFormat` 来控制值的渲染方式:
```
// Currency
NumberFlow(
value: 1234.56,
format: const NumberFlowFormat.currency(currencyCode: 'USD', symbol: r'$'),
locale: 'en_US',
)
// Percent (0.0–1.0 → 0%–100%)
NumberFlow(value: 0.42, format: const NumberFlowFormat.percent())
// Compact — 1,234,567 → 1.2M
NumberFlow(
value: 1234567,
format: const NumberFlowFormat.compact(maxFraction: 1),
)
// Scientific — 12345 → 1.23×10⁴ (the exponent renders as a superscript)
NumberFlow(
value: 12345,
format: const NumberFlowFormat.scientific(maxFraction: 2),
)
// Engineering — exponent is always a multiple of 3
NumberFlow(value: 1500000, format: const NumberFlowFormat.engineering())
// Plain decimal with fraction bounds
NumberFlow(
value: 3.14159,
format: const NumberFlowFormat.decimal(minFraction: 2, maxFraction: 2),
)
```
每种格式都接受一个 `sign` (`SignDisplay.auto`、`always`、`exceptZero`、
`negative`、`never`) 来控制何时显示 `+`/`-` 号。
### 前缀、后缀和趋势
```
NumberFlow(
value: score,
prefix: '~',
suffix: ' pts',
// Direction the digits roll. Omit to auto-detect from the value change.
trend: 1, // 1 up, -1 down, 0 shortest path
)
```
如需自定义规则,请传递 `trendFn: (prev, next) => ...` 而不是 `trend`。
### 样式和布局
```
NumberFlow(
value: 1234,
style: const TextStyle(fontSize: 48, fontWeight: FontWeight.w600),
textAlign: TextAlignValue.end,
direction: Direction.auto, // ltr / rtl / auto
tabularNums: true, // uniform digit width — no horizontal shift
)
```
### 动画
默认情况下,滚动使用固定时长的缓动曲线。可以对其进行自定义,或者选择
启用弹簧效果:
```
// Custom curve timing
NumberFlow(
value: v,
spinTiming: const TimingConfig(
duration: Duration(milliseconds: 700),
curve: NumberFlowCurve(),
),
)
// iOS-like spring (overshoot + velocity hand-off on interrupt)
NumberFlow(value: v, spring: NumberFlowSpring.ios)
// Custom spring
NumberFlow(
value: v,
spring: const NumberFlowSpring(mass: 1, stiffness: 170, damping: 20),
)
```
其他动画参数:
| 属性 | 描述 |
| --- | --- |
| `animated` | 主开关;为 `false` 时直接切换,无动画。 |
| `respectMotionPreference` | 为 `true`(默认)时,遵循 OS 的减弱动态效果设置。 |
| `transformTiming` | 水平重排的计时(数字滑动到新列)。 |
| `opacityTiming` | 进入/退出淡入淡出的计时。 |
| `continuous` | 里程表模式:在进位时未更改的低位数字会额外旋转一整圈(需要有方向的 `trend`)。 |
| `stagger` | 每个数字从左到右的延迟(高位数字在前)。 |
| `wheelSpacing` | 滚轮字形之间的垂直间距(× 行高)。 |
| `mask` | 数字窗口的顶部/底部边缘淡出效果。 |
### 动态模糊和挤压
滚动较快时数字会发生模糊和压缩,然后在即将停止时恢复锐利并
弹回:
```
NumberFlow(
value: v,
motionBlur: 8, // max blur sigma (px) at peak speed
motionBlurVelocityScale: 6, // lower = blur ramps in sooner / stronger
motionBlurThreshold: 0.5, // roll speed below which no blur is applied
digitSquish: 0.3, // horizontal squish: scaleX → 1 - 0.3 at peak
)
```
模糊是对整个数字应用柔和的**磨砂**模糊,在快速滚动时会增强,
并在静止时完全消退变清晰 —— 因此只有移动的数字
才会模糊,已经静止的数字永远不会模糊。
`digitSquish` 在峰值速度时会将滚动数字水平压缩(至 `1 - digitSquish`),
并在其静止时弹回至全宽,从而产生一种干脆利落、
运动被压缩的感觉。它只影响实际正在移动的数字。
使用 `NumberFlowSpring.ios` 时,动态模糊、数字挤压和 stagger 都会自动
默认为 iOS 预设值 —— 显著的模糊(峰值 sigma 为 `4`,速度
缩放为 `4`)、`0.3` 的挤压以及从左到右的 stagger。传递显式值可以
覆盖其中任意一项。
### 逐位限制
限制各个滚轮(例如,个位数只能在 0–5 之间变化的评分):
```
NumberFlow(
value: rating,
// position 0 = ones, 1 = tens, …
digits: {0: const DigitConstraint(max: 5)},
)
```
### Scrub 模式
对于更新速度超过 widget 重新构建速度的值(滑块拖动、
图表 scrubber),可以通过 `ValueListenable` 驱动它。widget 每次 tick 都会重新格式化,
并且仅滚动发生变化的数字,除非数字位数发生变化,否则无需完全重新构建即可重绘:
```
final scrub = ValueNotifier(0);
NumberFlow.scrub(
value: scrub,
format: const NumberFlowFormat.currency(currencyCode: 'USD', symbol: r'$'),
tabularNums: true, // steady width for smooth scrubbing
)
// later, from a gesture:
scrub.value = newValue;
```
## TimeFlow
动画时钟、倒计时或秒表。它是通过 prop 驱动的 —— 父级拥有
tick 的控制权(`Timer`、`Ticker` 或 stream)并推送新值。
```
// Explicit fields
TimeFlow(hours: 12, minutes: 30, seconds: 45)
// From a Duration (countdown / stopwatch)
TimeFlow.duration(
elapsed,
showHours: elapsed.inHours > 0,
showCentiseconds: true,
)
// From a DateTime (wall clock)
TimeFlow.dateTime(DateTime.now(), showSeconds: true, is24Hour: false)
```
`TimeFlow` 接受与 `NumberFlow` 相同的动画属性
(`spinTiming`、`spring`、`motionBlur`、`wheelSpacing`、`mask` 等)。
## 回调
两个 widget 都接受 `onAnimationsStart` 和 `onAnimationsFinish`:
```
NumberFlow(
value: v,
onAnimationsStart: () => debugPrint('rolling'),
onAnimationsFinish: () => debugPrint('settled'),
)
```
## 无障碍
每个 widget 都会暴露格式化后值的纯文本语义标签(例如
`"$1,234.56"`,科学计数法为 `"1.2E4"`),因此屏幕阅读器会读出
数字,而不是单独滚动的字形。当平台减弱动态效果
设置开启时,过渡会直接切换,而不是滚动(除非
`respectMotionPreference: false`)。
## 示例
查看 [`example/`](example/) 应用以获取交互式图库:计数器、
货币、百分比、紧凑型、科学计数法、实时秒表、scrub 滑块,以及
针对每个动画参数的滑块。
## 许可证
请参阅代码库中的 `LICENSE` 文件。
标签:Dart, Flutter, UI组件, 动画效果, 数字滚动, 移动开发