riverpod_lint 1.1.8 icon indicating copy to clipboard operation
riverpod_lint: ^1.1.8 copied to clipboard

Riverpod_lint is a developper tool for users of Riverpod, designed to help stop common issue and simplify repetetive tasks.

Build Status codecov Star on Github License: MIT Discord

Deploys by Netlify

Riverpod


Riverpod_lint is a developer tool for users of Riverpod, designed to help stop common issue and simplify repetitive tasks.

Riverpod_lint adds various warnings with quick fixes and refactoring options, such as:

  • Warn if runApp does not include a ProviderScope at its root
  • Warn if provider parameters do not respect the rules of family
  • Refactor a widget to a ConsumerWidget/ConsumerStatfulWidget
  • ...

Convert to ConsumerStatefulWidget sample

Table of content #

Installing riverpod_lint #

Riverpod_lint is implemented using custom_lint. As such, it uses custom_lint's installation logic.
Long story short:

  • Add both riverpod_lint and custom_lint to your pubspec.yaml:

    dev_dependencies:
      custom_lint:
      riverpod_lint:
    
  • Enable custom_lint's plugin in your analysis_options.yaml:

    analyzer:
      plugins:
        - custom_lint
    

Enabling/disabling lints. #

By default when installing riverpod_lint, most of the lints will be enabled. To change this, you have a few options.

Disable one specific rule #

You may dislike one of the various lint rules offered by riverpod_lint. In that event, you can explicitly disable this lint rule for your project by modifying the analysis_options.yaml

analyzer:
  plugins:
    - custom_lint

custom_lint:
  rules:
    # Explicitly disable one lint rule
    - missing_provider_scope: false

Note that you can both enable and disable lint rules at once. This can be useful if your analysis_options.yaml includes another one:

include: path/to/another/analys_options.yaml
analyzer:
  plugins:
    - custom_lint

custom_lint:
  rules:
    # Enable one rule
    - provider_parameters
    # Disable another
    - missing_provider_scope: false

Disable all lints by default #

Instead of having all lints on by default and manually disabling lints of your choice, you can switch to the opposite logic:
Have lints off by default, and manually enable lints.

This can be done in your analysis_options.yaml with the following:

analyzer:
  plugins:
    - custom_lint

custom_lint:
  # Forcibly disable lint rules by default
  enable_all_lint_rules: false
  rules:
    # You can now enable one specific rule in the "rules" list
    - missing_provider_scope

Running riverpod_lint in the terminal/CI #

Custom lint rules created by riverpod_lint may not show-up in dart analyze. To fix this, you can run a custom command line: custom_lint.

Since your project should already have custom_lint installed (cf installing riverpod_lint), then you should be able to run:

dart run custom_lint

Alternatively, you can globally install custom_lint:

# Install custom_lint for all projects
dart pub global activate custom_lint
# run custom_lint's command line in a project
custom_lint

All the lints #

missing_provider_scope #

Flutter applications using Riverpod should have a ProviderScope widget at the top of the widget tree.

Good:

void main() {
  runApp(ProviderScope(child: MyApp()));
}

Bad:

void main() {
  runApp(MyApp());
}

provider_dependencies (riverpod_generator only) #

If a provider depends on providers which specify dependencies, they should themselves specify dependencies and include all the scoped providers.

This lint only works for providers using the @riverpod annotation.

Consider the following providers:

// A non-scoped provider
@riverpod
int root(RootRef ref) => 0;

// A possibly scoped provider
@Riverpod(dependencies: [])
int scoped(ScopedRef ref) => 0;

Good:

// No dependencies used, no need to specify "dependencies"
@riverpod
int example(ExampleRef ref) => 0;

// We can specify an empty "dependencies" list if we wish to.
// This marks the provider as "scoped".
@Riverpod(dependencies: [])
int example(ExampleRef ref) => 0;

@riverpod
int example(ExampleRef ref) {
  // rootProvider is not scoped, no need to specify it as "dependencies"
  ref.watch(rootProvider);
}

@Riverpod(dependencies: [scoped])
int example(ExampleRef ref) {
  // scopedProvider is scoped and as such specifying "dependencies" is required.
  ref.watch(scopedProvider);
}

Bad:

// scopedProvider isn't used and should therefore not be listed
@Riverpod(dependencies: [scoped])
int example(ExampleRef ref) => 0;

@Riverpod(dependencies: [])
int example(ExampleRef ref) {
  // scopedProvider is used but not present in the list of dependencies
  ref.watch(scopedProvider);
}

@Riverpod(dependencies: [root])
int example(ExampleRef ref) {
  // rootProvider is not a scoped provider. As such it shouldn't be listed in "dependencies"
  ref.watch(rootProvider);
}

scoped_providers_should_specify_dependencies (generator only) #

Providers that do not specify "dependencies" shouldn't be overridden in a ProviderScope/ProviderContainer that is possibly not at the root of the tree.

Consider the following providers:

