Skip to content

Commit b8d126d

Browse files
Viktor-Mudrytskyiyurii-prykhodko-solidillia-romanenko
authored
Avoid returning widgets lint improvement (#139)
* commit * raw version * Improvement for avoid_returning_widget lint * doc improvement * changelog and version * Update CHANGELOG.md Co-authored-by: Illia Romanenko <[email protected]> --------- Co-authored-by: Yurii Prykhodko <[email protected]> Co-authored-by: Illia Romanenko <[email protected]>
1 parent 46026f7 commit b8d126d

File tree

4 files changed

+69
-10
lines changed

4 files changed

+69
-10
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
- Added `avoid_debug_print` rule
44
- Fixed an issue with no_magic_number lint
5+
- Improvement for `avoid_returning_widget` lint:
6+
- ignores methods that override ones that return widget (build() for example)
7+
- no longer allows returning widgets from methods/functions named build
58

69
## 0.1.4
710

lib/src/lints/avoid_returning_widgets/avoid_returning_widgets_rule.dart

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:analyzer/dart/ast/ast.dart';
2+
import 'package:analyzer/dart/element/type.dart';
23
import 'package:analyzer/error/listener.dart';
34
import 'package:custom_lint_builder/custom_lint_builder.dart';
45
import 'package:solid_lints/src/models/rule_config.dart';
@@ -10,6 +11,9 @@ import 'package:solid_lints/src/utils/types_utils.dart';
1011
/// Using functions instead of Widget subclasses for decomposing Widget trees
1112
/// may cause unexpected behavior and performance issues.
1213
///
14+
/// Exceptions:
15+
/// - overriden methods
16+
///
1317
/// More details: https://github.com/flutter/flutter/issues/19269
1418
///
1519
/// ### Example
@@ -20,7 +24,8 @@ import 'package:solid_lints/src/utils/types_utils.dart';
2024
/// Widget avoidReturningWidgets() => const SizedBox(); // LINT
2125
///
2226
/// class MyWidget extends StatelessWidget {
23-
/// Widget _test1() => const SizedBox(); // LINT
27+
/// Widget get box => SizedBox(); // LINT
28+
/// Widget test1() => const SizedBox(); //LINT
2429
/// Widget get _test3 => const SizedBox(); // LINT
2530
/// }
2631
/// ```
@@ -29,7 +34,14 @@ import 'package:solid_lints/src/utils/types_utils.dart';
2934
/// #### GOOD:
3035
///
3136
/// ```dart
32-
/// class MyWidget extends StatelessWidget {
37+
/// class MyWidget extends MyWidget {
38+
///
39+
/// @override
40+
/// Widget test1() => const SizedBox();
41+
///
42+
/// @override
43+
/// Widget get box => ColoredBox(color: Colors.pink);
44+
///
3345
/// @override
3446
/// Widget build(BuildContext context) {
3547
/// return const SizedBox();
@@ -51,7 +63,8 @@ class AvoidReturningWidgetsRule extends SolidLintRule {
5163
name: lintName,
5264
problemMessage: (_) =>
5365
'Returning a widget from a function is considered an anti-pattern. '
54-
'Extract your widget to a separate class.',
66+
'Unless you are overriding an existing method, '
67+
'consider extracting your widget to a separate class.',
5568
);
5669

5770
return AvoidReturningWidgetsRule._(rule);
@@ -64,15 +77,24 @@ class AvoidReturningWidgetsRule extends SolidLintRule {
6477
CustomLintContext context,
6578
) {
6679
context.registry.addDeclaration((node) {
67-
final isWidgetReturned = switch (node) {
80+
// Check if declaration is function or method,
81+
// simultaneously checks if return type is [DartType]
82+
final DartType? returnType = switch (node) {
6883
FunctionDeclaration(returnType: TypeAnnotation(:final type?)) ||
6984
MethodDeclaration(returnType: TypeAnnotation(:final type?)) =>
70-
hasWidgetType(type),
71-
_ => false,
85+
type,
86+
_ => null,
7287
};
7388

74-
// `build` methods return widgets by nature
75-
if (isWidgetReturned && node.declaredElement?.name != "build") {
89+
if (returnType == null) {
90+
return;
91+
}
92+
93+
final isWidgetReturned = hasWidgetType(returnType);
94+
95+
final isOverriden = node.declaredElement?.hasOverride ?? false;
96+
97+
if (isWidgetReturned && !isOverriden) {
7698
reporter.reportErrorForNode(code, node);
7799
}
78100
});

lint_test/avoid_returning_widget_test.dart

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,25 @@ import 'package:flutter/material.dart';
99
// expect_lint: avoid_returning_widgets
1010
Widget avoidReturningWidgets() => const SizedBox();
1111

12-
class MyWidget extends StatelessWidget {
12+
class BaseWidget extends StatelessWidget {
13+
const BaseWidget({super.key});
14+
15+
// Not allowed even though overriding it is alllowed
16+
// expect_lint: avoid_returning_widgets
17+
Widget get box => SizedBox();
18+
19+
// expect_lint: avoid_returning_widgets
20+
Widget decoratedBox() {
21+
return DecoratedBox(decoration: BoxDecoration());
22+
}
23+
24+
@override
25+
Widget build(BuildContext context) {
26+
return const Placeholder();
27+
}
28+
}
29+
30+
class MyWidget extends BaseWidget {
1331
const MyWidget({super.key});
1432

1533
// expect_lint: avoid_returning_widgets
@@ -25,8 +43,24 @@ class MyWidget extends StatelessWidget {
2543
// expect_lint: avoid_returning_widgets
2644
Widget get _test3 => const SizedBox();
2745

46+
// Allowed
47+
@override
48+
Widget decoratedBox() {
49+
return super.decoratedBox();
50+
}
51+
52+
// Allowed
53+
@override
54+
Widget get box => ColoredBox(color: Colors.pink);
55+
56+
// Allowed
2857
@override
2958
Widget build(BuildContext context) {
3059
return const SizedBox();
3160
}
3261
}
62+
63+
// expect_lint: avoid_returning_widgets
64+
Widget build() {
65+
return Offstage();
66+
}

lint_test/no_magic_number_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// ignore_for_file: unused_local_variable
1+
// ignore_for_file: unused_local_variable, avoid_returning_widgets
22
// ignore_for_file: prefer_match_file_name
33
// ignore_for_file: avoid_unused_parameters
44
// ignore_for_file: no_empty_block

0 commit comments

Comments
 (0)