A strict static analyzer and script for formatting Dart code.
Designed to solve problems in a project at the design and support stages.
Script:
- automatic fix of detected linter errors;
- sorting in alphabetical order of the fields of
.arb
files; - sorting in alphabetical order import, export and part declarations of
.dart
files; - converting relative import declarations of
.dart
files; - deleting unused import declarations of
.dart
files; - sorting in alphabetical order of
dependencies
,dev_dependencies
,dependency_overrides
keys inpubspec.yaml
file; - Dart code formatting.
Analyzer:
- strict architectural rules;
- strict stylistic rules;
- not strict rules of approach.
The package allows you to maintain projects of any size in terms of code base and localizations in a clean and tidy manner, focusing on the fact that the linter rules were not violated and the script was run before merging the code base.
- Add two packages to the
pubspec.yaml
file in thedev_dependencies
section:
dev_dependencies:
custom_lint: ^latest_version
pedant: ^latest_version
- Add the inclusion of a custom analyzer to the
analysis_options.yaml
file:
analyzer:
plugins:
- custom_lint
# For rules configuration add this inclusion
custom_lint:
rules:
- pedant:
It is advisable to restart the IDE after connecting the analyzer.
Current default configuration:
custom_lint:
rules:
- pedant:
add_bloc_cubit_event_state_file: true
add_bloc_cubit_state_postfix: true
add_bloc_cubit_state_sealed: true
add_bloc_event_postfix: true
add_bloc_event_sealed: true
add_bloc_postfix: true
add_class_postfix_by_keyword_list: null
add_class_postfix_by_path_list: null
add_class_prefix_by_keyword_list: null
add_class_prefix_by_path_list: null
add_comma: true
add_const_constructor: true
add_const: true
add_constructor: true
add_controller_postfix: true
add_cubit_postfix: true
add_extension_postfix: true
add_if_braces: true
add_mixin_postfix: true
add_override: true
add_this: true
add_type: true
delete_bloc_cubit_dependent_bloc_cubit: true
delete_bloc_cubit_dependent_flutter: true
delete_bloc_cubit_public_property: true
delete_class_postfix_list:
- Impl
- Implementation
- Model
delete_class_prefix_list: null
delete_function_list:
- print
- debugPrint
- debugPrintThrottled
delete_new: true
# delete_package_list: - Check note
# delete_type_list: - Check note
delete_widget_method: true
edit_arrow_function: true
edit_constructor_private_named_parameter: true
edit_constructor_public_named_parameter: true
edit_file_length_by_path_list: null
edit_function_private_named_parameter: true
edit_function_public_named_parameter: true
edit_multiple_variable: true
edit_private_in_function: true
edit_relative_import: true
edit_variable_name_by_type: true
priority: 100
Note:
Default list of delete_package_list check here.
Default list of delete_type_list check here.
The script is designed from the point of view of maximum coverage and bringing order to the project.
Run the script:
dart run pedant
--no-fix - disable fix of analyzed linter problems;
--no-dart-fix - disable fix of analyzed Dart linter problems;
--no-sort-arb - disable alphabetical sorting of .arb files;
--no-convert-import - disable alphabetical sorting of declarations of imports, exports and parts and deleting unused imports of .dart files;
--no-sort-pubspec - disable alphabetical sorting dependencies in the pubspec.yaml file;
--no-dart-format - disable final formatting at the script completion stage;
All found files are sorted in alphabetical order.
The script sorts and converts Dart declarations of imports, exports and parts in the following and alphabetical order:
-- Lines before declarations --
1. Library declaration
library example;
2. Export declarations
export 'package:example/one.dart';
export 'package:example/two.dart';
3. Dart import declarations
import 'dart:async';
import 'dart:io';
4. Flutter import declarations
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
5. Package import declarations
import 'package:bloc/bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
6. Project import declarations
import 'package:example/one.dart';
import 'package:example/two.dart';
7. Part declarations
part 'one.dart';
part 'two.dart';
8. Part of declaration
part of 'one.dart';
-- Rest lines of the code --
Unused import declarations will be deleted.
Only files located in the /bin/...
and /lib/...
directories are sorted.
All dependencies in dependencies
, dev_dependencies
and dependency_overrides
keys are sorted in alphabetical order in pubspec.yaml
file.
Linter has next rules:
The Bloc
/Cubit
state and event class must be located either in the same file or in the same visibility area through part
/part of
.
// BAD:
import 'package:example/example_event.dart';
import 'package:example/example_state.dart';
class ExampleBloc extends Bloc<ExampleEvent, ExampleState> {
...
}
// GOOD:
class ExampleBloc extends Bloc<ExampleEvent, ExampleState> {
...
}
sealed class ExampleEvent {
...
}
sealed class ExampleState {
...
}
// GOOD:
part of 'example_event.dart';
part of 'example_state.dart';
class ExampleBloc extends Bloc<ExampleEvent, ExampleState> {
...
}
The Bloc
/Cubit
state class must have a State
postfix.
// BAD:
sealed class ExampleSt {
...
}
// GOOD:
sealed class ExampleState {
...
}
The Bloc
/Cubit
state class must be declared with the sealed
keyword.
// BAD:
class ExampleState {
...
}
// GOOD:
sealed class ExampleState {
...
}
The Bloc
event class must have the Event
postfix.
// BAD:
sealed class ExampleEv {
...
}
// GOOD:
sealed class ExampleEvent {
...
}
The Bloc
event class must be declared with the sealed
keyword.
// BAD:
class ExampleEvent {
...
}
// GOOD:
sealed class ExampleEvent {
...
}
The Bloc
class must have a Bloc
postfix.
// BAD:
class ExampleBlc extends Bloc<ExampleEvent, ExampleState> {
...
}
// GOOD:
class ExampleBloc extends Bloc<ExampleEvent, ExampleState> {
...
}
Classes that contain keywords from the list must have the appropriate postfix.
Example:
add_class_postfix_by_keyword_list:
-
name: Base
keywordList:
- base
// BAD:
base class Example {
...
}
// GOOD:
base class ExampleBase {
...
}
Classes that are located along the path from the list must have the appropriate postfix. Example:
add_class_postfix_by_path_list:
-
path: lib/src
nameList:
- Model
// BAD:
base class Example {
...
}
// GOOD:
base class ExampleModel {
...
}
Classes that contain keywords from the list must be prefixed accordingly.
Example:
add_class_prefix_by_keyword_list:
-
name: I
keywordList:
- abstract
- interface
- sealed
// BAD:
interface class Example {
...
}
// GOOD:
interface class IExample {
...
}
Classes that are located along the path from the list must have the appropriate prefix. Example:
add_class_prefix_by_path_list:
-
path: lib/src
nameList:
- Model
// BAD:
class Example {
...
}
// GOOD:
class ModelExample {
...
}
There must be a comma at the end of the parameter list.
// BAD:
(a, b) {}
void exampleFunction({required String argument}) {
print("Hello World!");
}
// GOOD:
(
a,
b,
) {}
void exampleFunction({
required String argument,
}) {
print(
"Hello World!",
);
}
A class that has all final fields must have a const constructor.
// BAD:
class Example {
Example({
required this.title,
});
final String title;
}
// GOOD:
class Example {
const Example({
required this.title,
});
final String title;
}
Global variables, static fields, variables in functions, and objects that have the final keyword and can be constants must have the const
keyword.
// BAD:
final Example topLevel = Example(
title: "Title",
);
class Example {
static final String subTitle = "SubTitle";
const Example({
required this.title,
});
final String title;
}
void exampleFunction() {
final Example function = Example(
title: "Title",
);
}
// GOOD:
const Example topLevel = Example(
title: "Title",
);
class Example {
static const String title = "SubTitle";
const Example();
}
void exampleFunction() {
const Example function = Example(
title: "Title",
);
}
All classes must have an explicit constructor.
// BAD:
class Example {}
// GOOD:
class Example {
Example();
}
The ChangeNotifier
/ValueNotifier
class must have a Controller
postfix.
// BAD:
class ExampleNotifier extends ChangeNotifier {}
// GOOD:
class ExampleController extends ChangeNotifier {}
The Cubit
class must have the Cubit
postfix.
// BAD:
class ExampleCub extends Cubit<ExampleState> {
...
}
// GOOD:
class ExampleCubit extends Cubit<ExampleState> {
...
}
The extension must have the Extension
postfix.
// BAD:
extension ExampleX on Object {}
// GOOD:
extension ExampleExtension on Object {}
The if expression must have parentheses.
// BAD:
if (list.isEmpty) return;
// GOOD:
if (list.isEmpty) {
return;
}
A mixin must have a Mixin
postfix.
// BAD:
mixin StringMix on Object {}
// GOOD:
mixin StringMixin on Object {}
Fields and methods of a class overridden from the base one must have the @override
annotation.
// BAD:
class Example {
String toString() => "";
}
// GOOD:
class Example {
@override
String toString() => "";
}
Within a class, access to internal fields and methods must begin with the this
keyword.
// BAD:
class Example {
...
final String title;
@override
String toString() => title;
}
// GOOD:
class Example {
...
final String title;
@override
String toString() => this.title;
}
Variables and parameters of closures must have a type.
// BAD:
void exampleFunction(
field,
) {
final variable = "";
}
// GOOD:
void exampleFunction(
dynamic field,
) {
final String variable = "";
}
Need to remove the Bloc
/Cubit
dependency in the Bloc
/Cubit
class.
// BAD:
class ExampleBloc extends Bloc<IExampleEvent, IExampleState> {
ExampleBloc({
required AnotherBloc anotherbloc,
}) : this._anotherbloc = anotherbloc,
super(
const ExampleLoadingState(),
);
final AnotherBloc _anotherbloc;
}
// GOOD:
class ExampleBloc extends Bloc<IExampleEvent, IExampleState> {
ExampleBloc() : super(
const ExampleLoadingState(),
);
}
Need to remove the Flutter
resource dependency in the Bloc
/Cubit
class.
// BAD:
class ExampleBloc extends Bloc<IExampleEvent, IExampleState> {
ExampleBloc({
required TextEditingController textController,
}) : this._textController = textController,
super(
const ExampleLoadingState(),
);
final TextEditingController _textController;
}
// GOOD:
class ExampleBloc extends Bloc<IExampleEvent, IExampleState> {
ExampleBloc() : super(
const ExampleLoadingState(),
);
}
Need to remove public properties in the Bloc
/Cubit
class.
// BAD:
class ExampleBloc extends Bloc<IExampleEvent, IExampleState> {
ExampleBloc({
required this.publicProperty,
}) : super(
const ExampleLoadingState(),
);
final String publicProperty;
}
// GOOD:
class ExampleBloc extends Bloc<IExampleEvent, IExampleState> {
ExampleBloc() : super(
const ExampleLoadingState(),
);
}
Need to remove the class postfix included in the list.
// BAD:
class ExampleModel {
const ExampleModel();
}
// GOOD:
class Example {
const Example();
}
Need to remove the class prefix included in the list.
// BAD:
class ModelExample {
const ModelExample();
}
// GOOD:
class Example {
const Example();
}
Need to remove a function from the list.
// BAD:
void exampleFunction() {
print(something);
}
// GOOD:
void exampleFunction() {}
Need to remove the new
keyword when creating the instance.
// BAD:
final ExampleClass example = new ExampleClass();
// GOOD:
final ExampleClass example = ExampleClass();
Need to remove the package that is on the list.
# BAD:
dependencies:
bloc:
get:
get_it:
fpdart:
hive:
# GOOD:
dependencies:
bloc:
Need to remove a type from the list.
// BAD:
return Scaffold(
body: Container(
... ,
),
);
// GOOD:
return Scaffold(
body: Padding(
padding: ... ,
child: ColorBox(
color: ... ,
),
),
);
Need to remove the function that returns Widget
in StatelessWidget
, StatefulWidget
or State
.
// BAD:
Widget _buildRow() => Row(
... ,
);
// GOOD:
List<String> _entityList() => [
... ,
];
Need to edit the arrow function.
// BAD:
int exampleFunction() {
return 1 + 1;
}
// GOOD:
int exampleFunction() => 1 + 1;
Need to edit all parameters of the private constructor into named ones.
// BAD:
class _ExampleClass {
const _ExampleClass(
this.property0,
this.property1,
);
...
}
// GOOD:
class _ExampleClass {
const _ExampleClass({
required this.property0,
required this.property1,
});
...
}
Need to edit all parameters of the public constructor into named ones.
// BAD:
class ExampleClass {
const ExampleClass(
this.property0,
this.property1,
);
...
}
// GOOD:
class ExampleClass {
const ExampleClass({
required this.property0,
required this.property1,
});
...
}
Need to edit the file located along the path to the allowable code length. Example:
edit_file_length_by_path_list:
-
path: lib/src
length: 100
Need to edit all parameters of a private function into named ones.
// BAD:
void _exampleFunction(
String argument0,
String argument1,
) {
...
}
// GOOD:
void _exampleFunction({
required String argument0,
required String argument1,
}) {
...
}
Need to edit all parameters of a public function into named ones.
// BAD:
void exampleFunction(
String argument0,
String argument1,
) {
...
}
// GOOD:
void exampleFunction({
required String argument0,
required String argument1,
}) {
...
}
Need to edit the declaration of the list of variables into separate ones.
// BAD:
final String variable0, variable1, variable2 = "";
// GOOD:
final String variable0 = "";
final String variable1 = "";
final String variable2 = "";
Need to edit a private variable to public in a function.
// BAD:
void exampleFunction() {
final String _variable = "";
}
// GOOD:
void exampleFunction() {
final String variable = "";
}
Need to edit relative import to absolute.
// BAD:
import '../src/example.dart';
// GOOD:
import 'package:example/src/example.dart';
You need to edit the variable name based on its type. Exceptions - resources form Dart and Flutter SDK.
// BAD:
final ExampleClass a = ExampleClass();
// GOOD:
final ExampleClass example = ExampleClass();
The priority of displayed commands in the IDE.
For debugging analyzer, you'll have to update your analysis_options.yaml
as followed:
custom_lint:
debug: true
A file custom_lint.log
that records analyzer errors will automatically appear in the project.
Be sure to attach the log from this file if errors occur.
Feel free to open an issue if you find any bugs or errors or suggestions.