flutter_custom_lints 1.0.9
flutter_custom_lints: ^1.0.9 copied to clipboard
A collection of custom lint rules for Flutter and Dart projects that enforce best practices and prevent common coding mistakes.
Flutter Custom Lints #
A collection of custom lint rules for Flutter and Dart projects that enforce best practices and prevent common coding mistakes. This package provides robust, exception-safe analysis that works reliably across different analyzer versions and code complexity levels.
Installation #
Add flutter_custom_lints to your dev_dependencies in pubspec.yaml:
dev_dependencies:
custom_lint:
flutter_custom_lints:
Then run:
dart pub get
Setup #
Add the following to your analysis_options.yaml:
analyzer:
plugins:
- custom_lint
custom_lint:
rules:
# Enable specific rules
- dispose_controllers
- no_as_type_assertion
- no_direct_iterable_access
- no_null_force
- use_compare_without_case
Comprehensive Analysis Options #
This package provides a comprehensive analysis options configuration with enterprise-grade linting rules that you can use in your Flutter projects:
include: package:flutter_custom_lints/analysis_options_comprehensive.yaml
This comprehensive configuration includes:
- 180+ linter rules covering all aspects of Flutter and Dart development
- Flutter Lints as the base configuration
- Dart Code Metrics with complexity analysis and code quality metrics
- Strict type safety and null safety enforcement
- Performance optimizations and best practices
- Advanced code style enforcement
- Custom lint rules from this package automatically enabled
Usage #
In your project's analysis_options.yaml, simply include:
include: package:flutter_custom_lints/analysis_options_comprehensive.yaml
# You can still override specific rules if needed:
linter:
rules:
# Disable a specific rule if it's too strict for your project
avoid_print: false
# Add additional rules
- custom_rule_name
# Add project-specific analyzer configurations
analyzer:
exclude:
- lib/generated/**
- lib/specific_folder/**
Note: This preset already includes the custom_lint plugin configuration and enables all custom lint rules from this package automatically.
Available Lints #
dispose_controllers #
Ensures that controllers (AnimationController, TextEditingController, etc.) are properly disposed to prevent memory leaks. This rule includes robust error handling to work reliably with complex generic types and abstract interfaces.
❌ Bad:
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
final TextEditingController _textController = TextEditingController();
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: Duration(seconds: 1),
vsync: this,
);
}
@override
Widget build(BuildContext context) {
return TextField(controller: _textController);
}
// Missing dispose() method - Memory leak!
}
✅ Good:
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
final TextEditingController _textController = TextEditingController();
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: Duration(seconds: 1),
vsync: this,
);
}
@override
Widget build(BuildContext context) {
return TextField(controller: _textController);
}
@override
void dispose() {
_animationController.dispose();
_textController.dispose();
super.dispose();
}
}
// Alternative example using close() method
class StreamExampleWidget extends StatefulWidget {
@override
_StreamExampleWidgetState createState() => _StreamExampleWidgetState();
}
class _StreamExampleWidgetState extends State<StreamExampleWidget> {
final StreamController<String> _streamController = StreamController<String>();
final HttpClient _httpClient = HttpClient();
@override
Widget build(BuildContext context) {
return Container();
}
@override
void dispose() {
_streamController.close(); // Using close() instead of dispose()
_httpClient.close(); // I/O resources typically use close()
super.dispose();
}
}
Supported controller types:
AnimationController(usesdispose())TextEditingController(usesdispose())ScrollController(usesdispose())PageController(usesdispose())TabController(usesdispose())VideoPlayerController(usesdispose())FocusNode(usesdispose())StreamController(usesdispose()orclose())StreamSubscription(usescancel())Timer(usescancel())IOSink(usesclose())HttpClient(usesclose())WebSocket(usesclose())RandomAccessFile(usesclose())Socket(usesclose())
Supported disposal methods:
dispose()- Standard disposal methodclose()- For I/O resources and streamscancel()- For subscriptions and timers
no_as_type_assertion #
Prevents using as for type assertions which can cause runtime errors. This rule includes safe type resolution that handles complex type hierarchies and prevents analyzer exceptions during code analysis.
❌ Bad:
var user = data as User; // Can throw at runtime
✅ Good:
if (data is User) {
var user = data; // Safe type promotion
}
no_direct_iterable_access #
Prevents direct access to iterables without null checks.
❌ Bad:
var first = list.first; // Can throw if list is empty
✅ Good:
var first = list.isNotEmpty ? list.first : null;
// or create your own safe extension:
extension SafeIterable<T> on Iterable<T> {
T? get safeFirst => isEmpty ? null : first;
T? safeAt(int index) => index < 0 || index >= length ? null : elementAt(index);
}
no_null_force #
Prevents force unwrapping of nullable values.
❌ Bad:
String value = nullableString!; // Can throw at runtime
✅ Good:
String? value = nullableString;
if (value != null) {
// Use value safely
}
use_compare_without_case #
Suggests using case-insensitive string comparison methods.
❌ Bad:
if (str1.toLowerCase() == str2.toLowerCase()) {
// Less efficient
}
✅ Good:
// Create your own extension method:
extension StringComparisonExtension on String {
bool compareWithoutCase(String other) =>
toLowerCase() == other.toLowerCase();
}
// Then use it:
if (str1.compareWithoutCase(str2)) {
// More efficient and readable
}
Running Lints #
After setup, you can run the lints using:
dart run custom_lint
For Flutter projects:
flutter packages pub run custom_lint
Troubleshooting #
If you encounter any issues:
- Exception during analysis: The latest version (1.0.6+) includes robust error handling to prevent crashes during AST analysis
- Complex generic types: The lints now handle complex type hierarchies and abstract interfaces safely
- Analyzer compatibility: Enhanced compatibility with different analyzer versions
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
License #
This project is licensed under the MIT License - see the LICENSE file for details.