Mark
Mark

Reputation: 3859

Is it possible to have multiple Riverpod ChangeNotificationProviders of the same exact type?

I've successfully built a NewsScreen around the riverpod ChangeNotificationProvider to poll data from a REST api and then create a listview.

My app has multiple pages controlled by bottom tab navigation. In two of the tabs I want to show the NewsScreen.

The both show up great, but the problem is the datasource is the same. Meaning if on the first page I load a second page of news listings from the API, the other tab shows more news items as well.

The two pages are sharing the same data source as I guess is expected.

This is how I've declared my Provider at the top of my NewsScreen.dart page

final NewsProvider = ChangeNotifierProvider<NewsData>((ref) {
  return NewsData();
});

So in that sense its being used 'globally' by both tabbed pages.

Is there any kind of strategy to have these two pages get their data independently? I don't want to recode two versions of this new page using different providers. I'd ideally have the same code that is getting called 2 times (or more) in the tabs depending on how many tabs of news pages the user wants

Here is the general structure of my app in case it helps, but If there's any hints anyone has on how to do this, or anything I can read up on to figure it out its much appreciated!

main.dart

void main() {

  runApp(
    // Adding ProviderScope enables Riverpod for the entire project
    const ProviderScope(child: MyApp()),
  );

}


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

  @override
  Widget build(BuildContext context) {

    return  MaterialApp(
        title: Constants.appName,
        home:MainScreen()
        );


  }

mainscreen.dart

Standard persistent_bottom_navigation.   https://pub.dev/packages/persistent_bottom_nav_bar

My code can be provided if needed, but it calls NewsScreen() from 2 of the tabs on a bottom nav bar

NewsScreen.dart

final NewsProvider = ChangeNotifierProvider<NewsData>((ref) {
  return NewsData();
});



class NewsScreen extends StatefulWidget {
  NewsScreen({Key? key}) : super(key: key);


  @override
  _NewsScreenState createState() => _NewsScreenState();
}


class _NewsScreenState extends State<NewsScreen> {
  //NewsScreen({Key? key}) : super(key: key);

  int _allowNextPageLoad = 1;
  ScrollController _scrollController = ScrollController();


  @override
  void initState(){
    super.initState();

    /// LOAD THE DATA ONLY ON THE FIRST LOAD
    context.read(NewsProvider).fetchData;


     (... some scroll controller stuff ...)

  @override
  void dispose(){
      _scrollController.dispose();
      super.dispose();
  }


  @override
  Widget build(BuildContext context) {


    return
      Scaffold(
          appBar: AppBar(
              title: Text("News",
          ),
          body: RefreshIndicator(
            onRefresh: () async {
(...)
            },
            child: Center(
              child: Container(
                child: NewsScreenConsumer(),
            ),
          ),
          ),
      );
  }
}

NewsConsumer.dart

class NewsScreenConsumer extends ConsumerWidget {
  NewsScreenConsumer();


  @override
  Widget build(BuildContext context, ScopedReader watch)
  {
    /// PROVIDER SETUP
    final data = watch(NewsProvider);

        return Container ( ... UI using data from news provider ... );
   }
}

EDIT

I've gotten it to work if I move the NewsProvider = ChangeNotifierProvider into the Screen widget as follows:

class _NewsScreenState extends State<NewsScreen> {

  final NewsProvider = ChangeNotifierProvider<NewsData>((ref) {
    return NewsData();
  });
...

But a new problem arises, I have several UI widgets that depend on the information from NewsProvider and are called from within _NewsScreenState to build out the UI (essentially the widget in NewsConsumer.dart). These no longer have access to the no longer global NewsProvider. I was hoping they could be some kind of 'child' and understand NewsProvider.

For now I can physically move the code of all these widgets into my _NewsScreenState widget so that it has access to the provider, but its not very elegant. Is there a way to keep this NewsProvider as a sort of local variable for just a few 'child' widgets that are used to build the UI for NewsScreen?

Upvotes: 4

Views: 2162

Answers (1)

Mark
Mark

Reputation: 3859

Ok Using Alex Hartford's suggestion above I've looked into the .family concept and I have something that seems to be working.

I'm putting this here incase it helps anyone out there but also incase someone notices some glaring flaw in my thinking:

What I've done is switched to a NewsProvider .family by changing my global call to this

final NewsProvider = ChangeNotifierProvider.family<NewsData, int>((ref, id) {
  return NewsData();
});

Then inside my class _NewsScreenState extends State<NewsScreen> { I'll generate a random # that will serve as the ID for this family member we will be using

class _NewsScreenState extends State<NewsScreen> {

  int _randomNumber = Random().nextInt(100);
...

Now in the places I need to utilize my Provider I call it like this

await context.read(NewsProvider(_randomNumber)).fetchData;

It seems to be working just fine now. I guess there's the chance the same random # would be generated twice so I'll make it chose a random # out of 10milllion or maybe come up with some kind of random hash generator. I'm sure there's some flaw in my logic so I'm still all ears to a better approach!

Upvotes: 3

Related Questions