riverpod icon indicating copy to clipboard operation
riverpod copied to clipboard

[riverpod_lint] `provider_dependencies` seems to be missing collection of `ref.invalidate` method

Open moxiaov587 opened this issue 1 year ago • 1 comments

Describe the bug When I use ref.invalidate in my code lint does not prompt to include the provider in dependencies and when ref.invalidate fires I get an error that it needs to be included.

════════ Exception caught by gesture ═══════════════════════════════════════════
The following assertion was thrown while handling a gesture:
The provider bProvider:AutoDisposeAsyncNotifierProviderImpl<B, bool>#6153a tried to read aProvider:AutoDisposeAsyncNotifierProviderImpl<A, String>#60332, but it specified a 'dependencies' list yet that list does not contain aProvider:AutoDisposeAsyncNotifierProviderImpl<A, String>#60332.

To fix, add aProvider:AutoDisposeAsyncNotifierProviderImpl<A, String>#60332 to bProvider:AutoDisposeAsyncNotifierProviderImpl<B, bool>#6153a's 'dependencies' parameter
'package:riverpod/src/framework/element.dart':
Failed assertion: line 641 pos 11: 'listenable.dependencies == null ||
              provider != origin ||
              // Families are allowed to depend on themselves with different parameters.
              (origin.from != null && listenable.from == origin.from) ||
              origin.dependencies == null ||
              origin.dependencies!.contains(listenable.from) ||
              origin.dependencies!.contains(listenable)'

To Reproduce

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'main.g.dart';

@Riverpod(dependencies: [])
class A extends _$A {
  @override
  Future<String> build() {
    return Future.value('A');
  }
}

@Riverpod(dependencies: [])
class C extends _$C {
  @override
  Future<int> build() {
    return Future.value(0);
  }
}

@Riverpod(dependencies: [C])
class B extends _$B {
  @override
  Future<bool> build() {
    return Future.value(ref.watch(cProvider).valueOrNull == 0);
  }

  void refresh() {
    if (ref.read(cProvider).valueOrNull == 0) {
      ref.invalidate(aProvider);
    }
  }
}

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Consumer(
              builder: (context, ref, child) {
                final c = ref.watch(cProvider).valueOrNull;
                return Text(
                  'CProvider val ${c ?? 'null'}',
                );
              },
            ),
          ],
        ),
      ),
      floatingActionButton: Consumer(
        builder: (context, ref, child) {
          return FloatingActionButton(
            onPressed: () {
              ref.read(bProvider.notifier).refresh();
            },
            tooltip: 'Increment',
            child: const Icon(Icons.add),
          ); // Click me
        },
      ),
    );
  }
}

Expected behavior provider_dependencies should cover this case.

moxiaov587 avatar Jan 13 '24 03:01 moxiaov587

I tried adding RefInvalidateInvocation to cover this case so I can make a PR if needed:> (thanks to Remi's encapsulation), and I noticed that ref.exists was also missing Invocation.

moxiaov587 avatar Jan 13 '24 03:01 moxiaov587

Should be fixed on dev

rrousselGit avatar Mar 01 '24 13:03 rrousselGit