flutter_style_expiring_todos
An analyzer plugin that enforces Flutter-style TODO comments with optional
expiry conditions — // TODO(username)[condition]: message — and reports TODOs
when their conditions are met.
Overview
TODO comments can outlive their relevance. Add a date or version condition to a TODO, and this plugin reports it in your IDE and CI as soon as that condition is met.
The plugin extends the official
flutter_style_todos
lint. Existing conditionless Flutter-style TODOs continue to work, while the
extended form adds expiry conditions for dates, the project version,
dependencies, and the Dart and Flutter SDKs.
Diagnostics
All diagnostics are enabled by default and can be configured individually.
| Rule | Reports |
|---|---|
flutter_style_expiring_todos |
TODO comments that do not follow the expected format |
expired_todo |
Well-formed TODOs whose date or version condition has been met |
unresolved_todo_condition |
TODOs whose condition cannot be resolved |
flutter_style_expiring_todos and unresolved_todo_condition report defects
in the comment itself. expired_todo reacts to time and package state, so it
can be disabled when those changes should not fail CI.
Install
The plugin requires Dart 3.11+ / Flutter 3.38+.
Add it under the top-level plugins: section of analysis_options.yaml, then
disable the official flutter_style_todos lint. The official lint rejects the
conditional syntax, whereas this plugin accepts both forms.
Minimal configuration
plugins:
flutter_style_expiring_todos: 0.1.0
linter:
rules:
flutter_style_todos: false
Optional: configure individual diagnostics
Use this complete alternative configuration when you need to control
diagnostics individually. Do not add it alongside the minimal
flutter_style_expiring_todos: 0.1.0 declaration above.
plugins:
flutter_style_expiring_todos:
version: 0.1.0
diagnostics:
flutter_style_expiring_todos: true
expired_todo: true
unresolved_todo_condition: true
linter:
rules:
flutter_style_todos: false
Set a diagnostic to false to disable it. Restart the analysis server after
adding or changing plugin configuration (for example, Dart: Restart Analysis
Server in VS Code).
TODO syntax
Two forms are accepted:
// TODO(username): message <- Flutter style; never expires
// TODO(username)[condition, ...]: message <- one or more expiry conditions
- A username must match
[a-zA-Z0-9][-a-zA-Z0-9.]*. - In the conditional form,
)and[must be adjacent. Conditions are separated by,, optionally followed by one space; no whitespace is allowed inside a condition. - Either form may end with an issue URL:
, https://....
// Valid:
// TODO(alice): migrate to the new API
// TODO(alice)[2026/12/31]: remove this fallback
// TODO(alice)[>=2.0.0]: drop this shim once we ship 2.0
// TODO(alice)[provider@>=7.0.0]: use the new provider API
// TODO(alice)[2026/12/31, provider@>=7.0.0]: whichever comes first
// TODO(alice)[2026/12/31]: track issue, https://github.com/org/repo/issues/42
// Invalid (flutter_style_expiring_todos):
// TODO: no username
// Todo(alice): lowercase keyword
// TODO(alice) missing colon
// TODO(alice) [2026/12/31]: space before bracket
// TODO(alice)[< 1.0.0]: unsupported operator
// TODO(alice)[2026/2/3]: date not zero-padded
// TODO(alice)[2026/02/30]: date does not exist
// TODO(alice)[2026/12/31, 2027/01/01]: two dates are not allowed
Conditions
Conditions are written inside [...]. Only > and >= comparison operators
are accepted. Versions use semver: 1 and 5.3 mean 1.0.0 and 5.3.0, and
pre-releases sort below their corresponding release.
| Condition | Syntax | Resolves against | Reports when |
|---|---|---|---|
| Date | [YYYY/MM/DD] |
Local time, at day granularity | The day after the date |
| Project version | [>=1.0.0], [>2] |
Top-level version: in pubspec.yaml |
The project reaches the version |
| Dependency version | [name@>=version] |
Resolved version in pubspec.lock |
The dependency reaches the version |
| Dart SDK | [sdk@>=version] |
Lower bound of environment.sdk |
The lower bound reaches the version |
| Flutter SDK | [flutter@>=version] |
Lower bound of environment.flutter |
The lower bound reaches the version |
Multiple conditions can be separated by ,. Each is evaluated independently
and can produce its own diagnostic. A date and a project-version condition may
each appear at most once; conditions for different dependencies may be
repeated.
Evaluation details
- A date is valid on its due date and expires on the following local day.
- Version conditions use strict (
>) or inclusive (>=) semver precedence. - SDK and Flutter environment constraints are ranges. Their lower bounds are
used:
^3.11.0resolves to3.11.0and>=3.10.0 <4.0.0resolves to3.10.0. - An unbounded-below environment constraint such as
anyhas no lower bound.
Unresolved conditions
unresolved_todo_condition reports the following cases:
- A dependency is absent from
pubspec.lock. - A project-version condition has no top-level
version:. - An SDK or Flutter condition has no matching environment entry or no lower bound.
Files outside a pub package (with no ancestor pubspec.yaml) silently skip
version conditions.
Compatibility and scope
flutter_style_expiring_todos is compatible with the official
flutter_style_todos rule, except that it additionally accepts
// TODO(username)[condition, ...]: message. Disable the official lint to
avoid it reporting that extended form.
The design is based on
eslint-plugin-unicorn/expiring-todo-comments,
using pub equivalents: resolved dependency versions from pubspec.lock and
SDK lower bounds from pubspec.yaml. Its [+package] and [-package]
conditions are not currently implemented.
CI
Plugin diagnostics use INFO severity, matching flutter_style_todos. Make
them fail CI with:
dart analyze --fatal-infos
If date or version expiry should not fail CI, replace the plugin declaration
with a configuration that disables expired_todo:
plugins:
flutter_style_expiring_todos:
version: 0.1.0
diagnostics:
expired_todo: false
flutter_style_expiring_todos and unresolved_todo_condition remain enabled.
This setting affects both CI and the IDE. In a pub workspace, configure each
package from its own analysis_options.yaml as needed.
Command-line usage
The package also includes a CLI that recursively scans Dart files and prints
expired TODOs in a compact, CI-friendly format inspired by
reminder-lint:
dart run :flutter_style_expiring_todos_cli check-expired-todos
./lib/main.dart:12 // TODO(alice)[2026/07/20]: remove fallback
The command exits with status 1 when at least one expired TODO is found and
0 otherwise. Pass one or more files or directories to limit the scan:
dart run :flutter_style_expiring_todos_cli check-expired-todos \
lib test/example.dart
Only .dart files are scanned. .dart_tool, .git, .idea, .vscode, and
build directories are skipped. --date YYYY/MM/DD overrides the evaluation
date, which is useful for deterministic checks.
dart run :flutter_style_expiring_todos_cli check-expired-todos \
--date 2026/07/23 lib
Run the CLI without a command to list available commands, or show detailed help for the scanner command:
dart run :flutter_style_expiring_todos_cli
dart run :flutter_style_expiring_todos_cli help check-expired-todos
When invoking the executable from another package, use the fully qualified
target
flutter_style_expiring_todos:flutter_style_expiring_todos_cli instead of the
leading-colon shorthand.
Suppress a diagnostic
Diagnostic codes are namespaced by the plugin name:
// ignore: flutter_style_expiring_todos/expired_todo
// TODO(alice)[2020/01/01]: grandfathered in, tracked elsewhere
// ignore_for_file: flutter_style_expiring_todos/expired_todo works too.
Limitations
- Only a TODO at the start of a comment is recognized.
- Block comments are exempt from format validation, matching
flutter_style_todos. A valid dated TODO at the start of a block comment is still expiry-checked. - TODOs on inner lines of multi-line block comments are not detected.
- Only the first TODO in a comment is detected.
Contributing
See CONTRIBUTING.md for development, verification, and compatibility-suite maintenance.
License
MIT — see LICENSE.
test/compatibility/flutter_style_todos_official_test.dart is ported from the
Dart SDK and retains its BSD-3-Clause copyright header.
Libraries
- flutter_style_expiring_todos
- An analyzer plugin for TODO comments with optional expiry conditions.
- flutter_style_expiring_todos_cli
- Command-line interface for scanning expired Flutter-style TODO comments.