Step by Step Guide to Integrate DCM Into A Flutter & Dart Open Source Package: dart_mappable
I recently had the pleasure of collaborating with Kilian, the developer behind several Dart and Flutter open-source packages, including the popular "dart_mappable". "dart_mappable" is a package that simplifies JSON serialization, data classes, and supports powerful features like advanced inheritance and generics.
In this post, I'll walk you through how we integrated DCM into "dart_mappable" and show you how you can select rules, create baseline and start fixing issues quickly.
Step by Step Video Recording
We have recorded how we have done step by step, so if you prefer to watch us, feel free to check out the video below.
If you prefer the blog post, keep reading. I am going to break down what we have done in a few steps.
What is "dart_mappable"?
Before diving into DCM, it helps to know a bit more about "dart_mappable". As Kilian explained during our conversation and in his documentation, "dart_mappable" is:
“A JSON serialization and data-class solution for Dart, aiming to be a more advanced alternative to packages like
json_serializable
andfreezed
. It supports deep features such as generics, inheritance, and any edge case you might run into when working with data classes in Dart.”
The package is actually split into two sub-packages:
- dart_mappable (core): The core library, which holds the main logic for data modeling and JSON conversions.
- dart_mappable_builder: The builder package that integrates with
build_runner
to generate the boilerplate code needed for serialization.
|-- README.md -> packages/dart_mappable/README.md
|-- examples
| |-- fic_mappable
| |-- freezed_mappable
| |-- package_comparison
| `-- polymorph_copywith
|-- melos.yaml
|-- packages
| |-- dart_mappable
| `-- dart_mappable_builder
`-- pubspec.yaml
9 directories, 3 files
Kilian manages these packages within a monorepo using Melos, a package that helps to manage Dart or Flutter monorepo.
Setting up DCM
For the session, we assumed that the basic setup of DCM was already done. Normally, you would:
-
Install the CLI
Easily installing on macOS, Windows and Linux -
Install the VS Code Extension (Optional)
The extension offers real-time feedback in your editor. -
Activate Your License
For open-source projects, you can request a free license key from DCM’s team.
Since these steps were already completed, we could jump straight to integrating DCM.
DCM is a code quality tool that helps your team move faster by reducing the time spent on code reviews, finding tricky bugs, identifying complex code, and unifying code style.
Project Structure
We decided to focus on the core package first, where the main logic resides. This could help us to limit the scope to learn and gain more experience.
We opened the analysis_options.yaml
file for that package and added the following top-level section:
dart_code_metrics:
# DCM configuration will be placed here
Interestingly enough, he was using a few rules already from Flutter lint package which reminds me to mention that DCM rules are built on top of those rules.
Exploring Rules
With DCM ready, we wanted a peek at the rules that might apply to dart_mappable. That's where the preview
command shines. So, we run:
dcm init preview lib
This command scans the lib/
directory, checking all code against DCM’s rules. In the console, we saw an output that looked roughly like:
148 member-ordering - auto-fixable - style - 1.3h - https://dcm.dev/docs/rules/common/member-ordering
113 prefer-correct-identifier-length - style - 2.6h - https://dcm.dev/docs/rules/common/prefer-correct-identifier-length
101 parameters-ordering - auto-fixable - style - 42.4m - https://dcm.dev/docs/rules/common/parameters-ordering
76 avoid-dynamic - warning - 4.1h - https://dcm.dev/docs/rules/common/avoid-dynamic
75 prefer-named-parameters - style - 2.0h - https://dcm.dev/docs/rules/common/prefer-named-parameters
68 prefer-type-over-var - auto-fixable - style - 16.3m - https://dcm.dev/docs/rules/common/prefer-type-over-var
62 format-comment - auto-fixable - style - 14.9m - https://dcm.dev/docs/rules/common/format-comment
50 prefer-single-declaration-per-file - style - 3.8h - https://dcm.dev/docs/rules/common/prefer-single-declaration-per-file
46 prefer-typedefs-for-callbacks - style - 3.5h - https://dcm.dev/docs/rules/common/prefer-typedefs-for-callbacks
46 avoid-type-casts - warning - 2.8h - https://dcm.dev/docs/rules/common/avoid-type-casts
38 newline-before-return - auto-fixable - style - 10.3m - https://dcm.dev/docs/rules/common/newline-before-return
37 prefer-trailing-comma - auto-fixable - style - 10.0m - https://dcm.dev/docs/rules/common/prefer-trailing-comma
36 no-object-declaration - warning - 1.1h - https://dcm.dev/docs/rules/common/no-object-declaration
36 prefer-explicit-parameter-names - style - 1.1h - https://dcm.dev/docs/rules/common/prefer-explicit-parameter-names
27 no-magic-string - warning - 1.6h - https://dcm.dev/docs/rules/common/no-magic-string
24 avoid-collection-mutating-methods - warning - 1.8h - https://dcm.dev/docs/rules/common/avoid-collection-mutating-methods
24 avoid-redundant-else - auto-fixable - style - 6.5m - https://dcm.dev/docs/rules/common/avoid-redundant-else
...
✔ total applied lint rules: 393
Surprising! Yes, there were that many violations. Especially the first few ones like member-ordering
or avoid-dynamic
.
Well, that's great, because now we could see all issues! But that doesn't mean we wanted to enable all the rules right? Therefore, we had to decide on which rules. That's where filtering rules might be handy.
To avoid overwhelming ourselves, we started filtering by severity:
dcm init preview lib --severity=warning --no-empty-violations
Now, we only saw warnings that actually appeared in the code.
76 avoid-dynamic - warning - 4.1h - https://dcm.dev/docs/rules/common/avoid-dynamic
46 avoid-type-casts - warning - 2.8h - https://dcm.dev/docs/rules/common/avoid-type-casts
36 no-object-declaration - warning - 1.1h - https://dcm.dev/docs/rules/common/no-object-declaration
27 no-magic-string - warning - 1.6h - https://dcm.dev/docs/rules/common/no-magic-string
24 avoid-collection-mutating-methods - warning - 1.8h - https://dcm.dev/docs/rules/common/avoid-collection-mutating-methods
21 avoid-non-null-assertion - warning - 37.8m - https://dcm.dev/docs/rules/common/avoid-non-null-assertion
17 prefer-explicit-function-type - warning - 30.6m - https://dcm.dev/docs/rules/common/prefer-explicit-function-type
15 prefer-correct-throws - warning - 54.0m - https://dcm.dev/docs/rules/common/prefer-correct-throws
15 avoid-unsafe-collection-methods - warning - 54.0m - https://dcm.dev/docs/rules/common/avoid-unsafe-collection-methods
13 avoid-shadowing - warning - 23.4m - https://dcm.dev/docs/rules/common/avoid-shadowing
12 prefer-declaring-const-constructor - auto-fixable - warning - 5.0m - https://dcm.dev/docs/rules/common/prefer-declaring-const-constructor
11 avoid-collection-methods-with-unrelated-types - warning - 29.7m - https://dcm.dev/docs/rules/common/avoid-collection-methods-with-unrelated-types
11 no-magic-number - warning - 39.6m - https://dcm.dev/docs/rules/common/no-magic-number
10 prefer-match-file-name - warning - 20.0m - https://dcm.dev/docs/rules/common/prefer-match-file-name
10 avoid-mutating-parameters - warning - 1.3h - https://dcm.dev/docs/rules/common/avoid-mutating-parameters
8 avoid-late-keyword - auto-fixable - warning - 5.0m - https://dcm.dev/docs/rules/common/avoid-late-keyword
8 avoid-ignoring-return-values - warning - 32.0m - https://dcm.dev/docs/rules/common/avoid-ignoring-return-values
8 avoid-unused-generics - auto-fixable - warning - 5.0m - https://dcm.dev/docs/rules/common/avoid-unused-generics
5 avoid-nullable-parameters-with-default-values - auto-fixable - warning - 5.0m - https://dcm.dev/docs/rules/common/avoid-nullable-parameters-with-default-values
4 avoid-unnecessary-overrides - auto-fixable - warning - 5.0m - https://dcm.dev/docs/rules/common/avoid-unnecessary-overrides
4 avoid-assigning-to-static-field - warning - 20.0m - https://dcm.dev/docs/rules/common/avoid-assigning-to-static-field
3 avoid-recursive-calls - warning - 30.0m - https://dcm.dev/docs/rules/common/avoid-recursive-calls
3 avoid-unnecessary-nullable-return-type - auto-fixable - warning - 5.0m - https://dcm.dev/docs/rules/common/avoid-unnecessary-nullable-return-type
3 avoid-passing-self-as-argument - warning - 6.0m - https://dcm.dev/docs/rules/common/avoid-passing-self-as-argument
2 prefer-commenting-analyzer-ignores - warning - 5.0m - https://dcm.dev/docs/rules/common/prefer-commenting-analyzer-ignores
2 avoid-global-state - warning - 20.0m - https://dcm.dev/docs/rules/common/avoid-global-state
2 prefer-moving-to-variable - warning - 8.0m - https://dcm.dev/docs/rules/common/prefer-moving-to-variable
1 avoid-long-files - warning - 15.0m - https://dcm.dev/docs/rules/common/avoid-long-files
1 prefer-explicit-type-arguments - warning - 5.0m - https://dcm.dev/docs/rules/common/prefer-explicit-type-arguments
1 avoid-commented-out-code - warning - 5.0m - https://dcm.dev/docs/rules/common/avoid-commented-out-code
1 avoid-substring - warning - 5.0m - https://dcm.dev/docs/rules/common/avoid-substring
1 avoid-nullable-tostring - auto-fixable - warning - 5.0m - https://dcm.dev/docs/rules/common/avoid-nullable-tostring
1 prefer-both-inlining-annotations - auto-fixable - warning - 5.0m - https://dcm.dev/docs/rules/common/prefer-both-inlining-annotations
1 avoid-excessive-expressions - warning - 5.0m - https://dcm.dev/docs/rules/common/avoid-excessive-expressions
1 prefer-public-exception-classes - warning - 5.0m - https://dcm.dev/docs/rules/common/prefer-public-exception-classes
1 prefer-parentheses-with-if-null - warning - 5.0m - https://dcm.dev/docs/rules/common/prefer-parentheses-with-if-null
1 no-equal-arguments - warning - 5.0m - https://dcm.dev/docs/rules/common/no-equal-arguments
1 avoid-accessing-other-classes-private-members - warning - 10.0m - https://dcm.dev/docs/rules/common/avoid-accessing-other-classes-private-members
1 avoid-duplicate-constant-values - warning - 5.0m - https://dcm.dev/docs/rules/common/avoid-duplicate-constant-values
1 avoid-incomplete-copy-with - warning - 5.0m - https://dcm.dev/docs/rules/flutter/avoid-incomplete-copy-with
✔ total applied lint rules: 40
Much better. This helped us prioritize more pressing issues over stylistic ones.
Within the preview, DCM also labels certain rules auto-fixable, meaning a simple command can clean them up.
dcm init preview lib --only-fixable --no-empty-violations
We noticed rules like parameters-ordering and member-ordering had a large number of violations but were also auto-fixable, perfect candidates for our first wave of cleanup.
148 member-ordering - auto-fixable - style - 1.3h - https://dcm.dev/docs/rules/common/member-ordering
101 parameters-ordering - auto-fixable - style - 42.4m - https://dcm.dev/docs/rules/common/parameters-ordering
68 prefer-type-over-var - auto-fixable - style - 16.3m - https://dcm.dev/docs/rules/common/prefer-type-over-var
51 format-comment - auto-fixable - style - 12.2m - https://dcm.dev/docs/rules/common/format-comment
...
✔ total applied lint rules: 28
Enabling Recommended Rules
Well, that's one of way doing that. We started adding these rules one by one, but that's not the only options! In fact, rather than enabling rules individually, DCM provides presets:
dart_code_metrics:
extends:
- recommended
The recommended
preset covers a balanced selection of rules—everything from code style to potential logic pitfalls. Once we added this, we ran:
dcm analyze
And saw many new warnings. Some were minor style issues; others pointed to potential bugs or suboptimal code constructs.
WARNING Calling this method/property may throw an exception. Try using the safe 'elementAtOrNull' from 'package:collection' instead.
at lib/src/case_style.dart:108:32
avoid-unsafe-collection-methods : https://dcm.dev/docs/rules/common/avoid-unsafe-collection-methods
STYLE Prefer adding a trailing comma.
at lib/src/case_style.dart:109:11
prefer-trailing-comma : https://dcm.dev/docs/rules/common/prefer-trailing-comma
STYLE This 'else' block is redundant. Try moving all statements from it to the outer block and removing the 'else' block.
at lib/src/case_style.dart:158:12
avoid-redundant-else : https://dcm.dev/docs/rules/common/avoid-redundant-else
...
Scanned folders: 5
Scanned files: 25
warning issues: 182, style issues: 85
For instance:
- Avoid incomplete copyWith
- Avoid redundant else
- Avoid dynamic
- Member ordering
We could have manually addressed them all, but with DCM’s auto-fixing power, we decided to let DCM do the heavy lifting.
dcm fix lib
This command easily fixed all violations that DCM could fix automatically. We could even run a --dry-run
to ensure what will happen before actually running.
Creating a Baseline
In well-established existing codebases (like "dart_mappable"), you might have dozens or hundreds of warnings. Fixing them all at once can be overwhelming, and you might not want your pull requests to fail while you progressively improve the code. That's exactly what we decided to do as well.
We created a baseline with:
dcm init baseline lib
That generated a JSON file—called dcm-baseline.json
—listing all existing violations based on the enabled rules. For example, in our setup, we had the recommended
preset. By committing this to version control, we essentially “accept” those warnings for now. DCM will only flag new issues or those we specifically remove from the baseline file.
You can gradually fix baseline issues by deleting entries from the JSON file, then rerunning dcm analyze
. Each removed entry reactivates that warning. This approach lets you tackle a few warnings at a time without breaking your CI.
Auto-Fixing Violations
One of our highlights during the session was seeing how easily we could fix many issues at once. I think this is so important that I want to mention it again. With rules like member-ordering or parameters-ordering, we ran:
dcm fix
This command scanned our code and automatically rearranged parameters, method order, and other style elements to align with best practices. A quick check of the diffs (git diff
) showed changes that wouldn’t break functionality but made the code more structured and consistent.
This was also a surprise for Kilian on how fast you can add DCM and also find a rule and get it fixed.
Naturally, some rules don’t always align with your project’s design choices. For instance, the recommended
preset includes avoid-incomplete-copy-with, but in some advanced use cases, you might intentionally leave certain fields out of copyWith
or potentially you want to make this rule silent until further notice and investigation is done. That what we did.
To disable it, we added:
dart_code_metrics:
extends:
- recommended
rules:
avoid-incomplete-copy-with: false
We repeated the same process for other rules like prefer-public-exception-classes or avoid-redundant-else whenever we knew our design was intentional.
Detecting and Removing Unused Code
DCM is not only about the lint rules, but also comes with several commands to help you keep your codebase consistent and reliable.
One of the biggest features we explored was unused code detection. We ran:
dcm check-unused-code lib
and SUPERSIZE!
...
✖ total unused code declarations - 106
There was 106 unused code! Wow! Let's see if any of them is actually unused.
Since "dart_mappable" is a library meant for other projects to import, many public APIs appeared to be “unused” internally. But we actually needed them for external consumers! To handle this, we set:
dart_code_metrics:
exclude-public-api: true
This flag ensures public APIs of the package aren’t mistakenly flagged. For this purpose, it's also good to mention that we follow the Dart package structure convention that is common is Dart and Flutter community.
With this configuration, dcm check-unused-code lib
dropped from dozens of unused items to just a handful of actually unused functions or variables.
Good news is that after we identified a few, we used dcm fix
to immediately address them! Yes, DCM can DO THAT as well. We used:
dcm fix lib --type=unused-code
This automatically removed dead code. It’s a quick way to keep your package tidy, especially as features evolve over time. It's also surprising how much unused code can leak to your project without you even notice!
Pull Request
After these modifications, we did what every good developer does: checked the diffs carefully. Automated changes might sometimes have side effects, especially for advanced or domain-specific code. Satisfied, we staged our changes, committed them, and created a pull request for merging into the main branch.
Continuous Integration (CI)
Kilian was curious about adding DCM to the GitHub Actions workflow to automate code checks on every PR. For that, DCM provides a straightforward example script:
name: DCM
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Dart and Flutter
uses: subosito/flutter-action@v2
- name: Install dependencies
run: flutter pub get
- name: Install DCM
uses: CQLabs/setup-dcm@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Run DCM
run: dcm analyze --ci-key="${{ secrets.DCM_CI_KEY }}" --email="${{ secrets.DCM_EMAIL }}" lib
In fact, we support all major CD/CI tools and various output formats that you can leverage to use in any of your favorite CI/CDs. Read more here.
What We Learned
-
Incremental Approach
You can always get a preview of issues and select rules. However, It might be best to start with the recommended rules, then add or remove rules as needed. This avoids overloading the project with too many changes at once. -
Baselines Lower the Pressure
If a project has a large, established codebase, the baseline help you tidy up gradually without blocking development which we did in this step. -
Auto-Fix is Powerful
For styling and minor logic rules,dcm fix
can resolve hundreds of violations in seconds. Always verify diffs, but it’s a huge timesaver. -
Unused Code is Surprising
Many libraries have partial or legacy code that’s no longer relevant. DCM’s unused code detection helps you quickly find and remove such code. -
Flexible Configuration
Not all warnings are universal “must fixes.” DCM respects your project design. If you have a private exception class on purpose, for instance, you can safely disable or ignore that rule. Also, many rules come with custom configuration that you can consider modifying accordingly. -
CI Integration
By placing DCM in your CI pipeline, you catch issues early or block your PR. Contributors to your project can’t accidentally merge code that violates your standards.
Beyond Linting
While our session mostly focused on linting rules and auto-fixes, but also discussed other features. For example, DCM offers metrics as well or commands such as checking for unused files, or analyzing assets and more. Check out all feature on this page.
DCM is a code quality tool that helps your team move faster by reducing the time spent on code reviews, finding tricky bugs, identifying complex code, and unifying code style.
Conclusion and Next Steps
Integrating DCM into "dart_mappable" proved to be both straightforward and eye-opening. Even a well-maintained, widely-used library can have hidden issues, from small styling inconsistencies to truly unused internal code.
As Kilian mentioned, adding DCM was quite easy and pretty fast! Possibly took just a few seconds! But what we have seen just in a few first minutes was pretty surprising and indeed, something that DCM could help to address.
I hope this walkthrough gives you a clear, step-by-step illustration of how DCM can enhance code quality in any Dart or Flutter project. "dart_mappable" now has a solid foundation for ongoing code improvement—and your own library or application can too, with just a few commands. If you’ve been on the fence about introducing linting or advanced metrics, this is the perfect time to start. Happy coding—and happy linting!
Open Source Maintainer?
If you’re maintaining any Dart or Flutter project, consider requesting a free DCM Teams OSS license for your projects. Whether you prefer a thorough initial cleanup or a measured, rule-by-rule integration, the tool is flexible enough to fit your needs.
For deeper insights, advanced configuration, or help with licensing, check out dcm.dev and connect with the community on our Discord channel.
We are also happy to make a step by step video or blog post and help you integrate DCM into your package. Feel free to reach out to us.
Enjoying this article?
Subscribe to get our latest articles and product updates by email.