Custom lints wanted for personal developments.
Add sangria_lints to your pubspec.yaml
:
dev_dependencies:
sangria_lints:
sangria_lints comes bundled with its own rules using custom_lints.
-
Add both sangria_lints and custom_lint to your
pubspec.yaml
:dev_dependencies: sangria_lints: custom_lint: # <- add this
-
Enable
custom_lint
's plugin in youranalysis_options.yaml
:analyzer: plugins: - sangria_lints
By default when installing sangria_lints, all the lints will be enabled. To change this, you have a few options.
analyzer:
plugins:
- custom_lint
custom_lint:
rules:
# Explicitly disable one custom-lint rule.
- use_setstate_synchronously: false
A use_setstate_synchronously
rule that discourages the use of setState across asynchronous gaps within subclasses of State.
In async functions, the state of a widget may have been disposed across asynchronous gaps in a case when the user moves to a different screen. This leads to setState() called after dispose()
error.
Since widgets can be unmounted before a Future gets resolved, seeing if widgets are mounted is necessary before calling setState.
class _MyWidgetState extends State<MyWidget> {
String message;
@override
Widget build(BuildContext context) {
return Button(
onPressed: () async {
String fromSharedPreference = await getFromSharedPreference();
// LINT: Avoid calling 'setState' across asynchronous gaps without seeing if the widget is mounted.
setState(() {
message = fromSharedPreference;
});
},
child: Text(message),
);
}
}
class _MyWidgetState extends State<MyWidget> {
String message;
@override
Widget build(BuildContext context) {
return Button(
onPressed: () async {
String fromSharedPreference = await getFromSharedPreference();
if (mounted) {
setState(() {
message = fromSharedPreference;
});
}
},
child: Text(message),
);
}
}
A avoid_empty_container
rule that discourages the use of empty container.
Since Container
generates many properties like padding, margin, decoration, and constraints, it is perfered to use SizedBox
instead.
class CustomText extends StatelessWidget {
bool canDisplay;
@override
Widget build(BuildContext context) {
return canDisplay ? Text('canDisplay') : Container();
}
}
class CustomText extends StatelessWidget {
bool canDisplay;
@override
Widget build(BuildContext context) {
return canDisplay ? Text('canDisplay') : SizedBox.shrink();
}
}
A no_disabled_tests
rule that raises a warning about disabled tests inspired by no-disabled-tests rule in plugins for Jest and Vitest.
Test and flutter_test package have a feature that allows you to temporarily mark tests as disabled. This feature is often helpful, however before committing changes we may want to check that all tests are running.
void main() {
group('test group', () {
test('test', () {}, skip: true);
testWidgets('test widgets', (tester) async {}, skip: true);
}, skip: true)
}
void main() {
group('test group', () {
test('test', () {});
testWidgets('test widgets', (tester) async {});
})
}
A use_widget_ref_synchronously
rule that discourages the use of WidgetRef across asynchronous gaps within Consumer widgets provided by Riverpod.
In async functions, a widget may have been disposed across asynchronous gaps in a case when the user got back to previous screen. This leads to Cannot use ref after the widget was disposed called
error.
Since widgets can be unmounted before a Future gets resolved, seeing if widgets are mounted is necessary before using WidgetRef.
Riverpod recommends seeing if widgets are mounted to fix the error as well.
ElevatedButton(
onPressed: () async {
await future;
ref.read(...); // May throw "Cannot use "ref" after the widget was disposed"
}
)
ElevatedButton(
onPressed: () async {
await future;
if (!context.mounted) return;
ref.read(...); // No longer throws
}
)