Skip to main content

What’s new in DCM 1.32.0

· 13 min read

Cover

With a little delay, we are excited to announce the release of DCM 1.32.0!

This release includes 12 new rules, 21 new quick fix, optimized memory consumption for remaining DCM commands, improved support for pubspec rules across all DCM features, new metrics config to pass multiple thresholds for different paths, new "unset" metric threshold, new role for accessing only a selected number of projects in DCM Dashboards and improved UX for adding new seats to your subscription, and more!

Let’s go through the highlights of this release (and the full list of changes is in our changelog)!

Dashboard Improvements

New Role and External Users

To help you granularly manage access to your DCM projects, this release includes a new "View Selected" role for which you can select a list of projects a particular user or developer can access:

info

We've also removed the "Dashboards access" toggle and made the UX for managing developers and users more similar.

Additionally, you can now invite external users (users with an email address that does not match your account domain address), but their role is limited to "View Selected" only:

External Users

Improved Baseline

To reduce confusion between regular and baselined issues, cards with the active baseline state now have several distinctive elements:

Updated Billing Page and Seat Selection

The Billing page also got a few improvements.

First, it shows more details about the current plan and the numbers of added developers, users and projects:

Improved Billing Page

When it comes to adding new seats to your subscription, the modal dialog now includes more information about the cost of each seat and the billing interval.

Improved Seats Selection

Metrics Improvements

note

If you have any feedback or feature requests for current metrics, any metric-related features or new metrics, please let us know on our Discord or via email [email protected].

We plan to ship a lot of changes and improvements (including documentation updates, new metrics and more) in the upcoming 2 releases (1.33 and 1.34), so your feedback can help us make metrics even better!

Multiple Threshold

With this release we are introducing a new configuration for all metrics to accept multiple thresholds for different paths:

analysis_options.yaml
dcm:
metrics:
cyclomatic-complexity:
threshold: 20
entries:
- threshold: 10
paths:
- '.*some_folder.*'
- '.*some_file.dart'
- threshold: 15
paths:
- '.*another_folder'

For the example above, all files and folders that match .*some_folder.* or .*some_file.dart will have the cyclomatic complexity threshold equal to 10, the ones that match .*another_folder - 15, and for all other files the threshold will be 20.

info

Configuring the main threshold is required as that threshold will be used for file that are not covered by the entries config (if no threshold is set, the default threshold will be used instead).

We hope this change will make metrics usage even more convenient especially for cases when you want one set of thresholds for your UI widgets and another for business logic.

"unset" Threshold

On top of the multiple thresholds, you can now set a metric threshold to "unset" to avoid getting any violations.

analysis_options.yaml
dcm:
metrics:
cyclomatic-complexity:
threshold: unset

or for multiple thresholds:

analysis_options.yaml
dcm:
metrics:
cyclomatic-complexity:
threshold: 20
entries:
- threshold: 10
paths:
- '.*some_folder.*'
- '.*some_file.dart'
- threshold: unset
paths:
- '.*another_folder'

This threshold can be helpful if you want to simply collect metric values (for example, when uploading to DCM Dashboards).

"maximum-nesting-level" Updates

The maximum-nesting-level metric now covers additional cases (such as multiline list, map and set literals, if and for collection elements and switch cases without a block body).

For example, prior to this release, the code below had a nesting level of 1 (which is clearly not the case).

Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(),
Text(),
const SizedBox(height: 8),
Row(
children: [
for (var i = 0; i < 5; i++) ...[
const SizedBox(width: 4),
]
],
),
],
),
),

Now, it has a nesting level of 5 (with 3 list literals and 1 collection for element).

Improved HTML Reports

The metrics HTML report got a few UX improvements as well.

For example, the single file shows a summary for each metrics on the left (same as for other pages) and each summary now includes a threshold.

Plus, you can now open the file in VS Code with just one click by clicking "Open in VS Code".

Updated HTML Reporter

Memory Usage Improvements (part 2)

If you have checked out our previous release, there was a separate section dedicated to memory optimizations.

Unfortunately, that release didn't include a fix for all commands (and check-parameters and analyze-widgets were left without an optimization).

Now, all commands support the optimization and you should see the results of it when running dcm run --all .

Better Support for Pubspec Rules

With this release, all pubspec rules now have better integration with all other DCM features.

For example, you can now apply quick fixes and add # ignore_for_file: and # ignore: comments:

Plus, they are now fully supported in the analysis_options.yaml config (including autocompletion, code actions and configuration validation):

