Strict mode Dart: configuring Flutter Analyzer for code quality

Strict code analysis often breaks the mobile app build, and the team spends hours sorting through warnings instead of shipping. We set up Flutter Analyzer turnkey: we establish rules, thresholds, and automation so that quality grows while the build remains predictable. As a result, review time is reduced and the number of failures drops.

Development and support of all types of mobile applications:

Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Our competencies:

Frequently Asked Questions

Latest works

  • Development of a mobile application for FEEDME
    Development of a mobile application for FEEDME
    971
  • Development of a mobile application for XOOMER
    Development of a mobile application for XOOMER
    869
  • Development of a mobile application for RHL
    Development of a mobile application for RHL
    1289
  • Development of a mobile application for ZIPPY
    Development of a mobile application for ZIPPY
    1143
  • Development of a mobile application for Affhome
    Development of a mobile application for Affhome
    1076
  • Development of a mobile application for the FLAVORS company
    Development of a mobile application for the FLAVORS company
    677

Why strict code analysis breaks the mobile app build

Strict code analysis breaks the build not because the rules are bad, but because they are enabled on top of legacy code.

The team adds a set of recommended lints to analysis_options.yaml, runs flutter analyze — and gets hundreds of warnings on code that was written for years without checks.

The CI step with analysis turns red, the release branch does not pass, and the first thing the team does is disable the rules to keep moving forward.

Then the ratchet effect kicks in: the rules are off, warnings accumulate, and no one sees new violations. A few releases later the analyzer is turned back on — and again a avalanche appears, only now twice as large. This is how strict analysis turns into a tool that either blocks the build or does not work at all.

The root of the problem is that strict code analysis is not a binary switch, but a mode with gradation. Some rules catch real defects: unhandled null, unsafe type casts, forgotten await in asynchronous calls. Another part is style, which does not affect app behavior.

Mixing them in a single run and failing the build on everything at once means losing the signal in the noise.

A workable scheme is to separate rules by levels: critical errors block the build, analyzer warnings are collected into a report and do not interfere with the release, style is fixed gradually.

Then flutter analyze stops being a source of failures and becomes a code quality control tool that the team does not disable on the very first red run. Below we break down how such a mode is configured.

What a well-tuned analyzer configuration delivers

Strict analyzer mode is not a "checkbox in the config" but a set of rules that the team deliberately tightens as the codebase matures.

At the start, we enable a baseline set of lints and mandatory typing, then add rules for unused dependencies, unsafe type casts, and forgotten await.

Every tightening goes through the build — if a rule breaks existing code, it is either fixed immediately or moved into exceptions with a comment and a deadline.

The first measurable effect is build time. Dead code, duplicate imports, and circular dependencies do not pass the check, so nothing unnecessary makes it into the artifact.

On a mid-sized project, this consistently cuts part of the build time and eliminates the "flaky" crashes that used to be caught only on-device.

The second is the share of failures at the check stage. Type errors, null safety, and unhandled exceptions are filtered out before the code reaches review or the build.

The class of errors that used to make it to release — crashes on specific device models, incorrect state handling — is closed off at the analysis level.

The third is review speed. When style, types, and potential leaks are checked by the analyzer, the reviewer spends time on logic and architecture rather than formatting. This noticeably shortens the revision cycle and reduces the number of times a task is sent back.

The fourth is release stability. A single set of rules for all branches and developers eliminates "it works on my machine" discrepancies. The release builds predictably, and rolling back to a previous version does not turn into a root-cause investigation.

The fifth is maintenance predictability. A new engineer on the team gets the same constraints from day one, so code quality does not depend on who exactly is writing the module.

Approaches to Configuring Flutter Analyzer: Comparison of Modes

Configuring the analyzer is a choice between speed of adoption and strictness of control. A basic set of rules catches obvious errors and requires almost no maintenance, but it misses unsafe type casts and unused code.

An extended set with custom lints covers the team's architectural agreements, but requires time for maintenance and developer training.

Strict mode with build blocking provides maximum discipline: the build fails on any violation, and code with technical debt does not make it into the main branch. The downside is an increase in false positives and team resistance if you switch to it in a single step.

Therefore, in practice, gradual tightening is more often chosen: first warnings, then blocking on critical rules, and only then full strict mode.

Option When it fits Limitations
Basic set of rules Project start, small team, quick onboarding Does not catch architectural violations, weak type control
Extended with custom lints Mature team with its own agreements Requires lint maintenance, high entry barrier for newcomers
Strict mode with build blocking Critical product, high quality requirements False positives slow down releases without fine-tuning
Gradual tightening Existing project with accumulated debt Requires a transition plan and progress tracking by stages

