skip to Main Content

I am building an application focusing on reminders. From the list of reminders present on the homepage, a reminderSection widget is shown. The reminderSection has an attribute thisReminder. I want thisReminder to be accessible to all of the descendant widgets of thisReminder. So that they can use thisReminder and I don’t have to pass it around.

What I’ve done is use Riverpod. I wrapped myApp with ProviderScope and defined the provider in the initState of reminderSection like this:

final thisReminderProvider = Provider<Reminder>((ref) => widget.thisReminder);

And I want to use it in another widget, for example, reminderFormSection which is just where the title and dateTime of the reminder is set. I did the necessary changes to the class definition and tried accessing the provider in the initState like this:

class ReminderFormSection extends ConsumerStatefulWidget {

  const ReminderFormSection({
    super.key,
  });

  @override
  ConsumerState<ReminderFormSection> createState() => _ReminderFormSectionState();
}

class _ReminderFormSectionState extends ConsumerState<ReminderFormSection> {
  late Reminder thisReminder;

  @override
  void initState() {

    thisReminder = ref.read(thisReminderProvider);
    super.initState();
  }
}

The problem is that I’m getting the error that thisReminderProvider is undefined. I think I need to define the provider outside the class and the reminderSection file’s path to the reminderFormSection file. But how do I define the provider outside the class if the object I want to provide is a class attribute?

3

Answers


  1. Declare your provider outside of your ReminderForm or ReminderFormState section

    final myProvider = Provider<String>((_) => 'John Doe');
    
    class MyProviderClient extends ConsumerWidget {
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        // watch your provider here
        final providerValue = ref.watch(myProvider);
        return Text(providerValue);
      }
    }
    

    if you have a ConsumerStatefulWidget

    @override
      void initState() {
      var providerValue = ref.read(myProvider);
    }
    
    Login or Signup to reply.
  2. It seems what you are looking for it to be able to change the reminder that is selected from one part of your code, and be able to get the current value in another widget (ReminderFormSection).

    Providers shall always be global variables. In your case, since you want to be able to change the state you shall use a "Notifier":

    final reminderProvider = NotifierProvider<ReminderNotifier, Reminder?>(ReminderNotifier.new);
    
    final class ReminderNotifier extends Notifier<Reminder?> {
      @override
      Reminder? build() {
        return null;
      }
    
      /// Function used to change the selected reminder, which will notify any widgets watching the Provider.
      void select(final Reminder reminder) {
        state = reminder;
      }
    }
    

    Wherever you are selecting the reminder, use the notifier of the provider:

    final reminderNotifier = ref.read(reminderProvider.notifier);
    // ...
    reminderNotifier.select(reminder);
    

    Then, in widgets where you want to access to the currently selected reminder, you shall watch the provided value.

    class ReminderFormSection extends ConsumerWidget {
    
      @override
      Widget build(BuildContext context, WdigetRef ref) {
        final Reminder? reminder = ref.watch(reminderProvider);
    
    Login or Signup to reply.
  3. Seems the issue lies with defining the provider within initState of reminderSection. Providers in Riverpod should be defined outside of widgets and accessed throughout the widget tree. Here’s how to achieve this and make thisReminder accessible to all descendants:

    1. Define the Provider Outside reminderSection:

    Create a separate file (e.g., reminder_providers.dart) to house your providers.
    Define thisReminderProvider as a global final variable:

    // reminder_providers.dart
    final thisReminderProvider = Provider<Reminder>((ref) => throw UnimplementedError());
    
    1. Pass thisReminder as an Argument:

    Instead of using a provider within reminderSection, modify it to receive thisReminder as an argument in its constructor:

    // reminder_section.dart
    class ReminderSection extends StatelessWidget {
      final Reminder thisReminder;
    
      const ReminderSection({
        super.key,
        required this.thisReminder,
      });
    
      // ... rest of your widget implementation
    }
    
    1. Provide thisReminder in MyApp:

    Wrap your MyApp with ProviderScope.
    In the build method of MyApp, provide thisReminder using Provider.value:

    // main.dart
    void main() {
      runApp(
        ProviderScope(
          child: MyApp(
            // Access your reminder data here and pass it to ReminderSection
            thisReminder: Reminder(/* Initialize your reminder object */),
          ),
        ),
      );
    }
    
    class MyApp extends StatelessWidget {
      final Reminder thisReminder;
    
      const MyApp({super.key, required this.thisReminder});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          // ...
          home: ReminderSection(thisReminder: thisReminder),
        );
      }
    }
    

    Now, inside reminderFormSection, you can directly access thisReminder via the Consumer widget and the ref.watch method:

    class ReminderFormSection extends ConsumerWidget {
    const ReminderFormSection({super.key});
    
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        final thisReminder = ref.watch(thisReminderProvider);
    
        // Use thisReminder in your form section
    
        return // ...;
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search