@sun-asterisk/sunlint 1.3.41 → 1.3.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skill-assets/sunlint-code-quality/rules/dart/D001-recommended-lints.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D002-dispose-resources.md +44 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D003-prefer-widget-classes.md +53 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D004-avoid-shrinkwrap.md +31 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D005-widget-nesting.md +62 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D006-large-callbacks.md +54 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D007-lifecycle-order.md +44 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D008-long-functions.md +37 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D009-function-parameters.md +38 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D010-cyclomatic-complexity.md +46 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D011-named-parameters.md +31 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D012-named-booleans.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D013-single-public-class.md +34 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D014-safe-collection-access.md +30 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D015-copywith-consistency.md +52 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D016-project-tests.md +32 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D017-review-dependencies.md +24 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D018-no-commented-code.md +34 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D019-single-child-wrappers.md +31 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D020-if-else-limit.md +41 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D021-negated-booleans.md +26 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D022-setstate-usage.md +35 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D023-unnecessary-overrides.md +32 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D024-avoid-unnecessary-statefulwidget.md +45 -0
- package/skill-assets/sunlint-code-quality/rules/dart/D025-nested-ternaries.md +35 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB001-use-snake-case.md +30 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB002-use-camel-case.md +38 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB003-use-screaming-snake-case.md +26 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB004-predicate-methods.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB005-dangerous-methods.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB006-indentation.md +32 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB007-line-length.md +25 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB008-rescue-exception.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB009-save-bang.md +41 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB010-avoid-n-plus-one.md +32 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB011-use-find-each.md +30 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB012-sql-injection.md +29 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB013-prefer-has-many-through.md +35 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB014-dependent-associations.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB015-modern-validations.md +29 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB016-thin-controllers.md +45 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB017-avoid-fat-models.md +41 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB018-service-objects.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB019-avoid-metaprogramming.md +40 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB020-use-pluck.md +29 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB021-use-size.md +27 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB022-order-by-timestamps.md +24 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB023-where-missing.md +24 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB024-method-length.md +41 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB025-parameter-limits.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB026-avoid-deep-nesting.md +38 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB027-guard-clauses.md +37 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB028-class-length.md +32 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB029-meaningful-names.md +30 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB030-dry-principle.md +37 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB031-mvc-architecture.md +37 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB032-use-concerns.md +31 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB033-moderate-callbacks.md +31 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB034-use-decorators.md +33 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB035-comprehensive-tests.md +32 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB036-frozen-string-literal.md +29 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB037-it-parameter.md +25 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB038-modern-enum-syntax.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB039-solid-adapters.md +29 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB040-rails-authentication.md +26 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB041-async-query-loading.md +29 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB042-hotwire-turbo.md +30 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB043-use-propshaft.md +27 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB044-structured-logging.md +35 -0
- package/skill-assets/sunlint-code-quality/rules/ruby/RB045-prism-parser.md +29 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW001-block-based-kvo.md +40 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW002-class-delegate-protocol.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW003-compiler-protocol-init.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW004-contains-over-filter-count.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW005-convenience-type.md +34 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW006-discarded-notification-center-observer.md +41 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW007-discouraged-direct-init.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW008-discouraged-optional-boolean.md +32 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW009-empty-count.md +30 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW010-empty-string.md +30 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW011-explicit-init.md +26 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW012-fatal-error-message.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW013-for-where.md +30 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW014-force-cast.md +26 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW015-force-try.md +30 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW016-force-unwrapping.md +32 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW017-function-parameter-count.md +37 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW018-large-tuple.md +41 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW019-legacy-constructor.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW020-nesting.md +38 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW021-no-extension-access-modifier.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW022-overridden-super-call.md +30 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW023-override-in-extension.md +32 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW024-private-over-fileprivate.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW025-private-unit-test.md +32 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW026-prohibited-super-call.md +29 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW027-sorted-first-last.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW028-syntactic-sugar.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW029-unused-closure-parameter.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW030-unused-enumerated.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW031-unused-optional-binding.md +26 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW032-valid-ibinspectable.md +26 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW033-vertical-parameter-alignment.md +36 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW034-void-return.md +28 -0
- package/skill-assets/sunlint-code-quality/rules/swift/SW035-weak-delegate.md +28 -0
package/package.json
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Recommended Lint Rules Should Be Enabled
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Ensure code quality through standard lint configurations
|
|
5
|
+
tags: lint, quality, static-analysis
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Recommended Lint Rules Should Be Enabled
|
|
9
|
+
|
|
10
|
+
The `analysis_options.yaml` file should include recommended lint packages (flutter_lints, very_good_analysis, or lints) and critical lint rules should not be disabled. This ensures consistent code quality standards across the project.
|
|
11
|
+
|
|
12
|
+
**Incorrect (minimal components):**
|
|
13
|
+
|
|
14
|
+
```yaml
|
|
15
|
+
analyzer:
|
|
16
|
+
exclude:
|
|
17
|
+
- build/**
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Correct (recommended setup):**
|
|
21
|
+
|
|
22
|
+
```yaml
|
|
23
|
+
include: package:flutter_lints/flutter.yaml
|
|
24
|
+
|
|
25
|
+
analyzer:
|
|
26
|
+
exclude:
|
|
27
|
+
- "**/*.g.dart"
|
|
28
|
+
- "**/*.freezed.dart"
|
|
29
|
+
|
|
30
|
+
linter:
|
|
31
|
+
rules:
|
|
32
|
+
- avoid_print
|
|
33
|
+
- prefer_const_constructors
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Tools:** Custom analyzer (D001)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Always Dispose Resources and Remove Listeners
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Prevent memory leaks by ensuring proper resource disposal
|
|
5
|
+
tags: memory-leak, resources, disposal, lifecycle, performance
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Always Dispose Resources and Remove Listeners
|
|
9
|
+
|
|
10
|
+
All disposable resources (Controllers, StreamSubscriptions, FocusNodes, Listeners) must be properly disposed in the `dispose()` method. This includes TextEditingController, AnimationController, ScrollController, StreamSubscription, FocusNode, and other resources that implement Disposable. Failing to dispose these resources leads to memory leaks.
|
|
11
|
+
|
|
12
|
+
**Incorrect (not disposed):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
class _MyWidgetState extends State<MyWidget> {
|
|
16
|
+
final _controller = TextEditingController();
|
|
17
|
+
|
|
18
|
+
@override
|
|
19
|
+
Widget build(BuildContext context) {
|
|
20
|
+
return TextField(controller: _controller);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Correct (properly disposed):**
|
|
26
|
+
|
|
27
|
+
```dart
|
|
28
|
+
class _MyWidgetState extends State<MyWidget> {
|
|
29
|
+
final _controller = TextEditingController();
|
|
30
|
+
|
|
31
|
+
@override
|
|
32
|
+
void dispose() {
|
|
33
|
+
_controller.dispose();
|
|
34
|
+
super.dispose();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@override
|
|
38
|
+
Widget build(BuildContext context) {
|
|
39
|
+
return TextField(controller: _controller);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Tools:** Custom analyzer (D002)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Prefer Widgets Over Methods Returning Widgets
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: Improve performance and maintainability by extracting widget-returning methods into widget classes
|
|
5
|
+
tags: performance, maintainability, widgets, extraction
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Prefer Widgets Over Methods Returning Widgets
|
|
9
|
+
|
|
10
|
+
Methods that return widgets should be extracted into separate StatelessWidget or StatefulWidget classes. This improves performance as Flutter can optimize widget rebuilds, makes code more reusable, and follows Flutter best practices. Only the build() method and lifecycle methods are exempt from this rule.
|
|
11
|
+
|
|
12
|
+
**Incorrect (method returning widget):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
Widget _buildHeader() {
|
|
16
|
+
return Text("Title", style: TextStyle(fontSize: 24));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
Widget build(BuildContext context) {
|
|
21
|
+
return Column(
|
|
22
|
+
children: [
|
|
23
|
+
_buildHeader(),
|
|
24
|
+
Text("Content"),
|
|
25
|
+
],
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Correct (extracted widget class):**
|
|
31
|
+
|
|
32
|
+
```dart
|
|
33
|
+
class HeaderWidget extends StatelessWidget {
|
|
34
|
+
const HeaderWidget({super.key});
|
|
35
|
+
|
|
36
|
+
@override
|
|
37
|
+
Widget build(BuildContext context) {
|
|
38
|
+
return Text("Title", style: TextStyle(fontSize: 24));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@override
|
|
43
|
+
Widget build(BuildContext context) {
|
|
44
|
+
return Column(
|
|
45
|
+
children: [
|
|
46
|
+
const HeaderWidget(),
|
|
47
|
+
Text("Content"),
|
|
48
|
+
],
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Tools:** Custom analyzer (D003)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Avoid shrinkWrap in ListView
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: Prevent performance issues caused by shrinkWrap in scrollable widgets
|
|
5
|
+
tags: performance, scroll, listview, lazy-loading
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Avoid shrinkWrap in ListView
|
|
9
|
+
|
|
10
|
+
Using `shrinkWrap: true` in ListView or GridView disables lazy loading and forces all items to render at once, causing severe performance degradation. Instead, use Expanded or Flexible widgets to constrain the ListView size, or use SliverList within a CustomScrollView for better performance. The shrinkWrap parameter should only be used in rare cases where the list is guaranteed to be small.
|
|
11
|
+
|
|
12
|
+
**Incorrect (using shrinkWrap):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
ListView(
|
|
16
|
+
shrinkWrap: true,
|
|
17
|
+
children: [ ... ],
|
|
18
|
+
)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (using Expanded):**
|
|
22
|
+
|
|
23
|
+
```dart
|
|
24
|
+
Expanded(
|
|
25
|
+
child: ListView(
|
|
26
|
+
children: [ ... ],
|
|
27
|
+
),
|
|
28
|
+
)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Tools:** Custom analyzer (D004)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Limit Widget Nesting Depth to 6
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: Maintain code readability and prevent performance issues caused by deeply nested widgets
|
|
5
|
+
tags: readability, maintainability, performance, nesting
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Limit Widget Nesting Depth to 6
|
|
9
|
+
|
|
10
|
+
Widget nesting should not exceed 6 levels in the build method. Deeply nested widgets make code harder to understand, maintain, and can impact performance. When nesting exceeds this limit, extract nested widgets into separate StatelessWidget or StatefulWidget classes.
|
|
11
|
+
|
|
12
|
+
**Incorrect (deeply nested):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
@override
|
|
16
|
+
Widget build(BuildContext context) {
|
|
17
|
+
return Scaffold(
|
|
18
|
+
body: Center(
|
|
19
|
+
child: Container(
|
|
20
|
+
padding: const EdgeInsets.all(16),
|
|
21
|
+
child: Column(
|
|
22
|
+
children: [
|
|
23
|
+
Padding(
|
|
24
|
+
padding: const EdgeInsets.only(bottom: 8),
|
|
25
|
+
child: Row(
|
|
26
|
+
children: [
|
|
27
|
+
Expanded(
|
|
28
|
+
child: Card(
|
|
29
|
+
child: Padding(
|
|
30
|
+
padding: const EdgeInsets.all(12),
|
|
31
|
+
child: Text("Too deep!"), // Level 11
|
|
32
|
+
),
|
|
33
|
+
),
|
|
34
|
+
),
|
|
35
|
+
],
|
|
36
|
+
),
|
|
37
|
+
),
|
|
38
|
+
],
|
|
39
|
+
),
|
|
40
|
+
),
|
|
41
|
+
),
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Correct (extracted widget):**
|
|
47
|
+
|
|
48
|
+
```dart
|
|
49
|
+
@override
|
|
50
|
+
Widget build(BuildContext context) {
|
|
51
|
+
return Scaffold(
|
|
52
|
+
body: Center(
|
|
53
|
+
child: Container(
|
|
54
|
+
padding: const EdgeInsets.all(16),
|
|
55
|
+
child: const MyCustomHeader(), // Extracted at level 4
|
|
56
|
+
),
|
|
57
|
+
),
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Tools:** Custom analyzer (D005)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Prefer Extracting Large Callbacks from Build
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: Improve code readability and testability by extracting large callback functions
|
|
5
|
+
tags: readability, maintainability, testability, callback
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Prefer Extracting Large Callbacks from Build
|
|
9
|
+
|
|
10
|
+
Callback functions (onTap, onPressed, onChanged, etc.) in widget builders should not exceed 5 lines. Large inline callbacks make the build method harder to read and maintain. Extract callbacks that exceed this limit to separate methods or functions.
|
|
11
|
+
|
|
12
|
+
**Incorrect (large inline callback):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
@override
|
|
16
|
+
Widget build(BuildContext context) {
|
|
17
|
+
return ElevatedButton(
|
|
18
|
+
onPressed: () {
|
|
19
|
+
final data = _formKey.currentState?.value;
|
|
20
|
+
if (data != null && data.isNotEmpty) {
|
|
21
|
+
_logger.info('Submitting data: $data');
|
|
22
|
+
_repository.save(data).then((_) {
|
|
23
|
+
Navigator.pop(context);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
child: Text('Submit'),
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Correct (extracted callback method):**
|
|
33
|
+
|
|
34
|
+
```dart
|
|
35
|
+
void _handleSubmit() {
|
|
36
|
+
final data = _formKey.currentState?.value;
|
|
37
|
+
if (data != null && data.isNotEmpty) {
|
|
38
|
+
_logger.info('Submitting data: $data');
|
|
39
|
+
_repository.save(data).then((_) {
|
|
40
|
+
Navigator.pop(context);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@override
|
|
46
|
+
Widget build(BuildContext context) {
|
|
47
|
+
return ElevatedButton(
|
|
48
|
+
onPressed: _handleSubmit,
|
|
49
|
+
child: Text('Submit'),
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Tools:** Custom analyzer (D006)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Prefer Init First, Dispose Last
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Ensure proper lifecycle method ordering in StatefulWidget
|
|
5
|
+
tags: lifecycle, maintenance, flutter, statefulwidget
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Prefer Init First, Dispose Last
|
|
9
|
+
|
|
10
|
+
In StatefulWidget lifecycle methods, `super.initState()` should be called first before any initialization code, and `super.dispose()` should be called last after all cleanup code. This ensures that the framework's internal state management is properly initialized before your code runs and is the last to clean up.
|
|
11
|
+
|
|
12
|
+
**Incorrect (wrong order):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
@override
|
|
16
|
+
void initState() {
|
|
17
|
+
_controller = TextEditingController();
|
|
18
|
+
super.initState(); // Called after custom logic
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@override
|
|
22
|
+
void dispose() {
|
|
23
|
+
super.dispose(); // Called before cleanup
|
|
24
|
+
_controller.dispose();
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Correct (framework-friendly order):**
|
|
29
|
+
|
|
30
|
+
```dart
|
|
31
|
+
@override
|
|
32
|
+
void initState() {
|
|
33
|
+
super.initState(); // Always first
|
|
34
|
+
_controller = TextEditingController();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@override
|
|
38
|
+
void dispose() {
|
|
39
|
+
_controller.dispose();
|
|
40
|
+
super.dispose(); // Always last
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Tools:** Custom analyzer (D007)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Avoid Long Functions
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Improve code readability and maintainability by limiting function length
|
|
5
|
+
tags: readability, maintainability, clean-code, dev-efficiency
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Avoid Long Functions
|
|
9
|
+
|
|
10
|
+
Functions should not exceed 60 lines of effective code (excluding comments and opening/closing braces). Long functions are harder to understand, test, and maintain. They often indicate that the function is doing too much and should be broken down into smaller, more focused functions.
|
|
11
|
+
|
|
12
|
+
**Incorrect (too long):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
void processOrder(Order order) {
|
|
16
|
+
// Line 1
|
|
17
|
+
// ... 60+ more lines of logic ...
|
|
18
|
+
// Line 70
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Correct (modular functions):**
|
|
23
|
+
|
|
24
|
+
```dart
|
|
25
|
+
void processOrder(Order order) {
|
|
26
|
+
validateOrder(order);
|
|
27
|
+
calculateTotal(order);
|
|
28
|
+
saveToDatabase(order);
|
|
29
|
+
sendNotification(order);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
void validateOrder(Order order) { ... }
|
|
33
|
+
void calculateTotal(Order order) { ... }
|
|
34
|
+
// Each function is focused and short
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Tools:** Custom analyzer (D008)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Limit Function Parameters
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Improve code readability by limiting the number of function parameters
|
|
5
|
+
tags: readability, maintainability, clean-code
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Limit Function Parameters
|
|
9
|
+
|
|
10
|
+
Functions, methods, and constructors should not have more than 5 parameters. Too many parameters make code harder to read, understand, and maintain. When a function needs many parameters, consider grouping related parameters into a data class or using a configuration object.
|
|
11
|
+
|
|
12
|
+
**Incorrect (too many positional parameters):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
void createUser(String firstName, String lastName, int age, String email, String phone, String address, String city) {
|
|
16
|
+
// ...
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Correct (using data object or named parameters):**
|
|
21
|
+
|
|
22
|
+
```dart
|
|
23
|
+
// Option 1: Data class
|
|
24
|
+
void createUser(UserDetail details) {
|
|
25
|
+
// ...
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Option 2: Grouped named parameters
|
|
29
|
+
void createUser({
|
|
30
|
+
required String name,
|
|
31
|
+
required ContactInfo contact,
|
|
32
|
+
required Address address,
|
|
33
|
+
}) {
|
|
34
|
+
// ...
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Tools:** Custom analyzer (D009)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Limit Cyclomatic Complexity
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Improve code readability and maintainability by limiting cyclomatic complexity
|
|
5
|
+
tags: complexity, maintainability, clean-code
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Limit Cyclomatic Complexity
|
|
9
|
+
|
|
10
|
+
Functions, methods, and constructors should not have cyclomatic complexity exceeding 10. High cyclomatic complexity indicates that the code has too many independent paths, making it harder to understand, test, and maintain. Use early returns or extract complex logic into sub-functions.
|
|
11
|
+
|
|
12
|
+
**Incorrect (high complexity):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
String getStatusDescription(int status, bool isUrgent, bool isAdmin) {
|
|
16
|
+
if (status == 1) {
|
|
17
|
+
if (isUrgent) return "Urgent Pending";
|
|
18
|
+
return "Pending";
|
|
19
|
+
} else if (status == 2) {
|
|
20
|
+
if (isAdmin) return "Admin Approved";
|
|
21
|
+
return "Approved";
|
|
22
|
+
} else if (status == 3) {
|
|
23
|
+
return "Rejected";
|
|
24
|
+
} else {
|
|
25
|
+
return "Unknown";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Correct (reduced complexity with Map or early returns):**
|
|
31
|
+
|
|
32
|
+
```dart
|
|
33
|
+
static const statusMap = {
|
|
34
|
+
1: "Pending",
|
|
35
|
+
2: "Approved",
|
|
36
|
+
3: "Rejected",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
String getStatusDescription(int status, bool isUrgent, bool isAdmin) {
|
|
40
|
+
if (status == 1 && isUrgent) return "Urgent Pending";
|
|
41
|
+
if (status == 2 && isAdmin) return "Admin Approved";
|
|
42
|
+
return statusMap[status] ?? "Unknown";
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Tools:** Custom analyzer (D010)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Prefer Named Parameters
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: Improve code readability and prevent parameter confusion
|
|
5
|
+
tags: readability, safety, clean-code
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Prefer Named Parameters
|
|
9
|
+
|
|
10
|
+
Functions, methods, and constructors with more than 3 parameters that have 2 or more adjacent parameters of the same type should use named parameters. This improves code clarity by making it explicit which value corresponds to which parameter, reducing the risk of accidentally swapping arguments of the same type.
|
|
11
|
+
|
|
12
|
+
**Incorrect (confusing positional parameters):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
// At call site, hard to tell which is width and which is height
|
|
16
|
+
final rect = Rectangle(100.0, 50.0, 10.0, "red");
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Correct (clear named parameters):**
|
|
20
|
+
|
|
21
|
+
```dart
|
|
22
|
+
// Much clearer at call site
|
|
23
|
+
final rect = Rectangle(
|
|
24
|
+
width: 100.0,
|
|
25
|
+
height: 50.0,
|
|
26
|
+
padding: 10.0,
|
|
27
|
+
color: "red",
|
|
28
|
+
);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Tools:** Custom analyzer (D011)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Prefer Named Boolean Parameters
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: Improve code readability by avoiding unclear boolean parameters
|
|
5
|
+
tags: readability, clean-code, boolean
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Prefer Named Boolean Parameters
|
|
9
|
+
|
|
10
|
+
Boolean parameters in functions make code harder to understand at call sites (the "Boolean Trap"). When a function has boolean parameters, use named parameters to make the intent explicit. Better yet, create separate functions (e.g., `enableUser()` instead of `setUser(true)`).
|
|
11
|
+
|
|
12
|
+
**Incorrect (unclear boolean meaning):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
updateStatus(user, true); // What does true mean? Is it active? deleted? admin?
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Correct (explicit naming):**
|
|
19
|
+
|
|
20
|
+
```dart
|
|
21
|
+
// Option 1: Named parameter
|
|
22
|
+
updateStatus(user, isActive: true);
|
|
23
|
+
|
|
24
|
+
// Option 2: Semantic function
|
|
25
|
+
activateUser(user);
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Tools:** Custom analyzer (D012)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Prefer a Single Public Class Per File
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: Improve code organization and maintainability
|
|
5
|
+
tags: organization, maintainability, clean-code
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Prefer a Single Public Class Per File
|
|
9
|
+
|
|
10
|
+
Each Dart file should contain only one public class. Multiple public classes in a single file make code harder to navigate, test, and maintain. Private implementation classes (starting with `_`) are allowed in the same file.
|
|
11
|
+
|
|
12
|
+
**Incorrect (multiple public classes):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
// user_models.dart
|
|
16
|
+
class User { ... }
|
|
17
|
+
class UserProfile { ... }
|
|
18
|
+
class UserSettings { ... }
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (split into separate files):**
|
|
22
|
+
|
|
23
|
+
```dart
|
|
24
|
+
// user.dart
|
|
25
|
+
class User { ... }
|
|
26
|
+
|
|
27
|
+
// user_profile.dart
|
|
28
|
+
class UserProfile { ... }
|
|
29
|
+
|
|
30
|
+
// user_settings.dart
|
|
31
|
+
class UserSettings { ... }
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Tools:** Custom analyzer (D013)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Avoid Unsafe Collection Access
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Prevent runtime errors from accessing empty collections
|
|
5
|
+
tags: safety, stability, collections, crash-prevention
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Avoid Unsafe Collection Access
|
|
9
|
+
|
|
10
|
+
Using `.first`, `.last`, or `.single` on collections without checking if they're empty causes runtime `StateError` exceptions. Always check `isEmpty`, `isNotEmpty`, or use safe alternatives from `collection` package or built-in `firstOrNull`.
|
|
11
|
+
|
|
12
|
+
**Incorrect (potential crash):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
final firstUser = users.first; // Throws StateError if users is empty
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Correct (safe access):**
|
|
19
|
+
|
|
20
|
+
```dart
|
|
21
|
+
// Option 1: check length
|
|
22
|
+
if (users.isNotEmpty) {
|
|
23
|
+
final firstUser = users.first;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Option 2: use null-safe getter (Dart 3.0+)
|
|
27
|
+
final firstUser = users.firstOrNull;
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Tools:** Custom analyzer (D014)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Ensure copyWith includes all constructor parameters
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Maintain data integrity and completeness in immutable objects
|
|
5
|
+
tags: safety, immutability, data-integrity, boilerplate
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Ensure copyWith includes all constructor parameters
|
|
9
|
+
|
|
10
|
+
When a class implements a `copyWith` method for creating modified copies, it should include all constructor parameters. Missing parameters in `copyWith` can lead to unintended data loss or inability to update certain fields. This is especially important for data classes, models, and immutable state objects.
|
|
11
|
+
|
|
12
|
+
**Incorrect (incomplete copyWith):**
|
|
13
|
+
|
|
14
|
+
```dart
|
|
15
|
+
class User {
|
|
16
|
+
final String id;
|
|
17
|
+
final String name;
|
|
18
|
+
final int age;
|
|
19
|
+
|
|
20
|
+
User({required this.id, required this.name, required this.age});
|
|
21
|
+
|
|
22
|
+
User copyWith({String? name}) {
|
|
23
|
+
return User(
|
|
24
|
+
id: this.id,
|
|
25
|
+
name: name ?? this.name,
|
|
26
|
+
age: this.age, // age cannot be updated via copyWith
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Correct (complete copyWith):**
|
|
33
|
+
|
|
34
|
+
```dart
|
|
35
|
+
class User {
|
|
36
|
+
final String id;
|
|
37
|
+
final String name;
|
|
38
|
+
final int age;
|
|
39
|
+
|
|
40
|
+
User({required this.id, required this.name, required this.age});
|
|
41
|
+
|
|
42
|
+
User copyWith({String? name, int? age}) {
|
|
43
|
+
return User(
|
|
44
|
+
id: this.id,
|
|
45
|
+
name: name ?? this.name,
|
|
46
|
+
age: age ?? this.age,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Tools:** Custom analyzer (D015)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Project should have tests
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Ensure code quality and prevent regressions through automated testing
|
|
5
|
+
tags: testing, quality, reliability
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Project should have tests
|
|
9
|
+
|
|
10
|
+
Every Dart/Flutter project should have a `test` directory containing test files (files ending with `_test.dart`). Tests are essential for maintaining code quality, catching bugs early, and enabling safe refactoring.
|
|
11
|
+
|
|
12
|
+
**Incorrect (no tests):**
|
|
13
|
+
|
|
14
|
+
```text
|
|
15
|
+
my_project/
|
|
16
|
+
lib/
|
|
17
|
+
pubspec.yaml
|
|
18
|
+
# MISSING test/ directory
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (testing infrastructure included):**
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
my_project/
|
|
25
|
+
lib/
|
|
26
|
+
test/
|
|
27
|
+
unit_test.dart
|
|
28
|
+
widget_test.dart
|
|
29
|
+
pubspec.yaml
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Tools:** Custom analyzer (D016)
|