0

I am new to Provider. I am trying to use the provider model across multiple pages.

I have 2 sequence of pages. In the first sequence, there is only one page IncrementerPage, which will just increment the count. In the second sequence, there is an OptionsPage, which will display the count value and has 2 option buttons(Increment & Decrement). By clicking these buttons will navigate to corrensponding pages IncrementerPage & DecrementerPage to increment & decrement the count.

Note: In sequence1 and sequence2, the count should always start from zero.

This is what I done.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Home Page"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ChangeNotifierProvider<CounterModel>(
              create: (context) => CounterModel(),
              child: RaisedButton(
                child: Text("Sequence1"),
                onPressed: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => IncrementerPage(),
                    ),
                  );
                },
              ),
            ),
            ChangeNotifierProvider<CounterModel>(
              create: (context) => CounterModel(),
              child: RaisedButton(
                child: Text("Sequence2"),
                onPressed: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => OptionsPage(),
                    ),
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class OptionsPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Consumer<CounterModel>(
          builder: (context, model, child) {
            return Text("Count: ${model.count}");
          },
        ),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            RaisedButton(
              child: Text("Increment"),
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => IncrementerPage(),
                  ),
                );
              },
            ),
            RaisedButton(
              child: Text("Decrement"),
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => DecrementerPage(),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

class IncrementerPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Incrementor"),
      ),
      body: Center(
        child: Consumer<CounterModel>(
          builder: (context, model, child) {
            return Text(
              '${model.count}',
              style: const TextStyle(
                fontSize: 32,
                fontWeight: FontWeight.bold,
                color: Colors.black54,
              ),
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () {
          Provider.of<CounterModel>(context, listen: false).increment();
        },
      ),
    );
  }
}

class DecrementerPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Decrementer"),
      ),
      body: Center(
        child: Consumer<CounterModel>(
          builder: (context, model, child) {
            return Text(
              '${model.count}',
              style: const TextStyle(
                fontSize: 32,
                fontWeight: FontWeight.bold,
                color: Colors.black54,
              ),
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.remove),
        onPressed: () {
          Provider.of<CounterModel>(context, listen: false).decrement();
        },
      ),
    );
  }
}

class CounterModel extends ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }

  void decrement() {
    _count--;
    notifyListeners();
  }
}

But I am getting this error:

Could not find the correct Provider<CounterModel> above this Consumer<CounterModel> Widget

By moving ChangeNotifierProvider<CounterModel> outside the MaterialApp fixed the error. But both sequence of pages seems to be using the same counter variable (or model like global). So how to pass different model object per each sequence of pages? or is there any better way to do this?.

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Crazy Lazy Cat
  • 13,595
  • 4
  • 30
  • 54

1 Answers1

0

Probably the following code is causing the exception:

floatingActionButton: FloatingActionButton(
    child: Icon(Icons.remove),
    onPressed: () {
        Provider.of<CounterModel>(context, listen: false).decrement();
    },
),

As you can see, the ChangeNotifierProvider for CounterModel is Wrapped around widgets RaisedButton() for sequence 1 and 2 only.

Which are under the body parameter. While the floatingActionButton a colleague parameter for body.

Meaning the Provider.of<CounterModel> can not find the Provider from it's parent widgets.

What you can do is, Move the ChangeNotifierProvider code to cover HomePage and remove every other ChangeNotifierProvider code for CounterModel in the app.

Harshvardhan Joshi
  • 2,855
  • 2
  • 18
  • 31
  • Thanks. But even if we move `ChangeNotifierProvider` above `Scaffold`, the same problem will occur. Because `Provider.of(context)` won't look at the previous routes. – Crazy Lazy Cat Jan 02 '20 at 14:12
  • updated the answer, thanks for pointing it out. However in my experience, If the notifier provider is directly above the scaffold, it shouldn't be a problem. All the consumers and `Provider.of` getters will be able to get the provider since the most top one is still inside the scaffold of the parent widget. – Harshvardhan Joshi Jan 02 '20 at 16:51
  • Moving `ChangeNotifierProvider` above `Homepage` won't work. It should be outside the `MaterialApp` as I mentioned in my question. The problem is the counter model will act like global if I do that. – Crazy Lazy Cat Jan 02 '20 at 21:39
  • if you want to use two different models for each sequences, Change notifier must be wrapped on each sequences widgets respectively. but then floating action button won't work at will. – Harshvardhan Joshi Jan 03 '20 at 01:50