Skip to content

Commit 9e9ceb5

Browse files
authored
feat: Improve prefer_sliver_prefix to prefer_to_include_sliver_in_name (#58)
* feat: Improve prefer_sliver_prefix to prefer_to_include_sliver_in_name * chore: use early return
1 parent c2e3ccb commit 9e9ceb5

6 files changed

+173
-97
lines changed

packages/altive_lints/README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ There are also Altive-made rules by custom_lint.
1919
- [avoid\_single\_child](#avoid_single_child)
2020
- [prefer\_clock\_now](#prefer_clock_now)
2121
- [prefer\_dedicated\_media\_query\_methods](#prefer_dedicated_media_query_methods)
22-
- [prefer\_sliver\_prefix](#prefer_sliver_prefix)
22+
- [prefer\_to\_include\_sliver\_in\_name](#prefer_to_include_sliver_in_name)
2323
- [prefer\_space\_between\_elements](#prefer_space_between_elements)
2424
- [Lint rules adopted by altive\_lints and why](#lint-rules-adopted-by-altive_lints-and-why)
2525
- [public\_member\_api\_docs](#public_member_api_docs)
@@ -252,9 +252,9 @@ var size = MediaQuery.sizeOf(context);
252252
var padding = MediaQuery.viewInsetsOf(context);
253253
```
254254

255-
### prefer_sliver_prefix
255+
### prefer_to_include_sliver_in_name
256256

257-
Prefer to prefix the class name of a Widget that returns a Sliver type Widget with “Sliver”.
257+
Prefer to include ‘Sliver’ in the class name or named constructor of a widget that returns a Sliver-type widget.
258258

259259
This makes it easy for the user to know at a glance that it is a Sliver type Widget, and improves readability and consistency.
260260

packages/altive_lints/lib/altive_lints.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import 'src/lints/avoid_shrink_wrap_in_list_view.dart';
77
import 'src/lints/avoid_single_child.dart';
88
import 'src/lints/prefer_clock_now.dart';
99
import 'src/lints/prefer_dedicated_media_query_methods.dart';
10-
import 'src/lints/prefer_sliver_prefix.dart';
1110
import 'src/lints/prefer_space_between_elements.dart';
11+
import 'src/lints/prefer_to_include_sliver_in_name.dart';
1212

1313
/// Returns the Altive Plugin instance.
1414
PluginBase createPlugin() => _AltivePlugin();
@@ -24,6 +24,6 @@ class _AltivePlugin extends PluginBase {
2424
const PreferClockNow(),
2525
const PreferDedicatedMediaQueryMethods(),
2626
const PreferSpaceBetweenElements(),
27-
const PreferSliverPrefix(),
27+
const PreferToIncludeSliverInName(),
2828
];
2929
}

packages/altive_lints/lib/src/lints/prefer_sliver_prefix.dart

-76
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import 'package:analyzer/dart/ast/ast.dart';
2+
import 'package:analyzer/error/listener.dart';
3+
import 'package:collection/collection.dart';
4+
import 'package:custom_lint_builder/custom_lint_builder.dart';
5+
6+
/// A `prefer_to_include_sliver_in_name` rule that ensures widgets returning
7+
/// a Sliver-type widget include "Sliver" in their class names.
8+
///
9+
/// This naming convention improves code readability and consistency
10+
/// by clearly indicating the widget's functionality
11+
/// and return type through its name.
12+
///
13+
/// The rule also applies if "Sliver" is present in the named constructor,
14+
/// allowing flexibility in how the convention is followed.
15+
///
16+
/// ### Example
17+
///
18+
/// #### BAD:
19+
///
20+
/// ```dart
21+
/// class MyCustomList extends StatelessWidget {
22+
/// @override
23+
/// Widget build(BuildContext context) {
24+
/// return SliverList(...); // LINT
25+
/// }
26+
/// }
27+
/// ```
28+
///
29+
/// #### GOOD:
30+
///
31+
/// ```dart
32+
/// class SliverMyCustomList extends StatelessWidget {
33+
/// @override
34+
/// Widget build(BuildContext context) {
35+
/// return SliverList(...);
36+
/// }
37+
/// }
38+
/// ```
39+
class PreferToIncludeSliverInName extends DartLintRule {
40+
/// Creates a new instance of [PreferToIncludeSliverInName].
41+
const PreferToIncludeSliverInName() : super(code: _code);
42+
43+
static const _code = LintCode(
44+
name: 'prefer_to_include_sliver_in_name',
45+
problemMessage: 'Widgets returning Sliver should include "Sliver" '
46+
'in the class name or named constructor.',
47+
correctionMessage:
48+
'Consider adding "Sliver" to the class name or a named constructor.',
49+
);
50+
51+
@override
52+
void run(
53+
CustomLintResolver resolver,
54+
ErrorReporter reporter,
55+
CustomLintContext context,
56+
) {
57+
context.registry.addClassDeclaration((node) {
58+
final methodBody = node.members
59+
.whereType<MethodDeclaration>()
60+
.firstWhereOrNull((method) => method.name.lexeme == 'build')
61+
?.body;
62+
63+
if (methodBody is! BlockFunctionBody) {
64+
return;
65+
}
66+
67+
final returnStatements =
68+
methodBody.block.statements.whereType<ReturnStatement>();
69+
final returnsSliverWidget = returnStatements.any(
70+
(returnStatement) {
71+
final returnType = returnStatement.expression?.staticType;
72+
final typeName = returnType?.getDisplayString();
73+
return typeName?.startsWith('Sliver') ?? false;
74+
},
75+
);
76+
77+
if (!returnsSliverWidget) {
78+
return;
79+
}
80+
81+
final className = node.name.lexeme;
82+
83+
if (className.contains('Sliver')) {
84+
return;
85+
}
86+
87+
final constructorNames = node.members
88+
.whereType<ConstructorDeclaration>()
89+
.map((constructor) => constructor.name?.lexeme)
90+
.nonNulls;
91+
92+
final hasSliverInConstructor = constructorNames.any(
93+
(constructorName) => constructorName.toLowerCase().contains('sliver'),
94+
);
95+
96+
if (hasSliverInConstructor) {
97+
return;
98+
}
99+
100+
reporter.atNode(node, _code);
101+
});
102+
}
103+
}

packages/altive_lints/lint_test/lints/prefer_sliver_prefix.dart

-16
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import 'package:flutter/material.dart';
2+
3+
// expect_lint: prefer_to_include_sliver_in_name
4+
class MyWidget extends StatelessWidget {
5+
const MyWidget({super.key});
6+
7+
@override
8+
Widget build(BuildContext context) {
9+
return SliverList.builder(
10+
itemCount: 10,
11+
itemBuilder: (context, index) {
12+
return const Placeholder();
13+
},
14+
);
15+
}
16+
}
17+
18+
// expect_lint: prefer_to_include_sliver_in_name
19+
class MyWidget2 extends StatelessWidget {
20+
const MyWidget2({super.key, this.maxCount = 10});
21+
22+
const MyWidget2.small({super.key, this.maxCount = 5});
23+
24+
final int maxCount;
25+
26+
@override
27+
Widget build(BuildContext context) {
28+
return SliverList.builder(
29+
itemCount: maxCount,
30+
itemBuilder: (context, index) {
31+
return const Placeholder();
32+
},
33+
);
34+
}
35+
}
36+
37+
class MySliverWidget extends StatelessWidget {
38+
const MySliverWidget({super.key});
39+
40+
@override
41+
Widget build(BuildContext context) {
42+
return SliverList.builder(
43+
itemCount: 10,
44+
itemBuilder: (context, index) {
45+
return const Placeholder();
46+
},
47+
);
48+
}
49+
}
50+
51+
class MyWidget3 extends StatelessWidget {
52+
const MyWidget3({super.key});
53+
54+
const MyWidget3.sliver({super.key});
55+
56+
@override
57+
Widget build(BuildContext context) {
58+
return SliverList.builder(
59+
itemCount: 10,
60+
itemBuilder: (context, index) {
61+
return const Placeholder();
62+
},
63+
);
64+
}
65+
}

0 commit comments

Comments
 (0)