nilts

nilts is lint rules, quick fixes and assists for Dart and Flutter projects that helps you enforce best practices, and avoid errors.

build pub license


Contents

Usage

nilts depends on custom_lint.
You should add nilts and custom_lint to your dev_dependencies in pubspec.yaml file.

dev_dependencies:
  custom_lint: <version>
  nilts: <version>

And also, add custom_lint to your analysis_options.yaml file.

analyzer:
  plugins:
    - custom_lint

Configuration

You can configure all lint rules provided by nilts in analysis_options.yaml file.
Choice one of the following configuration strategies.

Disabling strategy

All of nilts rules are enabled by default.
Add lint rule name and set false to disable it.

custom_lint:
  rules:
    # Disable particular lint rules if you want ignore them whole package.
    - unnecessary_rebuilds_from_media_query: false

Enabling strategy

You can disable all lint rules depends on custom_lint by setting enable_all_lint_rules to false.
Add lint rule name and set true to enable it.

custom_lint:
  # Disable all lint rules depends on custom_lint.
  enable_all_lint_rules: false
  rules:
    - unnecessary_rebuilds_from_media_query: true

NOTE: If you enable_all_lint_rules set to false, all of lint rules (not only all of nilts's lint rules) depends on custom_lint will be disabled by default.

Lint rules and quick fixes

Read below to learn about each lint rules intend to.
Some of lint rules support quick fixes on IDE.

Quick fix demo

Overview

Rule name Overview Target SDK Rule type Maturity level Quick fix
defined_value_changed_type Checks void Function(T value) definitions. Any versions nilts supports Practice Experimental ✅️
defined_value_getter_type Checks T Function() definitions. Any versions nilts supports Practice Experimental ✅️
defined_value_setter_type Checks void Function(T value) definitions. Any versions nilts supports Practice Experimental ✅️
defined_void_callback_type Checks void Function() definitions. Any versions nilts supports Practice Experimental ✅️
fixed_text_scale_rich_text Checks usage of textScaler or textScaleFactor in RichText constructor. Any versions nilts supports Practice Experimental ✅️
flaky_tests_with_set_up_all Checks setUpAll usages. Any versions nilts supports Practice Experimental ✅️
no_support_multi_text_direction Checks if supports TextDirection changes. Any versions nilts supports Practice Experimental ✅️
no_support_web_platform_check Checks if Platform.isXxx usages. Any versions nilts supports Practice Experimental ✅️
shrink_wrapped_scroll_view Checks the content of the scroll view is shrink wrapped. Any versions nilts supports Practice Experimental ✅️
unnecessary_rebuilds_from_media_query Checks MediaQuery.xxxOf(context) or MediaQuery.maybeXxxOf(context) usages. >= Flutter 3.10.0 (Dart 3.0.0) Practice Experimental ✅️

Details

defined_value_changed_type

  • Target SDK : Any versions nilts supports
  • Rule type : Practice
  • Maturity level : Experimental
  • Quick fix : ✅

Consider replace void Function(T value) with ValueChanged which is defined in Flutter SDK.
If the value has been set, use ValueSetter instead.

BAD:

final void Function(int value) callback;

GOOD:

final ValueChanged<int> callback;

See also:

defined_value_getter_type

  • Target SDK : Any versions nilts supports
  • Rule type : Practice
  • Maturity level : Experimental
  • Quick fix : ✅

Consider replace T Function() with ValueGetter which is defined in Flutter SDK.

BAD:

final int Function() callback;

GOOD:

final ValueGetter<int> callback;

See also:

defined_value_setter_type

  • Target SDK : Any versions nilts supports
  • Rule type : Practice
  • Maturity level : Experimental
  • Quick fix : ✅

Consider replace void Function(T value) with ValueSetter which is defined in Flutter SDK.
If the value has changed, use ValueChanged instead.

BAD:

final void Function(int value) callback;

GOOD:

final ValueSetter<int> callback;

See also:

defined_void_callback_type

  • Target SDK : Any versions nilts supports
  • Rule type : Practice
  • Maturity level : Experimental
  • Quick fix : ✅

Consider replace void Function() with VoidCallback which is defined in Flutter SDK.

BAD:

final void Function() callback;

GOOD:

final VoidCallback callback;

See also:

fixed_text_scale_rich_text

  • Target SDK : Any versions nilts supports
  • Rule type : Practice
  • Maturity level : Experimental
  • Quick fix : ✅

Consider using Text.rich or adding textScaler or textScaleFactor (deprecated on Flutter 3.16.0 and above) argument to RichText constructor to make the text size responsive for user setting.

BAD:

RichText(
  text: TextSpan(
    text: 'Hello, world!',
  ),
)

GOOD:

Text.rich(
  TextSpan(
    text: 'Hello, world!',
  ),
)

GOOD:

RichText(
  text: TextSpan(
    text: 'Hello, world!',
  ),
  textScaler: MediaQuery.textScalerOf(context),
)

GOOD (deprecated on Flutter 3.16.0 and above):

RichText(
  text: TextSpan(
    text: 'Hello, world!',
  ),
  textScaleFactor: MediaQuery.textScaleFactorOf(context),
)

See also:

flaky_tests_with_set_up_all

  • Target SDK : Any versions nilts supports
  • Rule type : Practice
  • Maturity level : Experimental
  • Quick fix : ✅

Consider using setUp function or initialization on top level or body of test group. setUpAll may cause flaky tests with concurrency executions.

BAD:

setUpAll(() {
  // ...
});

GOOD:

setUp(() {
  // ...
});
void main() {
  // do initialization on top level
  // ...

 group('...', () {
  // or do initialization on body of test group
  // ...
 });
}

See also:

no_support_multi_text_direction

  • Target SDK : Any versions nilts supports
  • Rule type : Practice
  • Maturity level : Experimental
  • Quick fix : ✅

Consider using TextDirection aware configurations if your application supports different TextDirection languages.

BAD:

Align(
  alignment: Alignment.bottomLeft,
)

BAD:

Padding(
  padding: EdgeInsets.only(left: 16, right: 4),
)

BAD:

Positioned(left: 12, child: SizedBox())

GOOD:

Align(
  alignment: AlignmentDirectional.bottomStart,
)

GOOD:

Padding(
  padding: EdgeInsetsDirectional.only(start: 16, end: 4),
)

GOOD:

Positioned.directional(
  start: 12,
  textDirection: TextDirection.ltr,
  child: SizedBox(),
)

PositionedDirectional(
  start: 12,
  child: SizedBox(),
)

See also:

no_support_web_platform_check

  • Target SDK : Any versions nilts supports
  • Rule type : Practice
  • Maturity level : Experimental
  • Quick fix : ✅

Prefer using defaultTargetPlatform instead of Platform API if you want to know which platform your application is running on. This is because

  • Platform API throws a runtime exception on web application.
  • By combining kIsWeb and defaultTargetPlatform, you can accurately determine which platform your web application is running on.

BAD:

bool get isIOS => !kIsWeb && Platform.isIOS;

GOOD:

bool get isIOS => !kIsWeb && defaultTargetPlatform == TargetPlatform.iOS;

See also:

shrink_wrapped_scroll_view

  • Target SDK : Any versions nilts supports
  • Rule type : Practice
  • Maturity level : Experimental
  • Quick fix : ✅

Consider removing shrinkWrap argument and update the Widget not to shrink wrap.
Shrink wrapping the content of the scroll view is significantly more expensive than expanding to the maximum allowed size because the content can expand and contract during scrolling, which means the size of the scroll view needs to be recomputed whenever the scroll position changes.

You can avoid shrink wrap with 3 steps below in case of your scroll view is nested.

  1. Replace the parent scroll view with CustomScrollView.
  2. Replace the child scroll view with SliverListView or SliverGridView.
  3. Set SliverChildBuilderDelegate to delegate argument of the SliverListView or SliverGridView.

BAD:

ListView(shrinkWrap: true)

GOOD:

ListView(shrinkWrap: false)

See also:

unnecessary_rebuilds_from_media_query

  • Target SDK : >= Flutter 3.10.0 (Dart 3.0.0)
  • Rule type : Practice
  • Maturity level : Experimental
  • Quick fix : ✅

Prefer using MediaQuery.xxxOf or MediaQuery.maybeXxxOf instead of MediaQuery.of or MediaQuery.maybeOf to avoid unnecessary rebuilds.

BAD:

final size = MediaQuery.of(context).size;

GOOD:

final size = MediaQuery.sizeOf(context);

Note that using MediaQuery.of or MediaQuery.maybeOf makes sense following cases:

  • wrap Widget with MediaQuery overridden MediaQueryData
  • observe all changes of MediaQueryData

See also:

Assists

Upcoming... 🚀

Known issues

Quick fix priorities

The priorities assigned to quick fixes are not currently visible in IntelliJ IDEA and Android Studio due to the lack of support for PrioritizedSourceChange in these environments.
In contrast, VS Code does support this feature, allowing quick fixes to be listed along with their respective priorities.

VS Code IntelliJ IDEA / Android Studio
VS Code IntelliJ IDEA / Android Studio

See also:

fix-all assist

The fix-all assist feature has been introduced in custom_lint_builder 0.6.0.
However, this feature is not yet supported in IntelliJ IDEA and Android Studio, owing to their current lack of support for PrioritizedSourceChange.

VS Code IntelliJ IDEA / Android Studio
VS Code IntelliJ IDEA / Android Studio

Feature requests

If you have any feature requests, please create an issue from this template.

Bug reports

If you find any bugs, please create an issue from this template.

Contributing

Welcome your contributions!!
Please read CONTRIBUTING docs before submitting your PR.

Libraries

nilts