Efthimis Persidis
Efthimis Persidis

Reputation: 11

ConsumerStatefulWidget does not change state (riverpod generator)

I've got this simple Homepage

@override
  Widget build(BuildContext context) {

    return Scaffold(
      body: Center(
        child: Row(
          children: [
            MyDrawer(),
            selectWindow(),
          ],
        ),
      ),
    );
  }

and

class MyDrawer extends ConsumerStatefulWidget {
   MyDrawer({super.key});

  @override
  ConsumerState createState() {
    return _MyDrawerState();
  }
}

class _MyDrawerState extends ConsumerState<MyDrawer> {
@override
  Widget build(BuildContext context) {
    final myProvider = ref.watch(myProviderProvider);

    return Drawer(
      shape: const ContinuousRectangleBorder(),
      width: 300,
      child: Text(
      myProvider.length.toString(),
      style: const TextStyle(fontSize: 24),
      ),
    );
  }

my IDE suggests that i use const in these spots const MyDrawer({super.key}); and

body: Center(
        child: Row(
          children: [
           //HERE
            const MyDrawer(),
            selectWindow(),
          ],
        ),
      ),

but when I do add the const as suggested the state does not changes when the myProvider.length changes. I have to manually refresh so the length change appears.

Am I doing anything wrong or the IDE suggests something wrong?

Upvotes: 0

Views: 119

Answers (1)

Dan R
Dan R

Reputation: 1326

Not sure what you mean by manually refresh. I suspect the issue has to do with the fact that you have used an AutoDisposeNotifierProvider. You could check the type of the object myProviderProvider.

With myProviderProvider the build method is called whenever the page is accessed, and previous changes are overwritten.

I have used the provider below (notice the option keepAlive). With this option listSelectorProvider will be of type NotifierProvider.
The random list entries are just used to see if the build method was called or not.

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

part 'list_selector.g.dart';

final random = Random();

@Riverpod(keepAlive: true)
class ListSelector extends _$ListSelector {
  @override
  List<int> build() {
     return [random.nextInt(100), random.nextInt(100)];
  }

  void add(int value) {
    state.add(value);
  }

  void set(List<int> value) {
    state = value;
  }
}

There is little benefit in using a ConsumerStatefulWidget unless you store a local state variable and are calling the setState function. I used this drawer and it works like expected:

class MyDrawer extends ConsumerWidget {
  const MyDrawer({super.key});
  
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final list = ref.watch(listSelectorProvider);

    return Drawer(
      shape: const ContinuousRectangleBorder(),
      width: 300,
      child: Text(
        list.length.toString(),
        style: const TextStyle(fontSize: 24),
      ),
    );
  }
}

To change the state of listSelectorProvider I have used the class below. Notice the const keyword before MyDrawer.

class HomePage extends ConsumerWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home Page'),
      ),
      drawer: const MyDrawer(),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Starting page' + ref.watch(listSelectorProvider).toString(),
            ),
            Text(
              'Press + to change the list. ',
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          final list = ref.watch(listSelectorProvider);
          ref.read(listSelectorProvider.notifier).add(101);
          
        },
        child: const Icon(Icons.add),
      ), 
    );
  }
}

Upvotes: 0

Related Questions