Finding Duplicate Dart Code in Flutter Projects
AI-assisted Flutter work can reproduce the same widget, repository, or validation rule in slightly different forms. The code can compile and pass tests while leaving multiple implementations that need the same future fix.
Flutter widget trees and build methods can be long, so partially duplicated structure does not always stand out in review.
For the full algorithm write-up, see Research Background. This post is the Dart-and-Flutter-specific version.
AI-assisted code and duplication
GitClear's 2025 AI Copilot Code Quality study reports increased copy/pasted lines and duplicated blocks in the repositories it studied. Code Copycat Conundrum examines repetition in LLM-generated code at character, statement, and block levels.
This does not make AI-written Dart inherently bad. It does make repository-level duplicate checks useful during AI-assisted work.
Where Flutter duplication commonly appears
Common candidates include:
- widget trees and repeated layout fragments inside
buildmethods; - repositories, data mappers, and validation paths that differ mainly by names;
copyWithmethods, retry wrappers, and repeated*_test.dartsetup.
What a Dart duplicate-code check should look for
A useful check does not stop at exact line matches. It should find four levels of similarity:
- Exact duplicate code — the same Dart copied with only formatting or comment changes.
- Renamed duplicate code — the same structure with different identifiers: a
CustomerCardwidget cloned intoAccountCard,customerIdswapped foraccountId. - Near-duplicate code — mostly the same logic with statements inserted, deleted, or reordered: the same form validation with one extra branch.
- Same behavior, different code — two widgets or functions that solve the same problem with different syntax (a
forloop versus amap().toList()).
Clone-detection research calls these Type-1 through Type-4. A Deslop report names them Identical code [Type-1/2], Nearly identical code [Type-3], and Same behavior, different code [Type-4]. The implementation and research references are documented in Research Background.
Why line matching is not enough for Dart
Line-based tools catch literal copy-paste but are sensitive to surface changes:
CustomerCardbecomesAccountCard, every identifier renamed.- a helper is copied into a different class and re-indented.
setStatelogic is rewritten as aRiverpodnotifier that does the same thing.- the same validation rule is rebuilt with the branches in a different order.
That is why Deslop starts from the parsed syntax tree, not the text. It parses each .dart file with tree-sitter, strips out identifier and literal names so renamed copies still match, fingerprints the tree structure, widens the net to near-duplicates with sibling windows and MinHash, and can optionally add embeddings for same-behaviour matches. The short version: it compares structure first, lines never. The full audit trail is in How It Works.
Check before writing
Deslop's MCP server can move the check into the coding agent's editing loop.
If you use a coding agent for Flutter work, configure it to call Deslop's find-similar tool before it writes a new widget, repository, mapper, or test setup. A strong match gives the agent a chance to reuse or extend the existing implementation. See AI Integration for setup.
Ranking: the worst offender is line one
Deslop ranks clusters by measured impact using AST node count, additional copies, and logarithmically dampened byte span. The exact formula is documented in How It Works.
The JSON report gives agents the cluster order, byte ranges, bucket, and signals without a separate representation.
What to do when you find duplicate Dart
Do not treat every clone as a bug. Treat it as a decision.
- Extract when the copies are clearly the same abstraction and will change together — pull the repeated layout into a custom widget, lift the shared styling into
ThemeData. - Reuse when one implementation is already the better source of truth and the others should call it.
- Accept when the duplication is deliberate: generated code, fixtures, platform shims, or two paths that look alike today but are expected to diverge.