The choice depends on the state of the codebase: for a new project, it is reasonable to lay down an extended set right away; for an existing one, move gradually, recording debt and progressively converting rules from warnings to blocking. This approach reduces the risk that strict mode will halt development on the very first day.

How we implement strict analysis: stages of work

We start with an audit: we look at the current analysis_options.yaml, the set of enabled rules, the history of warnings in the repository, and how the team responds to analyzer feedback.

It often turns out that some rules were disabled "temporarily" and have stayed that way for years, and nobody reads the warnings — which means we first need to restore trust in the tool rather than simply adding strictness.

  1. Rule audit. We analyze which checks are already working, which are suppressed via // ignore and why. We calculate the share of files with suppressions — this reveals the real pain points of the codebase.
  2. Configuration design. We assemble a set of rules for the project: strict type checks, prohibition of implicit casts, control of unused code. We enable them in stages so as not to break the build with hundreds of warnings at once.
  3. Setting up exceptions. For generated code, third-party packages, and legacy modules, we write targeted exceptions instead of global disables — this way strictness is preserved where it has an effect.
  4. Build integration. We connect the analyzer to local development and to checks in the repository: warnings become blocking for new changes, but do not break existing code.
  5. Testing on real scenarios. We run the configuration on current branches, look at the share of false positives, and adjust thresholds until the signal becomes clean.
  6. Handover to the team. We document the rules, show how to read the report and how to properly suppress a check when it is truly necessary.

After implementation, new type errors and potential crashes are caught at the stage of writing code, not in production. We'll analyze how this can be addressed on your project.

Artifacts the team keeps after setup

Setting up an analyzer is not a one-off edit in the editor, but a set of files in the repository that live alongside the project and outlast developer turnover. Below is exactly what the team keeps in hand after we close the task.

  • Analyzer configuration files — analysis_options.yaml in the project root with an enabled rule set and strict mode settings. The file is versioned together with the code: any rule change goes through review and is visible in the history.
  • Rule scheme by strictness levels — a split into mandatory checks (build errors), warnings, and informational hints. We separately record which rules are enabled for new code and which are temporarily relaxed for legacy code.
  • Exception policy — a list of files and directories excluded from checks, with the reason and owner specified. Every exception is documented as a comment in the configuration, rather than a silent ignore in the code.
  • Rule update procedure — the order in which new checks are added: first a warning, then a mandatory requirement. This gives the team time to work through accumulated findings without halting releases.
  • Documentation for developers — a short guide: how to run the check locally, how to read the report, what to do when rules conflict, and where to report a disputed trigger.
  • Build pipeline integration — a configured check at the build stage that does not let changes with critical findings through. The threshold is set so that the build fails on real defects, not on style.
  • Baseline report on the current state — a snapshot of the project at the time of handover: how many findings there are, which categories prevail, and what gets addressed first.

These artifacts remain in the repository and do not depend on us: the team can change rules, add exceptions, and extend checks on its own.

Describe your task — we will come back with an estimate and a plan of what from this list you already have and what still needs to be put together.

Case study: how strict mode sped up builds and reduced the number of failures

The client came with a Flutter application that took 22 minutes to build on every commit to the main branch, and production crashes arrived in batches once a week.

A team of six developers spent almost a day a week waiting for builds, and the on-call engineer spent another half-day triaging failed releases.

What it was like before implementation

The analyzer ran in soft mode: warnings did not block the build, and some rules were disabled altogether so as to "not get in the way." As a result, unhandled exceptions when working with the network and parsing server responses leaked into the codebase — these were exactly what caused more than half of the crashes. Each such defect was found on the user's device rather than at the check stage.

What was changed

We enabled a strict set of analyzer rules and pinned it in the project configuration so that settings would not diverge between developers' machines and the build server.

The rules that caught unhandled exceptions and potential accesses to null values were moved into the blocking category: the build stops until the issue is resolved.

Separately, we configured warning triage by levels — anything that does not affect reliability was left as hints, and we do not slow down the pipeline with them.

What it became after

Build time dropped from 22 to 14 minutes: some of the heavy checks were moved to the analysis stage, and repeated runs stopped rebuilding unchanged modules.

The class of errors involving unhandled exceptions disappeared from production entirely — in the three months after implementation, not a single crash of this type occurred.

