flutter_skill_lints

pub package license

Analyzer plugin that turns the building-flutter-apps skill's architecture and code-quality rules into Dart analyzer diagnostics, plus a curated many_lints-inspired surface, so feedback shows up in your IDE, dart analyze, and full-project Flutter analyzer runs.

Designed for Riverpod + codegen Flutter apps.

Highly opinionated by design: This plugin enforces the accompanying building-flutter-apps skill's preferred architecture and code style. Treat it as a project policy package, not a neutral Dart/Flutter lint preset. Until v1.0.0, assume every release may include breaking changes as the lint surface is refined.

Highlights

Surface Count
Flutter skill warning rules 125
Flutter skill diagnostic codes 132
Additional Dart/Flutter warning rules 85
Quick fixes 64
Assists 1

Quick Start

  1. Add the plugin to the top-level plugins section of analysis_options.yaml (it is not a pubspec.yaml dependency):

    include: package:flutter_lints/flutter.yaml
    
    plugins:
      flutter_skill_lints:
      # Pre-release pin: latest stable is 3.1.3; use 3.1.4-dev.3 for Riverpod 3.3-era lint coverage.
      # Verify pub.dev before ship. Promote to a stable release when compatible.
      # Pre-release silently adopts dev behavior - review.
      riverpod_lint: 3.1.4-dev.3
    
    analyzer:
      exclude:
        - "**/*.g.dart"
        - "**/*.freezed.dart"
        - "**/*.gr.dart"
        - "**/*.arb"
      language:
        strict-casts: true
        strict-inference: true
        strict-raw-types: true
      errors:
        missing_required_param: error
        missing_return: error
        invalid_annotation_target: ignore
    
    formatter:
      page_width: 100
    
    linter:
      rules:
        - always_use_package_imports
        - require_trailing_commas
        - prefer_single_quotes
        - directives_ordering
        - avoid_multiple_declarations_per_line
        - prefer_const_constructors
        - prefer_const_declarations
        - prefer_const_literals_to_create_immutables
        - prefer_final_locals
        - avoid_redundant_argument_values
        - avoid_dynamic_calls
        - avoid_print
        - avoid_void_async
        - cancel_subscriptions
        - close_sinks
        - discarded_futures
        - unawaited_futures
    
  2. Restart the Dart Analysis Server (most editors expose "Dart: Restart Analysis Server"; otherwise restart the IDE).

  3. Run analysis:

    dart analyze       # CLI/CI gate; do not path-scope analysis
    dart test
    

    Full-project flutter analyze can be useful locally, but the building-flutter-apps skill uses dart analyze for CLI/CI because path-scoped Flutter analysis can drop plugin diagnostics. flutter_skill_lints can read project files and report configuration drift through Dart-analysis diagnostics, but it does not replace flutter pub get or pub.dev publish validation.

Optional: install the companion skill

If you use Claude Code or another agent runtime that consumes skills.sh skills, install the matching agent guidance:

npx skills add https://github.com/sgaabdu4/building-flutter-apps \
  --skill building-flutter-apps

Rules

Flutter Skill Diagnostics

Encode the architectural rules from building-flutter-apps.

Area Diagnostic IDs
Async safety use_ref_mounted_after_await, use_context_mounted_after_await, use_unawaited_for_fire_and_forget_futures, async_context_mounted_style
Riverpod avoid_legacy_riverpod_apis, riverpod_read_init_state, riverpod_service_locator, riverpod_manual_provider, riverpod_consumer_state_derived_cache, riverpod_watch_no_select, riverpod_select_arrow_syntax, riverpod_mutation_experimental_warning, riverpod_auto_dispose_keepalive_dependencies, riverpod_feature_notifier_keepalive, riverpod_keepalive_family, use_ref_invalidate
Notifiers avoid_silent_repository_null_return, avoid_sync_notifier_state_read, notifier_ensure_deps, notifier_watch_method
Freezed and serialization use_sealed_freezed_classes, freezed_per_class_explicit_to_json, freezed_to_json_with_from_json, freezed_legacy_when_map
Architecture arch_domain_import, arch_domain_serialization, arch_interface_contract, arch_repository_generated_extends, arch_concrete_dependency, arch_datasource_try_catch, arch_widget_path, atomic_provider_access, typed_id_raw_id, records_map_return, avoid_object_map_cast, vo_public_raw_constructor, domain_entity_primitive_factory, domain_custom_copy_with, hive_field_no_vo_type
Navigation guard_context_pop, avoid_route_param_throw_in_build, router_string_nav, router_gorouter_of, router_untyped_navigator_push, router_direct_route_call, router_raw_route_definition, router_modal_local_helpers, router_container_navigation_escape, router_context_navigation_extension, router_navigation_wrapper_api, router_pop_then_push, router_redirect_watch, router_redirect_loading_bounce, router_complex_extra
UI and accessibility avoid_widget_build_helpers, avoid_shrink_wrap, avoid_private_widget_classes, style_raw_token, style_raw_text_style, strings_hardcoded, l10n_context_direct_access, ui_snackbar_boundary, a11y_text_scale_clamp
Date and time datetime_now_requires_timezone_intent
Performance perf_build_work, perf_listview_children
State state_raw_response, state_raw_error_to_string, state_freezed_nullable_error, state_broad_invalidation
ShowcaseView avoid_showcase_key_filtering, showcase_listen_manual_handle, showcase_prev_null_guard, showcase_default_scope, showcase_dispose_on_tap
Services and mixins service_singleton, mixin_mixin_class, mixin_name_suffix, mixin_mutable_state, dart_static_namespace
Data and crash reporting data_log_rethrow, crash_possible_pii, crash_run_zoned_guarded_legacy, avoid_run_zoned_guarded, require_main_error_hooks
Tests test_provider_container, test_uncontrolled_scope, test_create_container, test_mock_concrete, test_pump_and_settle, test_tap_at, test_inline_value_key, test_first_match_finder
Project config flutter_skill_project_config
Extended architecture and Freezed arch_model_missing_to_entity, arch_model_extends_entity, arch_domain_json_annotation, freezed_missing_private_constructor
Extended navigation and ShowcaseView router_impure_redirect, router_shell_tab_push, showcase_v4_api, showcase_get_named_unhandled, showcase_scope_string_literal
Extended Flutter optimization flutter_key_created_in_build, flutter_unique_or_global_key, flutter_opacity_widget, flutter_save_layer_filter, flutter_clip_save_layer, flutter_intrinsic_layout, flutter_animated_builder_child, flutter_widget_operator_equals
Hive, Crash, and services hive_reserved_type_ids_missing, hive_duplicate_type_id, hive_duplicate_field_id, hive_test_close_missing, crash_direct_firebase_call, crash_init_before_run_app, fire_and_forget_missing_catch, service_static_side_effect, service_random_per_call, fire_forget_in_tests

