What’s new in DCM 1.33.0

With a little delay, we are excited to announce the release of DCM 1.33.0!
This release includes 8 new rules (5 for Dot shorthands), Dot shorthands support for all existing rules and features, improved detection of unused code in various new cases (unassigned fields, setters, code that is used only in generated files and so on), automatic Dashboards project creation based on the monorepo structure, redesigned Dashboards page with support for tagging projects, and more!
Let’s go through the highlights of this release (and the full list of changes is in our changelog)!
Dashboard Improvements
New Project View and Tags
To help you group projects by your own rules, Dashboards now support custom tags in the Project List:

You can also select a tag (or several tags) on the Projects Overview tab and get a summary for a particular tag (or several tags):

External Users
Inviting users to the DCM Teams Console now also supports external users (users with an email address that doesn't match the account's domain name).

Such users also have a dedicated label:

Note, that external users can only have the "View Selected" role. Please carefully check the external user's email before granting them access to your projects.
Automatic Projects Creation
This feature is available to Enterprise users only.
You can now automatically create Dashboards projects from your monorepo structure with the help of the new --scaffold flag.
Here are the required steps:
-
Add a new
projectentry to thedcmconfig in every package'sanalysis_options.yamlfile (this entry can be any string and will be used both as the unique project id and its name, which you can change later):analysis_options.yamldcm:
project: my_monorepo_package -
Add the
--scaffoldflag to your uploads (and use the Global Project Key for the--projectoption). -
Keep the
--scaffoldflag in every run (not just the first one).
When using --scaffold, the tool will compare unique project ids and create missing projects. Then, it will assign uploaded data to each corresponding project.
If the unique id changes, it will only create a new project and never delete any existing ones.
Unused Code Detection Improvements
Unassigned Fields
Now, if a public or private field is referenced, but never assigned, the tool will report such fields as unused code.
For example,
class AssignmentsExample {
String? inner;
void dispose() {
print(inner);
final value = inner?.split(',');
}
}
here, even though the field is referenced (print(inner) and inner?.split(',')), it actually never receives a value making the field itself unused and all the code that references this field to always result in null.
Collection Methods and Properties
Another improvement to the unused code detection covers collection methods and statements with properties.
For example,
class WithCollection {
final directMap = <String, String>{};
WithCollection();
void notUsage() {
directMap['hello'] = 'hi';
directMap.length;
}
}
here, even though, both directMap['hello'] = 'hi' and directMap.length reference the directMap field, both of them do not indicate the usage of that field.
With this release, such cases will be reported as unused code.
Asserts
With this release, any code that is used only in asserts is now reported as unused.
For example,
class Some {
final String? value
final int another;
const Some(this.value, this.another): assert(another > 0);
void work() {
print(value);
}
}
here, the only place the another field is used is the assert initializer.
Setters
Prior to this release, setters were excluded by default even if they were never used to support cases when developers always create a pair of a getter and setter.
With this release we are removing this exception making the command always report unused setters.
If you still wish to keep unused setters in your codebase, consider adding ignore comments (e.g. // ignore: unused-code).
Generated Code (moved to 1.34)
This feature was initially added in 1.33 and removed in 1.33.1 as it had a lot of false positives. We plan to bring it back in 1.34.
In some cases, for example, when you declare models for your app and then generate copyWith and other methods via code generation, such classes can still be referenced in the generated files, but not by any of your code.
With this change we aim to highlight such classes in the generated files so you can quickly identify and remove them.
IDE Integration
With this release we are removing the IDE integration for unused code and files detection.
The integration had several issues (including memory leaks, not analyzing excluded files properly, not supporting all issues reported by the check-unused-code command and so on).
We plan to reintroduce this feature later in 2026 while also addressing all the known issues.
Rule Updates
Dot Shorthands
All lint rules (and other features) now support latest language changes (including Dot shorthands).
Notable Changes
avoid-unused-local-variable
Now supports the same checks as the check-unused-code command when it comes to collection methods and properties:
void fn() {
final List<String> items = {}; // LINT
items.add('');
items.addAll(['']);
items.clear();
items.remove('');
items.removeWhere((element) => false);
items.retainWhere((element) => false);
final str = '123'; // LINT
str.length;
}
here, even though the items list is mutated, it is never passed to any other function therefore making the variable actually unused. Same for str.
avoid-unassigned-late-fields
Now only highlights private late fields (or late fields for private declarations):
class Test {
late int anotherField;
late int uninitializedField;
late int _privateLate; // LINT
void method() {
anotherField = 1;
}
}
New Rules
All new rules for Dot shorthands are also available to the Starter plan users.
prefer-returning-shorthands
Suggests returning dot shorthands from an expression function body.
Function and methods declarations already have an explicit return type and in cases when that type is the same as the returned instance, the instance can be simplified to a dot shorthand without reducing readability.
// LINT: This instance type matches the return type and can be replaced with a dot shorthand.
// Try using the dot shorthand constructor.
SomeClass getInstance() => SomeClass('val');
// LINT: This instance type matches the return type and can be replaced with a dot shorthand.
// Try using the dot shorthand constructor.
SomeClass getInstance() => SomeClass.named('val');
// LINT: This instance type matches the return type and can be replaced with a dot shorthand.
// Try using the dot shorthand constructor.
SomeClass getInstance(bool flag) =>
flag ? SomeClass('value') : SomeClass.named('val');
class SomeClass {
final String value;
const SomeClass(this.value);
const SomeClass.named(this.value);
}
To fix these issues, use dot shorthands in all highlighted places:
SomeClass getInstance() => .new('val');
SomeClass getInstance() => .named('val');
SomeClass getInstance(bool flag) =>
flag ? .new('value') : .named('val');
This rule also comes with auto-fix.
prefer-shorthands-with-static-fields
Suggests using dot shorthands with static fields.
When a static field type matches the type of its class, such fields can be used as dot shorthands (without an explicit class prefix).
void fn(SomeClass? e) {
switch (e) {
// LINT: Prefer dot shorthands instead of explicit class prefixes. Try removing the prefix.
case SomeClass.first:
print(e);
}
final v = switch (e) {
// LINT: Prefer dot shorthands instead of explicit class prefixes. Try removing the prefix.
SomeClass.first => 1,
_ => 2,
};
// LINT: Prefer dot shorthands instead of explicit class prefixes. Try removing the prefix.
final SomeClass another = SomeClass.first;
// LINT: Prefer dot shorthands instead of explicit class prefixes. Try removing the prefix.
if (e == SomeClass.first) {}
}
// LINT: Prefer dot shorthands instead of explicit class prefixes. Try removing the prefix.
void another({SomeClass value = SomeClass.first}) {}
// LINT: Prefer dot shorthands instead of explicit class prefixes. Try removing the prefix.
SomeClass getClass() => SomeClass.first;
class SomeClass {
final String value;
const SomeClass(this.value);
static const first = SomeClass('first');
static const second = SomeClass('second');
}
To fix these issues, remove the class name prefix:
void fn(SomeClass? e) {
switch (e) {
case .first:
print(e);
}
final v = switch (e) {
.first => 1,
_ => 2,
};
final SomeClass another = .first;
if (e == .first) {}
}
void another({SomeClass value = .first}) {}
SomeClass getClass() => .first;
Object getObject() => SomeClass.first;
This rule also comes with auto-fix.
prefer-shorthands-with-enums
Suggests using dot shorthands with enums.
void fn(MyEnum? e) {
switch (e) {
// LINT: Prefer dot shorthands instead of explicit enum prefixes. Try removing the enum prefix.
case MyEnum.first:
print(e);
}
final v = switch (e) {
// LINT: Prefer dot shorthands instead of explicit enum prefixes. Try removing the enum prefix.
MyEnum.first => 1,
_ => 2,
};
// LINT: Prefer dot shorthands instead of explicit enum prefixes. Try removing the enum prefix.
final MyEnum another = MyEnum.first;
// LINT: Prefer dot shorthands instead of explicit enum prefixes. Try removing the enum prefix.
if (e == MyEnum.first) {}
}
// LINT: Prefer dot shorthands instead of explicit enum prefixes. Try removing the enum prefix.
void another({MyEnum value = MyEnum.first}) {}
// LINT: Prefer dot shorthands instead of explicit enum prefixes. Try removing the enum prefix.
MyEnum getEnum() => MyEnum.first;
enum MyEnum { first, second }
To fix these issues, remove the enum prefix:
void fn(MyEnum? e) {
switch (e) {
case .first:
print(e);
}
final v = switch (e) {
.first => 1,
_ => 2,
};
final MyEnum another = .first;
if (e == .first) {}
}
void another({MyEnum value = .first}) {}
MyEnum getEnum() => .first;
Object getObject() => MyEnum.first;
This rule also comes with auto-fix.
avoid-nested-shorthands
Warns when a dot shorthand invocation has an argument that is also a dot shorthand.
Nested dot shorthands significantly reduce readability (to a point where every instance is .new(.new(...))) and should be generally avoided.
void fn() {
// LINT: Avoid nested dot shorthands as they significantly reduce readability. Try adding explicit types.
final Another a = .new(.new(version: .new('val')));
// LINT: Avoid nested dot shorthands as they significantly reduce readability. Try adding explicit types.
final a = Another(.new(version: .new('val')));
}
class SomeClass {
final String value;
const SomeClass(this.value);
}
class Some {
final SomeClass version;
const Some({required this.version});
}
class Another {
final Some some;
Another(this.some);
}
prefer-shorthands-with-constructors
Suggests using dot shorthand constructor invocations for the configured list of classes.
Padding(
// LINT: Prefer dot shorthands instead of explicit class instantiations.
// Try using the dot shorthand constructor.
padding: EdgeInsets.symmetric(
horizontal: horizontalPadding(screenWidth),
vertical: 12,
),
);
BoxDecoration(
color: Colors.transparent,
// LINT: Prefer dot shorthands instead of explicit class instantiations.
// Try using the dot shorthand constructor.
border: Border.all(
color: AppColors.primary.withOpacity(_controller.value),
width: 2,
),
// LINT: Prefer dot shorthands instead of explicit class instantiations.
// Try using the dot shorthand constructor.
borderRadius: BorderRadius.circular(18),
);
Additionally, this rule supports a config option called entries to configure a list of constructors that should be converted to dot shorthands.
For example,
dcm:
rules:
- prefer-shorthands-with-constructors:
entries:
- EdgeInsets
- BorderRadius
- Radius
- Border
- MyClass
This rule also comes with auto-fix.
newline-before-constructor
Enforces a blank line before a constructor declaration.
For example,
class Wrong {
Wrong();
Wrong.first(); // LINT: Missing a blank line before this constructor declaration. Try adding it.
Wrong.second(); // LINT: Missing a blank line before this constructor declaration. Try adding it.
}
is expected to be
class Wrong {
Wrong();
Wrong.first();
Wrong.second();
}
This rule also comes with auto-fix.
avoid-always-null-parameters
Warns when a private function or method has a parameter that always gets null. Such parameters can be removed.
class Some {
void work() {
_fn(null);
}
// LINT: This parameter always gets a null value. Try removing it.
void _fn(String? nullable) {
if (nullable == null) {
// handle nullable case
} else {
// handle regular case
}
}
}
avoid-unassigned-fields
Warns when a private field is not assigned a value.
For example,
class Test {
final _field = 'string';
// LINT: This field is never assigned a value within its declaration.
// Try assigning a value or removing this field.
int? _privateLate;
}
here, _privateLate never gets a value and should be either removed or assigned a value.
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.
