All articles
June 12, 20263 min read#ai#claude-code#flutter#developer-tools#open-source

Claude Code Plugins for Flutter Teams: How UFIL Enforces Architecture with AI

Why I built an open-source Claude Code plugin for Flutter, how it uses Dart MCP and LSP to generate code that follows Clean Architecture, and what AI-assisted development actually changes on a real team.

AI coding assistants are very good at producing code and very bad at producing your code. Point one at a Flutter project and ask for a new feature, and you get a working screen that ignores your package structure, invents a second way to do networking, and puts business logic in a widget. The code runs. The architecture is dead.

UFIL (Ultimate Flutter Intelligent Layer) is my answer. It is a Claude Code plugin, published in the gm-aiagent-plugins marketplace, that makes the assistant follow the same modular Clean Architecture my team uses by hand.

What a plugin can actually do

A Claude Code plugin bundles three kinds of things:

  • Skills: reusable instructions the assistant loads for a task, such as "create a feature module" with the exact folder layout, naming, and DI registration.
  • Agents: specialised reviewers. UFIL ships an architecture reviewer that reads a diff and flags a presentation file importing from data, a repository returning a DTO, or a bloc calling Dio directly.
  • Hooks: scripts that run around tool calls. UFIL runs dart format and dart analyze after every edit, so the assistant sees its own lint errors and fixes them before you do.

Dart MCP and LSP make the difference

Instructions alone are not enough. The plugin connects Claude Code to the Dart MCP server and the analysis server, so the assistant can resolve symbols, find references, and read real type information instead of guessing from file names. When it generates a use case it looks up the repository interface that already exists. When it adds a route it finds the actual router. The difference between "plausible" and "compiles on the first try" is almost entirely this.

A real session

Asking for "add an order history feature" on a UFIL project produces:

  1. A new features/order_history package with data, domain, and presentation.
  2. A GetOrderHistory use case against a new repository interface, with the implementation calling the existing network client.
  3. A bloc, a page, and a widget test, all registered in DI and routing.
  4. An analysis pass with zero warnings.

Then the review agent runs and, in one early session, caught that the generated bloc had imported the DTO instead of the entity. It fixed it before the diff reached a human. That is the moment the plugin paid for itself.

What changes on the team

  • Onboarding shrinks. New engineers read the skill files to learn the conventions, then watch the assistant apply them.
  • Reviews move up a level. Humans stop commenting on folder placement and start commenting on product behaviour.
  • Consistency across projects. Every Rancangin Aja client project and every in-house product uses the same plugin, so switching between them costs nothing.

What it does not do

It does not design your domain for you. Bad entities generated quickly are still bad entities. It does not replace tests. And it is only as good as the conventions written into the skills, which means the first job is still to decide, as a team, what the architecture is.

The plugin is MIT licensed. Install it with /plugin marketplace add ghozimahdi/gm-aiagent-plugins and /plugin install ufil@gm-aiagent-plugins, and open an issue if your conventions differ from mine. A Rails plugin, Rubyku, lives in the same repo, and Kotlin and React Native versions are next.

Written by
Ghozi Mahdi
Senior Mobile Engineer · Founder, Rancangin Aja
Get in touch