And last but not least, pubspec rules are now shown in the results of dcm init lints-preview and are applied with built-in presets!

Improved Detection of Pub Cache and Flutter SDK

If you are using tools like asdf, with the release we are happy to announce an improved support for automatic Flutter SDK detection and the overall improvement of our SDK detection logic.

Now, if the tool fails to locate the SDK in your PATH, here is the list of all places it checks to locate the SDK (in that particular order):

  1. --sdk-path CLI option (if present)
  2. the DCM_SDK_PATH env variable (if present)
  3. the FLUTTER_ROOT env variable (if present)
  4. fvm configuration
  5. .dart_tool/package_config.json
  6. which flutter / where flutter (depending on the platform)

We hope this change reduces the need of passing --sdk-path manually even more!

Additionally, we've improved the detection of external code (the tool treats all code from pub.dev packages as external) and it now correctly picks up the standard PUB_CACHE environment variable recognized by pub.

New Rules

Common

avoid-unremovable-callbacks-in-listeners

Warns when an addListener invocation receives a callback that cannot be unregistered.

Since no function expression (() => ... or () { ... }) is equal to any other function expression, passing it to addListener creates a callback that cannot be unregistered via removeListener and thus leads to a memory leak.

Same applies to extension method tear-offs as they are never equal to each other (including methods with the same signature).

class SomeClass {
final _someListener = Listener();

void work() {
// LINT: Avoid passing function expressions as callbacks since they are never equal to each other and can't be unregistered.
_someListener.addListener(() {});

// LINT: Avoid passing function expressions as callbacks since they are never equal to each other and can't be unregistered
_someListener.addListener(() => null);
}
}

To fix that, assign the function expression to a field or variable and use that field or variable in both addListener and removeListener.

You can also configure this rule to pass additional methods (for example, if you have a custom method that adds a listener).

avoid-constant-switches

Warns when a switch expression or statement has a constant expression.

Constant expressions in switches always evaluate to the same value and usually indicate a typo or a bug.

void fn() {
// LINT: This expression is constant and will always evaluate to the same value.
// Try changing this expression.
final value = switch (Some._val) {
...
};

// LINT: This expression is constant and will always evaluate to the same value.
// Try changing this expression.
switch (_another) {
...
};
}

abstract final class Some {
static const _val = '1';
}

const _another = 10;

To fix this issue, try either using a different value or replacing the switch statement/expression with the branch that matches the constant value.

avoid-constant-conditions

Warns when both sides of a binary expression are constant.

Constant conditions always evaluate to true or false and usually indicate a typo or a bug.

void fn() {
// LINT: This condition is constant and will always evaluate to 'false'.
// Try changing this condition.
if (_another == 11) {
print(value);
}

// LINT: This condition is constant and will always evaluate to 'true'.
// Try changing this condition.
if (Some._val == '1') {
print('hello');
} else {
print('hi');
}
}

abstract final class Some {
static const _val = '1';
}

const _another = 10;

avoid-unused-local-variable

Warns when a local variable is not referenced and can be removed or renamed to _.

void fn() {
var a = 1;
// LINT: This local variable is not used. Try removing the variable or using it.
var b = a;

print(a);
}

void withRecord() {
// LINT: This local variable is not used. Try removing the variable or using it.
final (a, b) = (1, 2);

print(a);
}

match-base-class-default-value

Warns when the default value of a parameter in an overridden method does not match the default value of the base class parameter.

Changing the default value of a parameter can lead to subtle bugs when some subclasses of the base class have different behavior due to different default values. Additionally, this rule helps spot changes in the base class default values that have not been propagated to subclasses.

class A {
void work({bool defaultValue = false}) {
...
}
}

class B implements A {
// LINT: This default value does not match the default value of the base class parameter.
// Try changing it to match the base class default value.

void work({bool defaultValue = true}) {
...
}
}

This rule comes with an IDE fix.

prefer-test-structure

Warns when a test is not separated in three sections: arrange, act, assert.

Using those sections in your tests helps keep the same structure for all tests and ensures that tests are not doing multiple rounds of arrange and act.

For example,

void main() {
test('bad unit test', () {
// act
final a = 1; // LINT: This test is missing an arrange section before this act section. Try refactoring this test.
final b = 2;

// arrange
final c = a + 1;

// assert
expect(b, c); // LINT: This test is missing an act section before this assert section. Try refactoring this test.
});
}

is expected to be

