my_timer 2.0.0
my_timer: ^2.0.0 copied to clipboard
A drift-free Flutter timer widget. Count up/down with pause/resume/reset/seek, format presets, background resync, and zero external dependencies.
Changelog #
All notable changes to this project are documented in this file. The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
2.0.0 - 2026-05-31 #
TL;DR —
my_timeris now drift-free, dependency-free, and backgrounding-aware. The controller gainspause/resume/reset/seek/add/subtractplus state-inspection getters. The widget gains a modernDuration-typed API, format presets, and a custom formatter. Your 1.x code keeps compiling via deprecated aliases.
Highlights #
| Drift-free engine | Elapsed time is reconciled against the wall clock each tick — long-running timers stay accurate. |
| Background resync | WidgetsBindingObserver recomputes elapsed time on AppLifecycleState.resumed. |
| Zero dependencies | provider is gone. State runs on ValueNotifier + ValueListenableBuilder. |
| Real controller | pause, resume, reset, seek, add, subtract + isRunning / isPaused / isCompleted / elapsed / remaining getters. |
| Format presets | auto, seconds, minutesSeconds, hoursMinutesSeconds, minutesSecondsMillis, daysHoursMinutesSeconds. |
Breaking changes #
Existing 1.x code keeps compiling thanks to
@Deprecatedaliases. The only signature break is thebuilderparameter — rename your old builder tolegacyBuilderand you're done. See the migration table in README.md.
providerremoved. State is now managed internally withValueNotifierand rebuilt viaValueListenableBuilder. If any code was reaching into the package's internalMyTimerProvider, that type no longer exists — useMyTimerControllerinstead.tickInSecondis now purely a UI refresh cadence. In 1.x it was incorrectly used as a "step size," so passingDuration(milliseconds: 500)made the timer count two seconds per real second. The new implementation tracks real time, so the displayed value is always accurate regardless of tick interval.tickInSecondis deprecated in favor oftickInterval.- Default text format changed from
HH:MM:SStoauto(picksmm:ssunder an hour,hh:mm:ssunder a day,dd:hh:mm:ssbeyond). Passformat: TimerFormat.hoursMinutesSecondsto keep the old behavior. - The
builderparameter now uses positionalDurationarguments:(BuildContext, Duration remaining, Duration elapsed) => Widget. The 1.x-style namedint remainingTimebuilder is available under the new namelegacyBuilder.
Added — controller #
pause()— stops the ticker while preserving elapsed time.resume()— alias forstart(); reads more naturally afterpause().reset()— zeroes elapsed time without starting the timer.seek(Duration)— jumps to a position, preserving running state.add(Duration)/subtract(Duration)— shift elapsed time by a delta.isRunning,isPaused,isCompleted,isAttached— state getters.elapsed,remaining— read current values without a callback.
Added — widget #
direction: TimerDirection(countUp/countDown) — modern, type-safe replacement forisIncrementing.duration: Duration— modernDuration-typed total. OldstartTimerInSeconds/endTimerInSecondsints remain as deprecated aliases.tickInterval: Duration— modern replacement fortickInSecond.autoStart: bool— setfalseto keep the timer paused untilcontroller.start().onTick/onComplete— now available directly on the widget in addition to the controller.resyncOnResume: bool— opt-in/opt-out of the lifecycle resync hook.
Added — formatting #
TimerFormatenum with six presets:auto,seconds,minutesSeconds,hoursMinutesSeconds,minutesSecondsMillis,daysHoursMinutesSeconds.formatter: String Function(Duration)— custom formatter escape hatch.formatDuration(Duration, TimerFormat)— exposed as a public helper for use outside the widget.
Added — engine #
- New internal
MyTimerEngine(drift-free, tick-driven + wall-clock reconciled). Single source of truth for any UI built on the controller. MyTimerEngine.elapsedis aValueNotifier<Duration>— power-users can subscribe directly withValueListenableBuilderif they need more control than the widget provides.
Fixed #
- Calling
controller.start()before the widget mounted threw aLateInitializationError. Method calls on an unattached controller are now safe no-ops. stop()followed bystart()reset elapsed time inconsistently. The newpause()/resume()pair preserves state cleanly.- The 1.0.1 widget tests asserted
'00:00'while the widget actually rendered'00:00:00'. Tests now pass — all 20 of them.
Migration cheatsheet #
| 1.x | 2.x |
|---|---|
isIncrementing: true |
direction: TimerDirection.countUp |
isIncrementing: false |
direction: TimerDirection.countDown |
startTimerInSeconds / endTimerInSeconds |
duration: Duration(...) |
tickInSecond |
tickInterval |
builder: ({context, remainingTime}) {...} |
legacyBuilder: ({context, remainingTime}) {...} |
1.0.1 - 2025-03-20 #
Added #
- Builder support — access to
remainingTimeInSecondsandBuildContextfor flexible UI updates. - Provider integration — refactored
MyTimerto useProviderfor state management. - New callbacks — additional callbacks on
MyTimerController. - Code refactor — improved structure and readability.
Fixed #
- Multiple timers could start without stopping the previous one.
- Reduced unnecessary
setStatecalls.
1.0.0 Initial release #
MyTimerwidget with incrementing and decrementing modes.- Custom tick interval via
tickInSecond. - Start / stop / complete callbacks via
MyTimerController. - Custom child widget or default time display.
- Styling options for the time display text.
