Flutter Custom Lint Rules: Enforce Conventions for AI and Human Code

Use custom_lint in Flutter to enforce API restrictions and project conventions so AI-generated and human-written code are checked the same way.

Flutter Custom Lint Rules: Enforce Conventions for AI and Human Code

Introduction

AI-assisted coding — whether with Copilot, Cursor, or Claude — can speed up development a lot. But it has a downside we all run into: it often generates code that is almost right.

It might use APIs we’ve banned, skip parameters we require (like RouteSettings), or ignore project conventions. Fixing these by hand is tedious and easy to miss in review.

That’s exactly why custom lint rules are more valuable than ever: they turn your team’s rules and conventions into something the toolchain enforces automatically, so AI-generated (and human-written) code is checked the same way.

Why Custom Lint Matters Now

When you paste a snippet or accept an AI suggestion, the code often:

  • Uses APIs your team has decided to avoid (e.g. ScaffoldMessenger in favour of a centralised service).
  • Omits parameters you’ve standardised on (e.g. routeSettings on every dialog and sheet for observability and testing).

Catching this only in code review is unreliable. Custom lint runs on every file the analyzer sees, so violations show up in the IDE and in CI. That way, both AI output and human code are held to the same rules.

Setup

In this article we’ll look at how to enforce API restrictions and project conventions in Flutter using the custom_lint package.

📁 File: pubspec.yaml

dev_dependencies:
  custom_lint: ^0.8.1
  custom_lint_rules:
    path: packages/custom_lint_rules

📁 File: analysis_options.yaml

analyzer:
  plugins:
    - custom_lint

custom_lint:
  rules:
    - no_scaffold_messenger
    - require_route_settings

Run flutter pub get. Lints appear in the IDE and when you run:

dart run custom_lint

Example: Correct vs Incorrect

Correct (no violations)

  • No ScaffoldMessenger; use your own service or route-based feedback.
  • showDialog / showModalBottomSheet with routeSettings: RouteSettings(name: '...').
  • MaterialPageRoute(..., settings: RouteSettings(name: '...'), builder: ...).
  • Navigator.pushNamed(context, '/detail') is allowed as-is (no routeSettings in the API).

Incorrect (reported by the rules)

  1. ScaffoldMessenger.of(context).showSnackBar(...)no_scaffold_messenger.
  2. showDialog(context: context, builder: ...) without routeSettingsrequire_route_settings.
  3. MaterialPageRoute(builder: ...) without settingsrequire_route_settings.

Conclusion

AI will keep suggesting code that doesn’t quite match your conventions. Custom lint turns those conventions into something the analyzer enforces for every edit — whether it comes from you or from the model. Starting with a couple of rules (like banning an API and requiring RouteSettings) is enough to get immediate value and a pattern you can reuse for more rules later.


Thank you for reading, I hope it is helpful. If you have any questions, feel free to ask on LinkedIn.

You can access the source code of the application from the link below:

https://github.com/hasankarli/example_custom_lint