Home > Back-end >  Riverpod - Rebuild screen when updating single property of StateNotifier
Riverpod - Rebuild screen when updating single property of StateNotifier

Time:02-01

I've been using what I found out was an anti-pattern when using Riverpod as my state management tool. I did what the user who asked this question did: update the properties that you want to change and add state = state. This solution worked up until the state_provider package was updated beyond 0.7.0, after which this solution stopped working altogether.

I've been trying the solution used in the thread below, but this will also not rebuild the UI for me. Can anyone tell me what I'm doing wrong here? I feel like the solution below should work, as this is essentially what you do with basically any other state management package, such as Redux.

My state model, Notifier and Provider

import 'package:hooks_riverpod/hooks_riverpod.dart';

class Test {
  Test({this.prop1, this.prop2});

  String? prop1;
  int? prop2;
}

class TestNotifier extends StateNotifier<Test> {
  TestNotifier() : super(Test());
  void update() {
    state.prop2 ??= 0;
    state = state..prop2 = state.prop2!   1;
  }
}

final StateNotifierProvider<TestNotifier, Test> testProvider =
    StateNotifierProvider((ref) => TestNotifier());

Full main.dart file

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:practice_buddy/state/test_state.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends HookConsumerWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final _testProvider = ref.watch(testProvider);
    final _testNotifier = ref.watch(testProvider.notifier);
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '${_testProvider.prop2 ?? 0}',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _testNotifier.update(),
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

Pubspec to show package versions

dependencies:
  cloud_firestore: ^3.1.7
  firebase_auth: ^3.3.6
  firebase_core: ^1.12.0
  flutter:
    sdk: flutter
  flutter_hooks: ^0.18.2
  hooks_riverpod: ^1.0.3

My Flutter version is 2.8.1.

CodePudding user response:

This can be solved by passing a new instance on update.

 void update() {
    state.prop2 ??= 0;
    state = state.copyWith(prop2: state.prop2!   1);
  }

And on model class copyWith constructor.

  Test copyWith({
    String? prop1,
    int? prop2,
  }) {
    return Test(
      prop1: prop1 ?? this.prop1,
      prop2: prop2 ?? this.prop2,
    );
  }
  •  Tags:  
  • Related