skip to Main Content
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

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

class Counter1 extends ChangeNotifier {
  int _counter = 0;

  int get counter {
    return _counter;
  }

  void increment() {
    _counter = _counter + 1;
    notifyListeners();
  }

  void reset() {
    _counter = 0;
    notifyListeners();
  }
}

class Counter2 extends ChangeNotifier {
  int _counter = 0;

  int get counter {
    return _counter;
  }

  void increment() {
    _counter = _counter + 1;
    notifyListeners();
  }
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: MultiProvider(providers: [
        ChangeNotifierProvider(
          create: (context) {
            return Counter2();
          },
        ),
        ChangeNotifierProxyProvider<Counter2, Counter1>(
          create: (context) {
            return Counter1();
          },
          update: (BuildContext context, Counter2 value, Counter1? previous) {
            if (previous == null) {
              throw StateError("Counter1 is null");
            }
            value.removeListener(previous.reset); // To avoid multiple listener
            value.addListener(previous.reset);
            return previous;
          },
        ),
      ], child: const MyHomePage()),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    var counter1 = Provider.of<Counter1>(context);
    var counter2 = Provider.of<Counter2>(context);
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title:
            const Text("Let's try to reset counter 1 when counter 2 increment"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'Counter 1:',
            ),
            Text(
              '${counter1.counter}',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
            ElevatedButton(
                onPressed: counter1.increment,
                child: const Text("Increment counter 1")),
            const Text(
              'Counter 2:',
            ),
            Text(
              '${counter2.counter}',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
            ElevatedButton(
                onPressed: counter2.increment,
                child: const Text("Increment counter 2")),
          ],
        ),
      ),
    );
  }
}

2

Answers


  1. To simplify the code, you can remove the manual adding and removing of listeners by using context.watch, which automatically listens for changes.

    Here is my proposition:

    import 'package:flutter/material.dart';
    import 'package:provider/provider.dart';
    
    class Counter1 extends ChangeNotifier {
      int _counter = 0;
    
      int get counter => _counter;
    
      void increment() {
        _counter = _counter + 1;
        notifyListeners();
      }
    
      void reset() {
        _counter = 0;
        notifyListeners();
      }
    }
    
    class Counter2 extends ChangeNotifier {
      int _counter = 0;
    
      int get counter => _counter;
    
      void increment() {
        _counter = _counter + 1;
        notifyListeners();
      }
    }
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MultiProvider(
          providers: [
            ChangeNotifierProvider(create: (_) => Counter2()),
            ChangeNotifierProxyProvider<Counter2, Counter1>(
              create: (_) => Counter1(),
              update: (context, counter2, counter1) {
                if (counter1 == null) throw ArgumentError.notNull('counter1');
                return counter1;
              },
            ),
          ],
          child: MaterialApp(
            home: MyHomePage(),
          ),
        );
      }
    }
    
    class MyHomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final counter1 = context.watch<Counter1>();
        final counter2 = context.watch<Counter2>();
    
        return Scaffold(
          appBar: AppBar(
            backgroundColor: Theme.of(context).colorScheme.inversePrimary,
            title:
                const Text("Let's try to reset counter 1 when counter 2 increment"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text('Counter 1: ${counter1.counter}',
                    style: const TextStyle(fontSize: 20)),
                Text('Counter 2: ${counter2.counter}',
                    style: const TextStyle(fontSize: 20)),
                const SizedBox(height: 20),
                ElevatedButton(
                  onPressed: () {
                    counter1.increment();
                  },
                  child: const Text('Increment Counter 1'),
                ),
                ElevatedButton(
                  onPressed: () {
                    counter1.reset();
                    counter2.increment();
                  },
                  child: const Text('Increment Counter 2'),
                ),
              ],
            ),
          ),
        );
      }
    }
    
    Login or Signup to reply.
  2. Your use of ChangeNotifierProxyProvider is generally correct, but there’s a small issue in how you’re managing the listeners. Let’s modify the code to fix this and improve the overall structure. Here’s the full, corrected version of your code:

    import 'package:flutter/material.dart';
    import 'package:provider/provider.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class Counter1 extends ChangeNotifier {
      int _counter = 0;
    
      int get counter => _counter;
    
      void increment() {
        _counter++;
        notifyListeners();
      }
    
      void reset() {
        _counter = 0;
        notifyListeners();
      }
    }
    
    class Counter2 extends ChangeNotifier {
      int _counter = 0;
    
      int get counter => _counter;
    
      void increment() {
        _counter++;
        notifyListeners();
      }
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          theme: ThemeData(
            colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
            useMaterial3: true,
          ),
          home: MultiProvider(
            providers: [
              ChangeNotifierProvider(create: (_) => Counter2()),
              ChangeNotifierProxyProvider<Counter2, Counter1>(
                create: (_) => Counter1(),
                update: (_, counter2, counter1) {
                  // Remove the old listener and add a new one
                  counter2.removeListener(counter1!.reset);
                  counter2.addListener(counter1.reset);
                  return counter1;
                },
              ),
            ],
            child: const MyHomePage(),
          ),
        );
      }
    }
    
    class MyHomePage extends StatelessWidget {
      const MyHomePage({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            backgroundColor: Theme.of(context).colorScheme.inversePrimary,
            title: const Text("Let's try to reset counter 1 when counter 2 increments"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text('Counter 1:'),
                Consumer<Counter1>(
                  builder: (_, counter1, __) => Text(
                    '${counter1.counter}',
                    style: Theme.of(context).textTheme.headlineMedium,
                  ),
                ),
                ElevatedButton(
                  onPressed: () => context.read<Counter1>().increment(),
                  child: const Text("Increment counter 1"),
                ),
                const SizedBox(height: 20),
                const Text('Counter 2:'),
                Consumer<Counter2>(
                  builder: (_, counter2, __) => Text(
                    '${counter2.counter}',
                    style: Theme.of(context).textTheme.headlineMedium,
                  ),
                ),
                ElevatedButton(
                  onPressed: () => context.read<Counter2>().increment(),
                  child: const Text("Increment counter 2"),
                ),
              ],
            ),
          ),
        );
      }
    }
    

    Key changes and improvements:

    1. In the ChangeNotifierProxyProvider, we’ve simplified the update function. We don’t need to check if previous (now counter1) is null because the create function ensures it’s always created.
    2. We’re still removing the old listener before adding a new one to avoid multiple listeners, which is good practice.
    3. In the MyHomePage widget, we’ve used Consumer widgets for more efficient rebuilds. This way, only the Text widgets showing the counter values will rebuild when the counters change, not the entire column.
    4. We’ve used context.read().increment() in the button callbacks instead of storing the counters in variables. This is more efficient as it doesn’t cause unnecessary rebuilds.
    5. We’ve added a SizedBox for some vertical spacing between the two counters.
    6. Minor syntax improvements like using => for single-line getters and removing unnecessary curly braces.

    This implementation should work as intended: Counter1 will reset to 0 whenever Counter2 is incremented. The use of ChangeNotifierProxyProvider is correct here, as it allows Counter1 to depend on and react to changes in Counter2.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search