pyramid_lint 0.6.0 pyramid_lint: ^0.6.0 copied to clipboard
pyramid_lint introduces a set of additional lints and fixes to enforce a consistent coding style, prevent common mistakes and enhance code readability.
Pyramid Lint #
pyramid_lint introduces a set of additional lints and fixes to enforce a consistent coding style, prevent common mistakes and enhance code readability using custom_lint.
Table of contents #
- Table of contents
- Installation
- Configuration
- Dart lints
- avoid_duplicate_import
- avoid_dynamic
- avoid_empty_block
- avoid_inverted_boolean_expression
- max_lines_for_file
- max_lines_for_function
- prefer_declaring_const_constructor
- prefer_declaring_parameter_name
- prefer_immediate_return
- prefer_iterable_first
- prefer_iterable_last
- prefer_underscore_for_unused_callback_parameters
- Flutter lints
- avoid_single_child_in_flex
- correct_order_for_super_dispose
- correct_order_for_super_init_state
- prefer_async_callback
- prefer_border_from_border_side
- prefer_border_radius_all
- prefer_dedicated_media_query_method
- prefer_spacer
- prefer_text_rich
- proper_controller_dispose
- proper_edge_insets_constructor
- proper_usage_of_expanded_and_flexible
- proper_usage_of_from_environment
- Dart assists
- Flutter assists
Installation #
Run the following command to add custom_lint
and pyramid_lint
to your project's dev dependencies:
dart pub add --dev custom_lint pyramid_lint
Enable custom_lint
in your analysis_options.yaml
file:
analyzer:
plugins:
- custom_lint
Configuration #
By default, all lints are enabled.
To disable a specific lint, add the following to your analysis_options.yaml
file:
custom_lint:
rules:
- specific_lint_rule: false # disable specific_lint_rule
To disable all lints and only enable the one you want, edit your analysis_options.yaml
file as below:
custom_lint:
enable_all_lint_rules: false # disable all lints
rules:
- specific_lint_rule # enable specific_lint_rule
Some lints have additional configuration options. To configure a lint, follow the example below:
custom_lint:
rules:
- configurable_lint_rule:
option1: value1
option2: value2
Dart lints #
avoid_duplicate_import #
Duplicate imports are unnecessary.
Bad
import 'dart:math' as math show max;
import 'dart:math';
final a = math.max(1, 10);
final b = min(1, 10);
Good
import 'dart:math' as math show max, min;
final a = math.max(1, 10);
final b = math.min(1, 10);
avoid_dynamic #
Using dynamic
sacrifices the benefits of static typing and decreases code readability. Only use dynamic
when necessary.
Using dynamic
with Map
will not trigger this lint.
Bad
dynamic thing = 'text';
void log(dynamic something) => print(something);
List<dynamic> list = [1, 2, 3];
final setLiteral = <dynamic>{'a', 'b', 'c'};
Good
String thing = 'text';
void log(String something) => print(something);
List<int> list = [1, 2, 3];
final setLiteral = <String>{'a', 'b', 'c'};
avoid_empty_block #
Empty block usually indicates a missing implementation.
Bad
void doSomething() {
}
Good
void doSomething() {
actuallyDoSomething();
}
void doSomething() {}
// TODO: implement doSomething
}
avoid_inverted_boolean_expression #
Inverted boolean expression decreases code readability.
Bad
if (!(number == 0)) {}
if (!(number > 0)) {}
final anotherNumber = !(number == 0) ? 1 : 2;
Good
if (number != 0) {}
if (number <= 0) {}
final anotherNumber = number != 0 ? 2 : 1;
Fix
max_lines_for_file #
A file should not exceed a certain number of lines.
Options:
max_lines
(default: 200)
max_lines_for_function #
A function should not exceed a certain number of lines.
Options:
max_lines
(default: 100)
prefer_declaring_const_constructor #
Constructors of classes with only final fields should be declared as const constructors when possible.
Bad
class Point {
final double x;
final double y;
Point(this.x, this.y);
Point.origin()
: x = 0,
y = 0;
}
Good
class Point {
final double x;
final double y;
const Point(this.x, this.y);
const Point.origin()
: x = 0,
y = 0;
}
Fix
prefer_declaring_parameter_name #
Not declaring parameter name decreases code readability and the IDEs code completion will not be able to suggest the parameter name.
Bad
typedef ItemBuilder = Widget Function(BuildContext, int);
Good
typedef ItemBuilder = Widget Function(BuildContext context, int index);
prefer_immediate_return #
Declaring a variable to return it on the next line is unnecessary.
Bad
int add(int a, int b) {
final result = a + b;
return result;
}
Good
int add(int a, int b) {
return a + b;
}
Fix
prefer_iterable_first #
Using iterable.first increases code readability.
Bad
const numbers = [1, 2, 3];
int firstNumber;
firstNumber = numbers[0];
firstNumber = numbers.elementAt(0);
Good
const numbers = [1, 2, 3];
int firstNumber;
firstNumber = numbers.first;
Fix
prefer_iterable_last #
Using iterable.last increases code readability.
Bad
const numbers = [1, 2, 3];
int lastNumber;
lastNumber = numbers[numbers.length - 1];
lastNumber = numbers.elementAt(numbers.length - 1);
Fix
Good
const numbers = [1, 2, 3];
int lastNumber;
lastNumber = numbers.last;
prefer_underscore_for_unused_callback_parameters #
Using _
for unused callback parameters increases code readability.
Bad
itemBuilder: (context, index) {
return Text('Item $index');
}
Good
itemBuilder: (_, index) {
return Text('Item $index');
}
Flutter lints #
avoid_single_child_in_flex #
Using Column or Row with only one child is inefficient.
Bad
Row(
children: [
Container(),
],
)
Good
Align(
child: Container(),
)
// or
Center(
child: Container(),
)
Fix
correct_order_for_super_dispose #
super.dispose() should be called at the end of the dispose method.
Bad
@override
void dispose() {
super.dispose();
_dispose();
}
Good
@override
void dispose() {
_dispose();
super.dispose();
}
Fix
correct_order_for_super_init_state #
super.initState() should be called at the start of the initState method.
Bad
@override
void initState() {
_init();
super.initState();
}
Good
@override
void initState() {
super.initState();
_init();
}
Fix
prefer_async_callback #
There is a typedef AsyncCallback defined in flutter.
Bad
final Future<void> Function() cb;
Good
final AsyncCallback cb;
prefer_border_from_border_side #
Border.all is not a const constructor and it uses const constructor Border.fromBorderSide internally.
Bad
DecoratedBox(
decoration: BoxDecoration(
border: Border.all(
width: 1,
style: BorderStyle.solid,
),
),
)
Good
const DecoratedBox(
decoration: BoxDecoration(
border: Border.fromBorderSide(
BorderSide(
width: 1,
style: BorderStyle.solid,
),
),
),
)
Fix
prefer_border_radius_all #
BorderRadius.all is not a const constructor and it uses const constructor BorderRadius.circular internally.
Bad
DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
),
)
Good
const DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(8)),
),
)
Fix
prefer_dedicated_media_query_method #
Using MediaQuery.of(context) to access below properties will cause unnecessary rebuilds.
- accessibleNavigation
- alwaysUse24HourFormat
- boldText
- devicePixelRatio
- disableAnimations
- displayFeatures
- gestureSettings
- highContrast
- invertColors
- navigationMode
- orientation
- padding
- platformBrightness
- size
- systemGestureInsets
- textScaleFactor
- viewInsets
- viewPadding
Bad
final size = MediaQuery.of(context).size;
final orientation = MediaQuery.maybeOf(context)?.orientation;
Good
final size = MediaQuery.of(context).size;
final orientation = MediaQuery.maybeOrientationOf(context);
Fix
prefer_spacer #
Using Expanded with an empty Container or SizedBox is inefficient.
Bad
Column(
children: [
Expanded(
flex: 2,
child: SizedBox(),
),
],
)
Good
Column(
children: [
Spacer(flex: 2),
],
)
Fix
prefer_text_rich #
RichText does not inherit TextStyle from DefaultTextStyle.
Bad
RichText(
text: const TextSpan(
text: 'Pyramid',
children: [
TextSpan(text: 'Lint'),
],
),
)
Good
const Text.rich(
TextSpan(
text: 'Pyramid',
children: [
TextSpan(text: 'Lint'),
],
),
)
Fix
prefer_value_changed #
There is a typedef ValueChanged defined in flutter.
Bad
final void Function(int) cb;
Good
final ValueChanged<int> cb;
prefer_void_callback #
There is a typedef VoidCallback defined in flutter.
Bad
final void Function() cb;
Good
final VoidCallback cb;
proper_controller_dispose #
Controllers should be disposed in dispose method.
- AnimationController
- PageController
- ScrollController
- SearchController
- TabController
- TextEditingController
- UndoHistoryController
Bad
class _MyWidgetState extends State<MyWidget> {
final _textEditingController = TextEditingController();
@override
Widget build(BuildContext context) {
return Container();
}
}
Good
class _MyWidgetState extends State<MyWidget> {
final _textEditingController = TextEditingController();
@override
Widget build(BuildContext context) {
return Container();
}
@override
void dispose() {
_textEditingController.dispose();
super.dispose();
}
}
proper_edge_insets_constructor #
EdgeInsets.all(0) should be replaced with EdgeInsets.zero.
Bad
padding = const EdgeInsets.fromLTRB(0, 0, 0, 0);
padding = const EdgeInsets.fromLTRB(8, 8, 8, 8);
padding = const EdgeInsets.fromLTRB(8, 0, 8, 0);
padding = const EdgeInsets.fromLTRB(8, 4, 8, 4);
padding = const EdgeInsets.fromLTRB(8, 4, 8, 0);
padding = const EdgeInsets.only(left: 0, top: 0, right: 0, bottom: 0);
padding = const EdgeInsets.only(left: 8, top: 8, right: 8, bottom: 8);
padding = const EdgeInsets.only(left: 8, top: 0, right: 8, bottom: 0);
padding = const EdgeInsets.only(left: 8, top: 4, right: 8, bottom: 4);
padding = const EdgeInsets.only(left: 2, top: 4, right: 6, bottom: 8);
padding = const EdgeInsets.symmetric(horizontal: 0, vertical: 0);
padding = const EdgeInsets.symmetric(horizontal: 8, vertical: 8);
padding = const EdgeInsets.symmetric(horizontal: 8, vertical: 0);
Good
padding = EdgeInsets.zero;
padding = const EdgeInsets.all(8);
padding = const EdgeInsets.symmetric(horizontal: 8);
padding = const EdgeInsets.symmetric(horizontal: 8, vertical: 4);
padding = const EdgeInsets.only(left: 8, top: 4, right: 8);
padding = EdgeInsets.zero;
padding = const EdgeInsets.all(8);
padding = const EdgeInsets.symmetric(horizontal: 8);
padding = const EdgeInsets.symmetric(horizontal: 8, vertical: 4);
padding = const EdgeInsets.fromLTRB(2, 4, 6, 8);
padding = EdgeInsets.zero;
padding = const EdgeInsets.all(8);
padding = const EdgeInsets.symmetric(horizontal: 8);
Fix
proper_usage_of_expanded_and_flexible #
Expanded and Flexible should be placed inside a Row, Column, or Flex.
Bad
Center(
child: Expanded(
child: Container(),
),
)
Good
Row(
children: [
Expanded(
child: Container(),
),
],
)
proper_usage_of_from_environment #
bool.fromEnvironment
, int.fromEnvironment
and String.fromEnvironment
constructors should be invoked as const
.
Bad
final boolean = bool.fromEnvironment('bool');
int integer = int.fromEnvironment('int');
var string = String.fromEnvironment('String');
Good
const boolean = bool.fromEnvironment('bool');
const integer = int.fromEnvironment('int');
const string = String.fromEnvironment('String');
Dart assists #
invert_boolean_expression #
Invert a boolean expression.
swap_then_else_expression #
Swap the then and else expression of a if-else expression or conditional expression.
Flutter assists #
use_edge_insets_zero #
Replace
- EdgeInsets.all(0)
- EdgeInsets.fromLTRB(0, 0, 0, 0)
- EdgeInsets.only(left: 0, top: 0, right: 0, bottom: 0)
- EdgeInsets.symmetric(horizontal: 0, vertical: 0)
with EdgeInsets.zero.
wrap_with_expanded #
Wrap the selected widget with an Expanded.
wrap_with_layout_builder #
Wrap the selected widget with a LayoutBuilder.
wrap_with_stack #
Wrap the selected widget with a Stack.