Skip to content
This repository was archived by the owner on Jul 16, 2023. It is now read-only.

Commit 4e18da1

Browse files
author
Roman Petrov
authored
feat: ignore Flutter builder functions in "prefer-extracting-callbacks" rule (#469)
* feat: ignore Flutter builder functions in "prefer-extracting-callbacks" rule * fix: address code review comment
1 parent 2da29bc commit 4e18da1

File tree

5 files changed

+35
-6
lines changed

5 files changed

+35
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* feat: Add static code diagnostic `prefer-const-border-radius`.
66
* Improve static code diagnostic `prefer-extracting-callbacks`, don't trigger on empty function blocks.
77
* Improve unused files check, add support for `vm:entry-point` annotation.
8+
* feat: ignore Flutter builder functions in `prefer-extracting-callbacks` rule.
89

910
## 4.3.3
1011

doc/rules/prefer-extracting-callbacks.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ Warns about inline callbacks in a widget tree and suggests to extract them to wi
1313
**NOTE** the rule will not trigger on:
1414
- arrow functions like `onPressed: () => _handler(...)` in order to cover cases when a callback needs a variable from the outside;
1515
- empty blocks.
16+
- Flutter specific: arguments with functions returning `Widget` type (or its subclass) and with first parameter of type `BuildContext`. "Builder" functions is a common pattern in Flutter, for example, [IndexedWidgetBuilder typedef](https://api.flutter.dev/flutter/widgets/IndexedWidgetBuilder.html) is used in [ListView.builder](https://api.flutter.dev/flutter/widgets/ListView/ListView.builder.html).
1617

17-
Use `ignored-named-arguments` configuration, if you want to ignore specific named parameters (`builder` argument is ignored by default).
18+
Use `ignored-named-arguments` configuration, if you want to ignore specific named parameters.
1819

1920
### Config example
2021

lib/src/analyzers/lint_analyzer/rules/flutter_rule_utils.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ bool isPaddingWidget(DartType? type) =>
2222
bool _isWidget(DartType? type) =>
2323
type?.getDisplayString(withNullability: false) == 'Widget';
2424

25+
bool isBuildContext(DartType? type) =>
26+
type?.getDisplayString(withNullability: false) == 'BuildContext';
27+
2528
bool _isSubclassOfWidget(DartType? type) =>
2629
type is InterfaceType && type.allSupertypes.any(_isWidget);
2730

lib/src/analyzers/lint_analyzer/rules/rules_list/prefer_extracting_callbacks/visitor.dart

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ class _InstanceCreationVisitor extends RecursiveAstVisitor<void> {
4040

4141
if (_isNotIgnored(argument) &&
4242
expression is FunctionExpression &&
43-
_hasNotEmptyBlockBody(expression)) {
43+
_hasNotEmptyBlockBody(expression) &&
44+
!_isFlutterBuilder(expression)) {
4445
_expressions.add(argument);
4546
}
4647
}
@@ -55,8 +56,19 @@ class _InstanceCreationVisitor extends RecursiveAstVisitor<void> {
5556
return body.block.statements.isNotEmpty;
5657
}
5758

59+
bool _isFlutterBuilder(FunctionExpression expression) {
60+
if (!isWidgetOrSubclass(expression.declaredElement?.returnType)) {
61+
return false;
62+
}
63+
64+
final formalParameters = expression.parameters?.parameters;
65+
66+
return formalParameters == null ||
67+
formalParameters.isNotEmpty &&
68+
isBuildContext(formalParameters.first.declaredElement?.type);
69+
}
70+
5871
bool _isNotIgnored(Expression argument) =>
5972
argument is! NamedExpression ||
60-
(argument.name.label.name != 'builder' &&
61-
!_ignoredArguments.contains(argument.name.label.name));
73+
!_ignoredArguments.contains(argument.name.label.name);
6274
}

test/analyzers/lint_analyzer/rules/rules_list/prefer_extracting_callbacks/examples/example.dart

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,13 @@ class MyAnotherWidget extends StatelessWidget {
5656
return null;
5757
},
5858
child: Container(),
59-
builder: () {
59+
builder: (context) {
60+
return null;
61+
},
62+
anotherBuilder: (context, index) {
63+
return null;
64+
},
65+
myOtherWidgetBuilder: (context) {
6066
return null;
6167
},
6268
);
@@ -86,6 +92,8 @@ class Widget {}
8692

8793
class StatelessWidget extends Widget {}
8894

95+
class BuildContext {}
96+
8997
class Container extends Widget {
9098
final Widget? child;
9199

@@ -95,12 +103,16 @@ class Container extends Widget {
95103
class TextButton {
96104
final Widget child;
97105
final void Function()? onPressed;
98-
final void Function()? builder;
106+
final Widget Function(BuildContext)? builder;
107+
final Widget Function(BuildContext, int)? anotherBuilder;
108+
final MyOtherWidget Function(BuildContext, int)? myOtherWidgetBuilder;
99109

100110
const TextButton({
101111
required this.child,
102112
required this.onPressed,
103113
this.builder,
114+
this.anotherBuilder,
115+
this.myOtherWidgetBuilder,
104116
});
105117
}
106118

0 commit comments

Comments
 (0)