Skip to main content

What’s new in DCM 1.10.0

· 9 min read

Cover

Today we’re excited to announce the release of DCM 1.10.0!

New command to deactivate the current device to help you manage your license activations, 14 new rules, general UX improvements and displaying the recommended rules for the Trail licenses by default 🚀.

Let’s go on a quick journey together to explore all updates!

New "dcm deactivate" command

Although we have only received a few requests for help with transferring a license activation to a new device, we decided to introduce a separate command to simplify the overall process.

Now you can simply call the "dcm deactivate" command and your current device will be deactivated with one activation being restored to your current license, meaning you will be able to activate a new device without any problems or support requests.

For Teams version users, this command can help you transfer a license when a developer leaves your team. Please note that for Teams users, deactivating any device will lock this device (you won't be able to activate the same DCM license on this device in the future). This behavior is intentional and done for security reasons. If you deactivated your device by mistake, please contact our support team.

To simplify the onboarding process for new Trial users, the "recommended" preset is now enabled by default, even if DCM is not configured in the analysis_options file. We hope this change will help speed up product evaluation during the trial period.

You can disable this behavior by adding any valid DCM config to the analysis_options file or by using any license version other than Trial.

General UX improvements

In this release, we have reworked the warning messages for most rules to make them more actionable. This change should help less experienced developers better understand the problems that DCM highlights and how to solve them.

We've also reduced the highlighted area for some rules to make them less annoying.

In the next release we plan to improve the overall state of the documentation, including a rewrite of the Getting Started section, introducing several user guides, as well as updating the example rules and adding more information about the problem behind each rule.

We hope these changes will help us better explain DCM's features and help you get started with the product faster. If you have any suggestions on what else can be improved, feel free to reach out on our Discord.

New rules

Get It

avoid-functions-in-register-singleton. Warns when a function is passed to registerSingleton.

Passing a function may produce unexpected results (ex. getting a new instance for each new call) and can be introduced during a refactoring. Consider passing an object instance, or use registerFactory or registerLazySingleton instead.

For example,

GetIt.I.registerSingleton(() => SomeClass());

should be rewritten to

GetIt.I.registerSingleton(SomeClass());

or

GetIt.I.registerSingleton(() => SomeClass());

Flutter

avoid-single-child-column-or-row. Warns when a Column or Row widget has only one child.

A Column or Row widget with only one child is usually a sign of a mistake. If you want to center the child widget, prefer the Center widget instead.

For example,

Widget build(BuildContext context) {
return Row(children: [
SomeWidget(),
]);
}

should be rewritten to

Widget build(BuildContext context) {
return Row(children: [
SomeWidget(),
SomeWidget(),
]);
}

or

Widget build(BuildContext context) {
return Center(child: SomeWidget());
}

prefer-sliver-prefix. Warns when a widget that returns a Sliver... widget does not have a Sliver prefix in its name.

For example,

class MyOtherWidget extends StatelessWidget {
Widget build() {
return SliverWidget();
}
}

should be

class SliverMyOtherWidget extends StatelessWidget {
Widget build() {
return SliverWidget();
}
}

Intl

prefer-providing-intl-examples. Warns when the Intl.message(), Intl.plural(), Intl.gender() or Intl.select() methods are invoked without an examples argument or have it incomplete.

For example,

static String simpleTitleWithoutExample(String source) {
return Intl.message(
'title',
name: 'SomeButtonClassI18n_simpleTitleWithoutExample',
args: [source],
);
}

static String titleWithManyParameter(String name, int value) {
return Intl.message(
'title $name, value: $value',
examples: const {'name': 'name'},
name: 'SomeButtonClassI18n_titleWithManyParameter',
args: [name, value],
);
}

here, the first method does not pass the examples argument at all and the second one passes an example only for one of the parameters (name). The rule will highlight both these cases.

Common

avoid-local-functions. Warns when a function declaration is a local function.

Local functions complicate the readability of the containing function by inverting its flow (you see a lot of code at first that will be somehow used later) and can lead to unexpected inclusion of outer scope variables.

For example,

bool someFunction(String externalValue) {
final value = 'some value';

bool isValid() {
// some
// additional
// logic
}

return isValid() && externalValue != value;
}

should be rewritten to

bool someFunction(String externalValue) {
final value = 'some value';

return isValid() && externalValue != value;
}

bool isValid() {
// some
// additional
// logic
}

avoid-unnecessary-local-late. Warns when a variable is assigned in all code branches before use.

The analyzer can determine statically whether a final local variable is assigned in all code paths before it is accessed. For that reason, it is not necessary to mark the variable as late.

Removing the late keyword can help you during a refactoring if you accidentally forget to assign a value to the variable. The variable will be highlighted by the analyzer.

For example,

void bad(int value) {
late final String foo;
if (value == 1) {
foo = 'one';
} else {
foo = 'two';
}
...
}

should be rewritten to

void good(int value) {
final String foo;
if (value == 1) {
foo = 'one';
} else {
foo = 'two';
}
...
}

avoid-unnecessary-getter. Warns when a getter provides access to an existing final field without any additional logic.

For example,

abstract class PageRoute<T> extends ModalRoute<T> {

bool get barrierDismissible => _barrierDismissible; // LINT
final _barrierDismissible = false;
}

should be rewritten to

abstract class PageRoute<T> extends ModalRoute<T> {

final barrierDismissible = false;
}

match-getter-setter-field-names. Warns when a getter or setter do not access at least one field with the name that matches the getter or setter name.

For example,

class Point {
int _x;
int _y;

int get x => _x;
set x(int newValue) {
_x = newValue;
}

int get y => _x;
set y(int newValue) {
_x = newValue;
}
}

here, it's really hard to spot that the y getter should access the _y variable instead of _x.

So the example should be rewritten to

class Point {
int _x;
int _y;

int get x => _x;
set x(int newValue) {
_x = newValue;
}

int get y => _y;
set y(int newValue) {
_y = newValue;
}
}

avoid-wildcard-cases-with-enums. Warns when a switch on the Enum value has a wildcard pattern case.

For example,

enum MyEnum { first, second }

void someFn() {
final value = MyEnum.first;

final result = switch (value) {
MyEnum.first => 1,
_ => 2,
};
}

should be rewritten to

enum MyEnum { first, second }

void someFn() {
final value = MyEnum.first;

final result = switch (value) {
MyEnum.first => 1,
MyEnum.second => 2,
};
}

avoid-accessing-collections-by-constant-index. Warns when a collection is accessed by a constant index inside a for loop.

For example,

const _array = [1, 2, 3, 4, 5, 6, 7, 8, 9];

const constIndex = 1;
final finalIndex = 1;

void func() {
for (final element in _array) {
_array[0];
_array[constIndex];
_array[finalIndex];
}
}

here, all these accesses within the loop are either a sign of a mistake or can be moved out of the loop and declared only once.

avoid-nullable-parameters-with-default-values. Warns when a parameter with the default value is marked as nullable.

For example,

void fn({int? a = 5}) {}

void fn([int? a = 5]) {}

here, removing the nullability suffix from both parameter types won't change the code, but can be confusing for any reader (as if a null value is possible).

So the example should be rewritten to

void fn({int a = 5}) {}

void fn([int a = 5]) {}

or

void fn({int? a}) {}

prefer-named-imports. Warns if the configured import is not a named import.

Can help your team both standardize the named imports across your codebase and never forget to add a name to certain imports.

For example, if you want the path package always have a p name

import 'package:path/path.dart';
import 'package:path/path.dart' as something;

should be rewritten to

import 'package:path/path.dart' as p; 

avoid-extensions-on-records. Warns when an extension is declared on a record type.

For example,

typedef Record = (String, String);

extension RecordX on Record {}

here, the extensions will be available to every (String, String) record in your codebase, which can be sometimes confusing.

avoid-long-files. Warns when a file length (number of lines) exceeds the configured maximum.

Long files can be hard to read and maintain. Consider splitting them into several smaller ones.

What’s next

New commands. Reworking metric HTML reports unblocked other commands that need an HTML report as well. We hope to add them in the upcoming release to make DCM cover even more use-cases.

Improving the "unused code in the IDE" feature. There are plenty areas of improvements (for example, this feature only supports the "monorepo" mode for now, can be a bit annoying when you just created a new class and so on). That's why we are releasing it as "Beta" (and disabled by default) and will work on improving it in the upcoming releases.

Sharing your feedback

If there is something you miss from DCM right now or want us to make something better or have any general feedback - join our Discord server!