A strict static analyzer and script for formatting Dart code.
Designed to solve problems in a project at the design and support stages.
Analyzer:
- strict architectural rules;
- strict stylistic rules;
- not strict rules of approach.
Script:
- automatic fix of detected linter errors;
- sorting in alphabetical order of the fields of .arb files;
- sorting in alphabetical order and converting declarations of imports, exports and parts;
- sorting in alphabetical order of dependencies, dev_dependencies, dependency_overrides keys in pubspec.yaml;
- Dart code formatting.
- Add two packages to the pubspec.yaml file in the dev_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_function_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
Arguments:
--no-fix - disable fix of analyzed linter problems;
--no-sort-arb - disable alphabetical sorting of .arb files;
--no-sort-convert-export-import-part - disable alphabetical sorting of declarations of imports, exports and parts of .dart files;
--no-sort-pubspec-dependencies - disable alphabetical sorting dependencшуы in the pubspec.yaml file;
--no-format - disable final formatting at the script completion stage;
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<IExampleEvent, IExampleState> {
...
}
// GOOD:
class ExampleBloc extends Bloc<IExampleEvent, IExampleState> {
...
}
sealed class IExampleEvent {
...
}
sealed class IExampleState {
...
}
// GOOD:
part of 'example_event.dart';
part of 'example_state.dart';
class ExampleBloc extends Bloc<IExampleEvent, IExampleState> {
...
}
The Bloc/Cubit state class must have a State postfix.
// BAD:
sealed class IExampleSt {
...
}
// GOOD:
sealed class IExampleState {
...
}
The Bloc/Cubit state class must be declared with the 'sealed' keyword.
// BAD:
class ExampleState {
...
}
// GOOD:
sealed class IExampleState {
...
}
The Bloc event class must have the Event postfix.
// BAD:
sealed class IExampleEv {
...
}
// GOOD:
sealed class IExampleEvent {
...
}
The Bloc event class must be declared with the 'sealed' keyword.
// BAD:
class ExampleEvent {
...
}
// GOOD:
sealed class IExampleEvent {
...
}
The Bloc class must have a Bloc postfix.
// BAD:
class ExampleBlc extends Bloc<IExampleEvent, IExampleState> {
...
}
// GOOD:
class ExampleBloc extends Bloc<IExampleEvent, IExampleState> {
...
}
Classes that contain keywords from the list must have the appropriate postfix.
Example:
add_class_postfix_by_keyword_list:
-
keywordList:
- base
name: Base
// BAD:
base class Example {
...
}
// GOOD:
base class ExampleBase {
...
}
Classes that are located along the path from the list must have the appropriate postfix.
// BAD:
base class Example {
...
}
// GOOD:
base class ExampleBase {
...
}
Classes that contain keywords from the list must be prefixed accordingly.
Example:
add_class_prefix_by_keyword_list:
-
keywordList:
- abstract
- interface
- sealed
name: I
// BAD:
interface class Example {
...
}
// GOOD:
interface class IExample {
...
}
Classes that are located along the path from the list must have the appropriate prefix.
// BAD:
interface class Example {
...
}
// GOOD:
interface class IExample {
...
}
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.
// 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.
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.
// BAD:
final ExampleClass a = ExampleClass();
// GOOD:
final ExampleClass example = ExampleClass();
The priority of displayed commands in the IDE.