my_timer 2.0.0 copy "my_timer: ^2.0.0" to clipboard
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.

my_timer #

Pub Version Pub Likes Pub Popularity Pub Points License: MIT Platform

A drift-free, dependency-free Flutter timer widget that handles count-up and count-down out of the box, with proper pause / resume / reset / seek, format presets, custom builders, and automatic resync after the app returns from the background.


Table of contents #


Why my_timer? #

my_timer most other packages
Count-up & count-down in one widget yes usually two separate widgets
pause() / resume() / reset() / seek() / add() / subtract() yes partial
Drift-free, wall-clock anchored yes rarely
Resyncs after app backgrounding yes rarely
Built-in format presets (mm:ss, hh:mm:ss, etc.) yes usually manual
Zero external dependencies yes many pull in provider / rxdart
Backwards-compatible 1.x API yes

Screenshots #

Countdown
Countdown demo
pause · resume · reset · ±10s
Stopwatch
Stopwatch demo
count-up, millisecond precision
Custom builder
Custom builder demo
progress bar + timer text
Format presets
Format presets demo
auto, mm:ss, hh:mm:ss, mm:ss.mmm, ...

Want to see it live? Run the example app — all four scenarios above are wired up as tabs.


Install #

dependencies:
  my_timer: ^2.0.0
flutter pub add my_timer

Quick start #

Count-down #

import 'package:my_timer/my_timer.dart';

MyTimer(
  duration: const Duration(minutes: 5),
  direction: TimerDirection.countDown,
  format: TimerFormat.minutesSeconds,
  onComplete: () => debugPrint('done!'),
)

Count-up #

MyTimer(
  duration: const Duration(minutes: 1),
  direction: TimerDirection.countUp,
  format: TimerFormat.minutesSeconds,
)

Programmatic control #

final controller = MyTimerController(
  onTick: (remaining) => print('$remaining left'),
  onComplete: () => print('done!'),
);

MyTimer(
  controller: controller,
  duration: const Duration(minutes: 10),
  direction: TimerDirection.countDown,
  autoStart: false,
);

// Anywhere in your app:
controller.start();
controller.pause();
controller.resume();
controller.reset();
controller.seek(const Duration(minutes: 2));
controller.add(const Duration(seconds: 30));
controller.subtract(const Duration(seconds: 30));

// Inspect state:
controller.isRunning;
controller.isPaused;
controller.isCompleted;
controller.elapsed;
controller.remaining;

Custom rendering with builder #

MyTimer(
  duration: const Duration(minutes: 25),
  direction: TimerDirection.countDown,
  builder: (context, remaining, elapsed) {
    final total = const Duration(minutes: 25).inMilliseconds;
    return Column(
      children: [
        LinearProgressIndicator(value: elapsed.inMilliseconds / total),
        Text('${remaining.inMinutes}:${(remaining.inSeconds % 60).toString().padLeft(2, '0')}'),
      ],
    );
  },
)

Custom formatter #

MyTimer(
  duration: const Duration(minutes: 5),
  formatter: (d) => '${d.inSeconds} seconds left',
)

Format presets #

TimerFormat Output for 93s Output for 3725s
auto 01:33 01:02:05
seconds 93 3725
minutesSeconds 01:33 62:05
hoursMinutesSeconds 00:01:33 01:02:05
minutesSecondsMillis 01:33.000 62:05.000
daysHoursMinutesSeconds 00:00:01:33 00:01:02:05

API reference #

MyTimer #

Parameter Type Default Description
duration Duration? 120s Total duration the timer runs for.
direction TimerDirection countUp countUp or countDown.
tickInterval Duration 1s UI refresh cadence. Doesn't affect clock accuracy.
autoStart bool true Start ticking on mount.
controller MyTimerController? null Remote control.
builder Widget Function(BuildContext, Duration remaining, Duration elapsed)? null Custom renderer.
legacyBuilder Widget Function({BuildContext context, int remainingTime})? null 1.x-style builder, for migration.
child Widget? null Static replacement widget.
style TextStyle? Style for default text.
format TimerFormat auto Built-in format preset.
formatter String Function(Duration)? null Custom formatter.
resyncOnResume bool true Recompute on AppLifecycleState.resumed.
onTick void Function(Duration)? null Per-tick callback.
onComplete VoidCallback? null Fires once at completion.

MyTimerController #

Method / getter Description
start() / resume() Start or resume.
pause() / stop() Pause, preserving elapsed time.
reset() Zero elapsed time.
seek(Duration) Jump to a position.
add(Duration) / subtract(Duration) Adjust elapsed time.
isRunning / isPaused / isCompleted / isAttached State checks.
elapsed / remaining Current values.
getTimer() 1.x compatibility — returns the displayed value.
onTick / onComplete Callback fields.

Migrating from 1.x #

The 1.x API is preserved under deprecated names — your existing code keeps compiling, but you should switch to the new names:

1.x 2.x
isIncrementing: true direction: TimerDirection.countUp
isIncrementing: false direction: TimerDirection.countDown
startTimerInSeconds / endTimerInSeconds duration
tickInSecond tickInterval
builder: ({context, remainingTime}) {...} legacyBuilder: ({context, remainingTime}) {...}

The biggest under-the-hood improvement: the timer is now drift-free and survives app backgrounding. Previously, tickInSecond of 500ms would cause the displayed seconds to count twice as fast as real time — that's now fixed. See the full CHANGELOG for details.


Contributing #

Issues and PRs welcome at https://github.com/Priyanshu-techind/my_timer.

License #

MIT

5
likes
160
points
7
downloads
screenshot

Documentation

API reference

Publisher

verified publishertechind.co

Weekly Downloads

A drift-free Flutter timer widget. Count up/down with pause/resume/reset/seek, format presets, background resync, and zero external dependencies.

Repository (GitHub)
View/report issues

Topics

#timer #countdown #stopwatch #widget #duration

License

MIT (license)

Dependencies

flutter

More

Packages that depend on my_timer