{"id":31195645,"url":"https://github.com/davigmacode/flutter_chronograph","last_synced_at":"2026-06-23T04:31:14.572Z","repository":{"id":312640180,"uuid":"1048185283","full_name":"davigmacode/flutter_chronograph","owner":"davigmacode","description":"Lightweight reactive stopwatch/countdown for Flutter. ChronoGraph provides ValueListenable for stopwatch, timer, and DateTime countdown, plus a tiny ChronoView.","archived":false,"fork":false,"pushed_at":"2025-09-01T14:43:12.000Z","size":311,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-20T03:48:11.121Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://pub.dev/packages/chronograph","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davigmacode.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["davigmacode"],"patreon":null,"open_collective":null,"ko_fi":"davigmacode","tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":["https://www.buymeacoffee.com/davigmacode"]}},"created_at":"2025-09-01T04:16:11.000Z","updated_at":"2025-09-01T14:43:16.000Z","dependencies_parsed_at":"2025-09-01T05:55:52.685Z","dependency_job_id":null,"html_url":"https://github.com/davigmacode/flutter_chronograph","commit_stats":null,"previous_names":["davigmacode/flutter_chronograph"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/davigmacode/flutter_chronograph","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davigmacode%2Fflutter_chronograph","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davigmacode%2Fflutter_chronograph/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davigmacode%2Fflutter_chronograph/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davigmacode%2Fflutter_chronograph/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davigmacode","download_url":"https://codeload.github.com/davigmacode/flutter_chronograph/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davigmacode%2Fflutter_chronograph/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34675970,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-23T02:00:07.161Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-09-20T03:45:47.879Z","updated_at":"2026-06-23T04:31:14.524Z","avatar_url":"https://github.com/davigmacode.png","language":"Dart","funding_links":["https://github.com/sponsors/davigmacode","https://ko-fi.com/davigmacode","https://www.buymeacoffee.com/davigmacode"],"categories":[],"sub_categories":[],"readme":"[![Pub Version](https://img.shields.io/pub/v/chronograph)](https://pub.dev/packages/chronograph) ![GitHub](https://img.shields.io/github/license/davigmacode/flutter_chronograph) [![GitHub](https://badgen.net/badge/icon/buymeacoffee?icon=buymeacoffee\u0026color=yellow\u0026label)](https://www.buymeacoffee.com/davigmacode) [![GitHub](https://badgen.net/badge/icon/ko-fi?icon=kofi\u0026color=red\u0026label)](https://ko-fi.com/davigmacode)\n\nChronograph is a lightweight, reactive engine for stopwatch and countdown use‑cases in Flutter. It exposes a simple ValueListenable\u003cChrono\u003e (ChronoGraph) that widgets can listen to, with factory constructors for a classic stopwatch, a fixed‑duration timer, and a countdown to a specific DateTime. A tiny ChronoView widget makes UI binding trivial while keeping you in full control of the rendering.\n\n## Features\n\n- Reactive engine: expose `ValueListenable\u003cChrono\u003e` for easy UI updates\n- Three modes: `stopwatch`, `timer(duration)`, `countdown(date)`\n- Tiny view helper: `ChronoView` wraps `ValueListenableBuilder\u003cChrono\u003e`\n- Provider helper: `ChronoProvider` manages lifetime and exposes a shared graph\n- Configurable tick `interval` and display `level` (days/hours/minutes/seconds)\n- Optional `autostart` and `onCompleted` callback for countdowns/timers\n- Immutable `Chrono` value with padded strings and total time helpers\n- Pure Dart implementation — no platform channels, test‑friendly\n\n## Usage\n\nFor a complete usage, please see the [example](https://davigmacode.github.io/flutter_chronograph).\n\nTo read more about classes and other references used by `chronograph`, see the [API Reference](https://pub.dev/documentation/chronograph/latest/).\n\n### Stopwatch (count up)\n\n```dart\nfinal graph = ChronoGraph.stopwatch(\n  level: ChronoLevel.seconds,\n  interval: const Duration(seconds: 1),\n  autostart: false, // call start() manually\n);\n\n// Control\ngraph.start();\ngraph.pause();\ngraph.reset();\n\n// Build UI\nChronoView(\n  graph: graph,\n  builder: (context, info, _) =\u003e Text(\n    '${info.paddedMinutes}:${info.paddedSeconds}',\n  ),\n);\n```\n\n### Timer (fixed duration)\n\n```dart\nfinal timer = ChronoGraph.timer(\n  duration: const Duration(seconds: 10),\n  autostart: true,\n  onCompleted: () =\u003e debugPrint('Timer completed'),\n);\n\nChronoView(\n  graph: timer,\n  builder: (context, info, _) =\u003e Text(\n    '${info.paddedMinutes}:${info.paddedSeconds}',\n  ),\n);\n```\n\n### Countdown to a DateTime\n\n```dart\nfinal countdown = ChronoGraph.countdown(\n  date: DateTime(2025, 1, 1), // target date\n  autostart: true,\n  onCompleted: () =\u003e debugPrint('Happy New Year!'),\n);\n\nChronoView(\n  graph: countdown,\n  builder: (context, info, _) =\u003e Text(\n    '${info.paddedMinutes}:${info.paddedSeconds}',\n  ),\n);\n```\n\n### Using ChronoProvider and ChronoView.of\n\nWrap a subtree with `ChronoProvider` to manage a shared graph and dispose it automatically. Use a `Builder` (or a separate widget) so the `context` passed to `ChronoView.of` is below the provider.\n\n```dart\nreturn ChronoProvider(\n  graph: ChronoGraph.stopwatch(level: ChronoLevel.seconds),\n  child: Builder(\n    builder: (context) =\u003e Column(\n      children: [\n        ChronoView.of(\n          context,\n          builder: (ctx, info, _) =\u003e Text('${info.paddedMinutes}:${info.paddedSeconds}'),\n        ),\n      ],\n    ),\n  ),\n);\n```\n\nAlternatively, use the builder convenience:\n\n```dart\nChronoProvider.builder(\n  graph: ChronoGraph.timer(duration: const Duration(seconds: 10)),\n  builder: (ctx, graph, _) =\u003e Column(\n    children: [\n      ChronoView(\n        graph: graph,\n        builder: (ctx, info, __) =\u003e Text(\n          '${info.paddedMinutes}:${info.paddedSeconds}',\n        ),\n      ),\n      Row(\n        children: [\n          TextButton(onPressed: graph.start, child: const Text('Start')),\n          TextButton(onPressed: graph.pause, child: const Text('Pause')),\n          TextButton(onPressed: graph.reset, child: const Text('Reset')),\n        ],\n      )\n    ],\n  ),\n);\n```\n\nNote: `ChronoProvider` owns the provided graph by default and disposes it on unmount. To keep the graph alive (managed externally), pass `maintainLifetime: false`:\n\n```dart\nChronoProvider(\n  graph: externalGraph,\n  maintainLifetime: false,\n  child: Builder(\n    builder: (context) =\u003e ChronoView.of(\n      context,\n      builder: (ctx, info, _) =\u003e Text(info.paddedSeconds),\n    ),\n  ),\n)\n```\n\n### Callbacks on ChronoGraph\n\nYou can observe lifecycle events with callbacks:\n\n```dart\nfinal g1 = ChronoGraph.stopwatch(\n  onStart: (c) =\u003e debugPrint('Started at ${c.inSeconds}s'),\n  onPause: (c) =\u003e debugPrint('Paused at ${c.inSeconds}s'),\n  onReset: (c) =\u003e debugPrint('Reset to ${c.inSeconds}s'),\n);\n\nfinal g2 = ChronoGraph.timer(\n  duration: const Duration(seconds: 10),\n  onStart: (c) =\u003e debugPrint('Timer start: ${c.inSeconds}s remaining'),\n  onPause: (c) =\u003e debugPrint('Timer pause: ${c.inSeconds}s remaining'),\n  onReset: (c) =\u003e debugPrint('Timer reset: ${c.inSeconds}s remaining'),\n  onCompleted: () =\u003e debugPrint('Timer completed'),\n);\n```\n\n## Sponsoring\n\n\u003ca href=\"https://www.buymeacoffee.com/davigmacode\" target=\"_blank\"\u003e\u003cimg src=\"https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png\" alt=\"Buy Me A Coffee\" height=\"45\"\u003e\u003c/a\u003e\n\u003ca href=\"https://ko-fi.com/davigmacode\" target=\"_blank\"\u003e\u003cimg src=\"https://storage.ko-fi.com/cdn/brandasset/kofi_s_tag_white.png\" alt=\"Ko-Fi\" height=\"45\"\u003e\u003c/a\u003e\n\nIf this package or any other package I created is helping you, please consider to sponsor me so that I can take time to read the issues, fix bugs, merge pull requests and add features to these packages.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavigmacode%2Fflutter_chronograph","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavigmacode%2Fflutter_chronograph","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavigmacode%2Fflutter_chronograph/lists"}