Dart Linting Tutorial – Healthy Flutter Codebase with analysis_options.yaml

Nobody cares about code quality... until they feel the consequences of this negligence. Keeping the quality of your code high is absolutely crucial, especially when you work on a team. Every app is built by writing individual statements first so before even discussing high-level design patterns, you should start with project cleanliness from the ground up - by configuring more advanced linting.

Insufficient defaults

Every Flutter project comes with a handful of linter rules already enabled. For example, when you have a FloatingActionButton but you forget to specify the onPressed callback function you get warned about it.

info_message.dart

FloatingActionButton(
  child: Icon(Icons.announcement),
  // onPressed is missing - we get a warning message
);

Sure, it's nice to get warned but wouldn't it be better to get a fully-blown error? As you know, omitting a positional argument results in an error popup which is very hard to ignore.

error_message.dart

Text(/*missing positional arg*/);

Popup when trying to run the app

With omitted required named parameters though, we won't get any such helpful popups from the Dart compiler by default.

Changing severity

Fortunately, we're not limited to the default way of resolving individual linter rules! All we need to do is to edit a file called analysis_options.yaml.  New Flutter projects currently don't come with this file pre-created, so let's add it to the root of the project. Inside, we'll tweak the severity of the error message to be a fully blown error.

analysis_options.yaml

analyzer:
  errors:
    missing_required_param: error
The possible values for the severity are ignore, info, warning and error.

Now we finally get the same treatment for omitting required named arguments as we do for omitting positional arguments.

Error instead of just a "friendly reminder" info message

Enabling new rules

It's easy to change the severity of an already enabled lint rule. After all, we already got a squiggly line even while the rule called "missing_required_param" was just a warning message.  How can we enable other rules though and where can we find which rules are available? The answer is the official linter package!

You can find all of the available rules (most of them are disabled by default) at the GitHub pages of the Dart language.

Let's for example tackle the reassignment of method parameters. In regular math, input of a function cannot be magically changed inside of it. All a function should do with the input is just to use it. And look, there's a handy lint rule that we can enable!

Enabling rules is as simple as adding them to the list inside analysis_options.yaml. Since the parameter_assignments rule will produce only info messages by default, let's also change its severity.

analysis_options.yaml

linter:
  rules:
    - parameter_assignments

analyzer:
  errors:
    missing_required_param: error
    parameter_assignments: error

Finally, writing this horrible piece of code will produce a well-deserved error.

error_message.dart

void someMethod(int x) {
  x = 5; // error 🚨
}
In addition to enabling rules, you can also completely disable them. One way to do it is to change their severity to ignore. Otherwise, you can turn the linter -> rules YAML list into a YAML dictionary.
linter:
  rules:
    first_rule: false # disable
    second_rule: true # enable

Lint packages

Although looking through the list of all possible linter rules can be enlightening, I think we all have other things to spend time on. Thankfully, we can utilize packages which come with some sensible defaults regarding lint rules and their severity.

There are currently 3 such packages worth mentioning.

  1. pedantic - used internally by Google which is both a blessing and a curse since they don't want to break their codebase by enabling certain rules which are otherwise worth enabling.
  2. effective_dart - a community package with rules stemming from official Dart guidelines
  3. lint - another community package which I personally like to use

Enabling rules from a package

Just like with any other package, even lint packages need to be added to pubspec.yaml. Because linting happens only during development, it's enough to add it to dev_dependencies.

pubspec.yaml

dev_dependencies:
  lint: ^1.1.1

All of the lint packages come with a pre-configured analysis_options.yaml file. The only thing we need to do is to include it in our own file, usually in the first line.

analysis_options.yaml

include: package:lint/analysis_options.yaml
...

You can find out about the rules we just enabled by looking at the package's analysis_options.yaml. For example, we now get an info message when we use a const-capable constructor outside of a const context. (Learn all you need to know about const in a this tutorial.)

const_context.dart

Text('Info message here');
const Text('No issues here');

Additionally, we also get a handy quick-fix after pressing the CTRL + . in VS Code. Now you have no excuse not to use const 😜

Fix Dart's unreasonable defaults

Modifying analysis_options.yaml yourself opens up possibilities to fix Dart's idiotic defaults. I mean, why does not returning a value from a method produce only an insignificant info message?! The lint package we're using bumps the severity from info to warning (more prominent message). Most of the real programming languages (sorry JavaScript 😅) bombard you with an error if you forget about a return statement. Well, now you know how to bring this behavior into Dart!

analysis_options.yaml

analyzer:
  errors:
    missing_return: error

Be sure to tweak the rules and their severity to make your codebase as healthy as it possibly can be!

Matt Rešetár
 

Matt is an app developer with a knack for teaching others. Working as a senior software developer at HandCash, freelancer and most importantly developer educator, he doesn't have a lot of free time 😅 Yet he still manages to squeeze in tough workouts 💪 and guitar 🎸

>