@Riverpod(dependencies: [])
int scoped(ScopedRef ref) => 0;

@riverpod
int root(RootRef ref) => 0;

(Providers defined without riverpod_generator are not supported)

Good

void main() {
  runApp(
    ProviderScope(
      // This is the main ProviderScope. All providers can be overridden there
      overrides: [
        rootProvider.overrideWith(...),
        scopedProvider.overrideWith(...),
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ProviderScope(
      // This ProviderScope is not the root one, so only providers with "dependencies"
      // can be specified.
      overrides: [
        scopedProvider.overrideWith(...),
      ],
      child: Container(),
    );
  }
}

Bad:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ProviderScope(
        // This ProviderScope is not the root one, so only providers with "dependencies"
      // can be specified.
      overrides: [
        // rootProvider does not specify "dependencies" and therefore should not
        // be overridden here.
        rootProvider.overrideWith(...),
      ],
      child: Container(),
    );
  }
}

avoid_manual_providers_as_generated_provider_dependency #

Providers using riverpod_generator should not depend on providers which do not use riverpod_generator. Failing to do so would break the provider_dependencies lint.

Good:

@riverpod
int dep(DepRef ref) => 0;

@riverpod
int example(ExampleRef ref) {
  /// Generated providers can depend on other generated providers
  ref.watch(depProvider);
}

Bad:

final depProvider = Provider((ref) => 0);

@riverpod
int example(ExampleRef ref) {
  // Generated providers should not depend on non-generated providers
  ref.watch(depProvider);
}

provider_parameters #

Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override ==.

Good:

// Parameters may override ==
ref.watch(myProvider(ClassThatOverridesEqual()));

// Constant objects are canonalized and therefore have a consistent ==
ref.watch(myProvider(const Object()));
ref.watch(myProvider(const [42]));

// Passing a variable disable the lint, as the variable may be cached.
ref.watch(myProvider(variable));

Bad:

// New instances passed as provider parameter must override ==
ref.watch(myProvider(ClassWithNoCustomEqual()));
// List/map/set litterals do not have a custom ==
ref.watch(myProvider([42]));

unsupported_provider_value (riverpod_generator only) #

The riverpod_generator package does not support StateNotifier/ChangeNotifier and manually creating a Notifier/AsyncNotifier.

This lints warns against unsupported value types.

Good:

@riverpod
int integer(IntegerRef ref) => 0;

@riverpod
class IntegerNotifier extends _$IntegerNotifier {
  @override
  int build() => 0;
}

Bad:

// KO: riverpod_generator does not support creating StateNotifier/ChangeNotifiers/...
// Instead annotate a class with @riverpod.
@riverpod
MyStateNotifier stateNotifier(StateNotifierRef ref) => MyStateNotifier();

class MyStateNotifier extends StateNotifier<int> {
  MyStateNotifier(): super(0);
}

stateless_ref (riverpod_generator only) #

Stateless providers must receive a ref matching the provider name as their first positional parameter.

Good:

@riverpod
int myProvider(MyProviderRef ref) => 0;

Bad:

// No "ref" parameter found
@riverpod
int myProvider() => 0;

// The ref parameter is not correctly typed (int -> MyProviderRef)
@riverpod
int myProvider(int ref) => 0;

generator_class_extends (riverpod_generator only) #

Classes annotated by @riverpod must extend _$ClassName

Good:

@riverpod
class Example extends _$Example {
  int build() => 0;
}

Bad:

// No "extends" found
@riverpod
class Example {
  int build() => 0;
}

// An "extends" is present, but is not matching the class name
@riverpod
class Example extends Anything {
  int build() => 0;
}

All assists #

Wrap widget with a Consumer #

Wrap with Consumer sample

Wrap widget with a ProviderScope #

Wrap with ProviderScope sample

Convert widget to ConsumerWidget #

Convert to ConsumerWidget sample

Convert widget to ConsumerStatefulWidget #

Convert to ConsumerStatefulWidget sample

Convert functional @riverpod to class variant #

Convert provider to class variant sample

Convert class @riverpod to functional variant #

Convert provider to functional variant sample

Upcoming content: #

  • Warn if a provider's dependencies parameter doesn't match the ref.watch/read/listen usages.
  • Refactoring to convert AsyncNotifier<>Notifier + autoDispose/family variants
  • Warn if an AsyncNotifierProvider.autoDispose doesn't use an AutoDisposeAsyncNotifier

and much more

199
likes
0
pub points
96%
popularity

Publisher

unverified uploader

Riverpod_lint is a developper tool for users of Riverpod, designed to help stop common issue and simplify repetetive tasks.

Homepage
Repository (GitHub)
View/report issues

Funding

Consider supporting this project:

github.com

License

Icon for licenses.unknown (LICENSE)

Dependencies

analyzer, analyzer_plugin, collection, custom_lint_builder, meta, path, riverpod, riverpod_analyzer_utils, source_span, yaml

More

Packages that depend on riverpod_lint