The share of defects caught before release roughly doubled, and the on-call engineer stopped triaging emergency releases on weekends.

An important point: strict mode only works if the rules are the same for everyone and are committed to the repository. Otherwise the settings live in people's heads rather than in the project, and within a month everything returns to its original state.

How does the analyzer's strict mode differ from a regular rule set?

A regular rule set is a list of recommendations that the analyzer shows in the editor and in the inspection report. The developer sees a warning, evaluates it, and decides for themselves: fix it now, postpone it, or ignore it.

Formally, the code passes, the build builds, the release ships — the warning stays hanging in the queue. A month later, hundreds of such "postponed" warnings have accumulated, and the report stops being read by anyone except the analyzer itself.

Strict mode changes the status of a violation: a rule is converted from a hint into a condition under which the build does not build.

The difference is not in the number of rules, but in the point at which the constraint takes effect. A regular set works after the fact — at review, at triage, in the next sprint, when the context is already lost.

Strict mode takes effect before the fact: the verification pipeline stops at the analysis stage, and code with a violation physically does not make it into either the main branch or the build for testing.

This is a fundamentally different class of feedback — not "we'll deal with it later," but "it won't pass now."

A team's discipline rests not on agreements, but on the fact that a violation cannot be bypassed unnoticed. When a rule blocks the build, the developer has two paths: fix it, or explicitly and justifiably exclude the case with a note in the code.

The latter is visible at review and gets discussed — the exclusion becomes a conscious decision rather than a silent ignore. This is how a rule set stops being a wish and becomes part of the code quality contract.

There is also a cost: strict mode requires that the rules be calibrated to the project. Too broad a set blocks the build on harmless spots, and the team starts looking for workarounds instead of working.

That is why we enable it in stages — first on new code, then on modified files, and only after that on the entire codebase.

Technical doubts and answers on analyzer configuration

Configuring the analyzer almost always comes down to the same technical questions: will strict mode break existing code, will it slow down the build, what to do about false positives, and how to keep the rules up to date. Below are the answers to each.

Compatibility: if we enable strict mode, will the project stop building?

No, if the rules are introduced in stages. First we record the current state as a baseline and enable new checks only for modified files, not for the entire codebase at once. Legacy modules remain in warning mode, while new code is written according to strict rules. This way strict mode does not block releases, and the debt is reduced as edits are made.

How does this affect build speed and work in the editor?

The analysis runs incrementally: only modified files are checked, not the entire project. On a cold run the time increases insignificantly; in day-to-day work the difference is unnoticeable.

If the project is large, we split the rules into tiers — fast checks on every save, heavy ones (for example, connectivity analysis) moved into a separate step before the build.

What to do about false positives?

Each one is suppressed in a targeted way: a line with a justification in the code or an entry in the configuration stating the reason, rather than disabling the rule entirely.

We keep a list of exceptions and review it periodically — if a rule produces noise in several places, more often the problem is in the rule itself or in the architecture, not in the code.

How are the rules updated, and will this break the project?

The rules live in the repository alongside the code, and changes go through review. We test an analyzer version update on a separate branch: we look at how many new findings appeared and decide — fix them right away or add them to the plan. This way an update does not turn into an unexpected stop for the team.

Let's discuss your Flutter Analyzer setup task

Setting up Flutter Analyzer doesn't start with tweaking the configuration, but with analyzing your project: which rules are already enabled, where the analyzer stays silent, and where it produces hundreds of warnings that the team has gotten used to ignoring.

This determines whether strict mode will be painless or turn into a source of conflicts in code reviews.

To come back with an estimate and a work start plan, we need a few things from you:

  • the repository or a fragment of it with the current analysis_options.yaml;
  • a list of problems that are already getting in the way: false positives, missed errors, discrepancies between developers;
  • how the code review process is structured — what runs locally, what runs in the shared build;
  • which rules the team is ready to adopt right away, and which require a transition period;
  • the versions of the current Flutter and Dart branch, so that the rules and linters are compatible;
  • who on the client side makes decisions on disputed rules.

After that, our engineers will go through the steps: enable the base set of rules, add strict checks for types and unused code, configure exclusions for generated files, and separate strictness levels for new and legacy code.

After that — a run on the real codebase to see how many issues appear and which of them are critical.

As a result, you'll get an estimate of effort, a plan for the phased rollout of rules, and an understanding of what will change in the team's daily work.

Describe your task — we'll come back with an estimate and a plan, discuss the start of work and the support format after implementation.