Additional Analyzer Coverage

Adapted from many_lints, original diagnostic IDs preserved where applicable. Covers:

  • Listener and disposal mistakes.
  • Constant conditions, duplicate cascades, contradictory expressions.
  • Collection misuse and unrelated-type checks.
  • Widget composition issues.
  • Riverpod and hook-specific mistakes.
  • Test matcher hygiene.
  • Naming and modern Dart style checks.
  • Magic literal checks via avoid_magic_literals for raw executable strings and numbers.
  • Error/status code owner checks via avoid_inline_error_codes.
  • Contract key owner checks via avoid_local_contract_key_constants.
  • Local suppression guards via avoid_flutter_skill_lint_suppression.
  • Class destructuring guidance, including prefer_class_destructuring.

Implementations live under lib/src/additional_lints/rules.

Examples

Each snippet shows the minimum context for the diagnostic to fire.

// lib/features/counter/presentation/counter_notifier.dart
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'counter_notifier.g.dart';

@riverpod
class CounterNotifier extends _$CounterNotifier {
  @override
  int build() => 0;

  Future<void> incrementLater() async {
    await Future<void>.delayed(Duration.zero);
    state = state + 1; // use_ref_mounted_after_await
  }
}

Also flagged: raw string or named route calls (router_string_nav), direct GoRouter.of(context) usage (router_gorouter_of), injected router/context route calls that bypass generated helpers (router_direct_route_call), raw router or route definitions outside the router boundary or shared test router helper (router_raw_route_definition), and direct page-route Navigator APIs (router_untyped_navigator_push). Fix by calling generated typed route helpers:

ProductDetailRoute(id: id).go(context);
await ProductCreateRoute(parentId: id).push<void>(context);
import 'dart:async';
import 'package:flutter/widgets.dart';

Future<void> saveLater<T>() async {}

void buildButton(VoidCallback onPressed) {}

void build() {
  buildButton(() {
    saveLater<void>(); // use_unawaited_for_fire_and_forget_futures
  });

  buildButton(() {
    unawaited(saveLater<void>());
  });
}

Future<void> precacheCachedNetworkImages(
  Iterable<ImageProvider<Object>> images,
  BuildContext context,
) async {
  await Future.wait([
    for (final image in images) precacheImage(image, context),
  ]);
}

void setupImages(
  Iterable<ImageProvider<Object>> images,
  BuildContext context,
) {
  unawaited(precacheCachedNetworkImages(images, context));
}
// lib/features/users/domain/user.dart
class User {
  const User({required this.userId, required this.orgId});

  final String userId; // typed_id_raw_id
  final String orgId;
}

Configuration

Suppress a single diagnostic with a line comment:

final raw = response.body!; // ignore: avoid_null_bang

Or scope to a file:

// ignore_for_file: avoid_null_bang, avoid_shrink_wrap

To disable a rule project-wide, add it to analysis_options.yaml:

analyzer:
  errors:
    avoid_shrink_wrap: ignore
    datetime_now_requires_timezone_intent: ignore

Troubleshooting

Diagnostics don't appear. Restart the Dart Analysis Server after editing analysis_options.yaml. The plugin loads only at server start.

Plugin fails to load. Check that your project resolves the analyzer versions listed under Compatibility. Analyzer plugin APIs are not stable across major versions.

Conflict with riverpod_lint. Both plugins are designed to coexist; pin riverpod_lint to prerelease 3.1.4-dev.3 to match the version we test against.

Compatibility

Targets the analyzer 12 line. Verified against:

  • analysis_server_plugin 0.3.14
  • analyzer 12.1.0
  • analyzer_plugin 0.14.8
  • riverpod_lint 3.1.4-dev.3

Recheck before publishing a new release.

Development

dart format .
dart analyze
dart test

Integration smoke test (creates a temporary Flutter app, gated):

RUN_FLUTTER_PLUGIN_SMOKE=1 dart test test/integration_plugin_smoke_test.dart \
  --reporter expanded

Release

A version bump merged to main triggers the release workflow, which tags vX.Y.Z. The tag workflow publishes to pub.dev and creates the GitHub Release.

Attribution

Inspired by many_lints. Portions of the internal analyzer rule implementation are distributed under the MIT license. See THIRD_PARTY_NOTICES.md.

Libraries

flutter_skill_lints
Analyzer plugin entrypoint for Flutter skill lint rules.
main
Entrypoint discovered by analysis_server_plugin.