void main() {
test('good unit test', () {
// arrange
final a = 1;
final b = 2;

// act
final c = a + 1;

// assert
expect(b, c);
});
}

Equatable

prefer-equatable-mixin

Warns when a class extends Equatable instead of mixing in the EquatableMixin.

There is no upside in extending Equatable, but using the mixin allows you to also extend another class while keeping all equatable features.

// LINT: The class should mix in 'EquatableMixin' instead of extending 'Equatable'.
class Person extends Equatable {
const Person(this.name);

final String name;


List<Object> get props => [name];
}

This rule comes with an IDE fix.

Pub

dependencies-ordering

Suggests sorting pubspec dependencies in alphabetical order (A to Z).

Works for dependencies, dev_dependencies and dependency_overrides pubspec section.

For example,

pubspec.yaml
name: some_package
description: ...
version: 1.0.0

dependencies:
xml: '>=5.3.0 <7.0.0'
yaml: 3.1.0+1
test: 1.22.2 # LINT: Dependencies are not sorted alphabetically. Try sorting them from A to Z.

is expected to be

pubspec.yaml
name: some_package
description: ...
version: 1.0.0

dependencies:
test: 1.22.2
xml: '>=5.3.0 <7.0.0'
yaml: 3.1.0+1

Additionally, this rule supports a config option called flutter to separately configure the order of flutter-related dependencies.

For example,

analysis_options.yaml
dcm:
pubspec-rules:
- dependencies-ordering:
flutter: first

will require flutter dependencies be first regardless of the alphabetical order.

pubspec.yaml
name: some_package
description: ...
version: 1.0.0

dependencies:
flutter:
sdk: flutter
collection: ^1.18.0
xml: '>=5.3.0 <7.0.0'
yaml: 3.1.0+1

This rule comes with an IDE fix.

newline-before-pubspec-entry

Enforces a blank line between the configured pubspec entries.

For example,

analysis_options.yaml
dcm:
pubspec-rules:
- newline-before-pubspec-entry:
entries:
- environment
- dependencies
- dev_dependencies
- flutter
- dependencies:collection

will highligh the following cases

pubspec.yaml
name: some_package
description: ...
version: 1.0.0
environment: # LINT: Missing a blank line before this entry. Try adding it.
sdk: '>=3.6.0 <4.0.0'
flutter: '>=3.27.1'
dependencies: # LINT: Missing a blank line before this entry. Try adding it.
flutter:
sdk: flutter
collection: ^1.18.0 # LINT: Missing a blank line before this entry. Try adding it.
meta: ^1.12.0
ordered_set: ^8.0.0
vector_math: ^2.1.4
dev_dependencies: # LINT: Missing a blank line before this entry. Try adding it.
benchmark_harness: ^2.3.1

until they are formatted to

pubspec.yaml
name: some_package
description: ...
version: 1.0.0

environment:
sdk: '>=3.6.0 <4.0.0'
flutter: '>=3.27.1'

dependencies:
flutter:
sdk: flutter

collection: ^1.18.0
meta: ^1.12.0
ordered_set: ^8.0.0
vector_math: ^2.1.4

dev_dependencies:
benchmark_harness: ^2.3.1

This rule comes with an IDE fix.

pubspec-ordering

Suggests sorting pubspec entries in the configured order.

For example,

pubspec.yaml
version: 1.0.0
description: ... # LINT: 'description' should be before 'version'
name: some_package # LINT: 'name' should be before 'description'

is expected to be

pubspec.yaml
name: some_package
description: ...
version: 1.0.0

This rule comes with an IDE fix.

add-resolution-workspace

Warns when a package is missing the resolution: workspace entry.

If you are using pub workspaces, this rule helps to ensure that all subpackages are included into the workspace.

pubspec.yaml
name: some_package # LINT: Add 'resolution: workspace' to include the package into your pub workspace.
description: ...
version: 1.0.0

This rule comes with an IDE fix.

prefer-correct-topics

Warns when the topics section has incorrect entries.

You can find all requirements for this section on the official website.

pubspec.yaml
topics:
- Network # LINT: The topic name should only use lowercase alphanumeric characters or hyphens and must start with a lowercase alphabet character
- A # LINT: The topic name should have between 2 and 32 characters

What’s next

To learn more about upcoming features, keep an eye on our public roadmap.

And to learn more about our upcoming videos and "Rules of the Week" content, subscribe to our Youtube Channel.

Sharing your feedback

If there is something you miss from DCM right now, want us to make something better, or have any general feedback — join our Discord server! We’d love to hear from you.