4

So I’m a little confused about how to port an application using setstate to provider.

Let’s say I have a person model which extends provider. Now I also want to have multiple person reports. Each report has it’s own properties such, title, created date etc plus a list of people.

The trouble I’m having understanding is how would I create a provider per person and per person report? All the examples I’ve seen of using provider appear to have just one instance of the provider.

Effectively I would want to be able to edit person ‘A’s values without it effecting person ‘B’s

EDIT:

Each instance of my object will need a separate state. How can provider manage states of multiple objects all of the same type?.

EDIT 2:

I’ll try to clarify further with a different example. Imagine the counter widget that is the default example when creating a flutter app. What if I require a list of dynamically created counters (maybe 10, maybe 100 of them). Would each counter would have it’s own provider controlling it’s state and if so, how would we create that?

Lee
  • 381
  • 1
  • 7
  • 16

4 Answers4

1

There is no hard rule on how to structure your data. From what i understand from you question :

 class Person{
  String name;
  Report reportData;
  
  Person(this.name, this.reportData);
}

class Report{
  String title;
  Map<String, dynamic> data;
  
  Report(this.title, this.data);
}

class PersonsData with ChangeNotifier
{
  List<Person> l1 = [];
  
  void addData()
  {
    l1.add(Person('John', Report('report1', {'dataTitle' : 'datDescription'})));
  }
}

Now with PersonsData class you can manage your persons. Every person has a report type object which represents the data within.

Fiaz Ali
  • 123
  • 11
  • How would I display multiple people on the screen at once, each with their own provider state? – Lee May 28 '21 at 19:39
0

I'm not sure if I'm understanding your question correctly, but a provider does not contain data, it just makes it available, and if you're talking about the provider package it helps other parts of your app get notified when changes have been done to your data. while a model is what you can make instances of.

  • Each instance of my object will need a separate state. How can provider manage states of multiple objects all of the same type?. – Lee May 28 '21 at 18:49
  • I suggest using BLoC or cubits for state management. check out this playlist: https://www.youtube.com/watch?v=w6XWjpBK4W8&list=PLptHs0ZDJKt_T-oNj_6Q98v-tBnVf-S_o and please upvote me if it helped:) – Fatemeh Hasheminasab May 28 '21 at 19:08
0

Your state objects must be uniquely named types, otherwise, Provider can't distinguish them.

You could create a base Person class and then extend that base class into PersonA, PersonB, etc.

Then they can be controlled separately, but still rely on the base class' functionality.

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

class Person with ChangeNotifier {
  String name;

  Person(this.name);

  void changeName(String newName) {
    name = newName;
    notifyListeners();
  }
}

class PersonA extends Person {
  PersonA(String value) : super(value);
}

class PersonB extends Person {
  PersonB(String value) : super(value);
}

class ProviderDuplicatePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => PersonA('Billy Boy')),
        ChangeNotifierProvider(create: (_) => PersonB('Francis')),
      ],
      child: Scaffold(
        appBar: AppBar(
          title: Text('Provider Duplicate Types'),
        ),
        body: DuplicateProviderStateObjects(),
      )
    );
  }
}

class DuplicateProviderStateObjects extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          Column(
            children: [
              Text('Provider One:'),
              Text(Provider.of<PersonA>(context).name),
              ElevatedButton(
                child: Text('Change Person A'),
                onPressed: () => Provider.of<PersonA>(context, listen: false).changeName("Kong"),
              )
            ],
          ),
          Column(
            children: [
              Text('Provider Two:'),
              Text(context.watch<PersonB>().name),
              ElevatedButton(
                child: Text('Change Person B'),
                onPressed: () => Provider.of<PersonB>(context, listen: false).changeName("Godzilla"),
              )
            ],
          ),
        ],
      ),
    );
  }
}
Baker
  • 24,730
  • 11
  • 100
  • 106
  • That doesn’t sound manageable if the list of people is dynamically generated by the user or loaded from an external data source? – Lee May 29 '21 at 05:22
  • @Lee Totally agree. We wouldn't create dozens of Person classes that for all intents & purposes are the same. This answer is to illustrate the mechanics of how to create multiple instances of the same provider type. Can you explain what's preventing you from using a data structure (like array, or map, etc.) inside of your state class to maintain state for multiples of your object? – Baker May 29 '21 at 19:19
0

I don't know if there is a way to create Providers dynamically, like in the example of edit2, but if you want to create multiple providers of the same type without repeating code you could pass a type T to the class which extends ChangeNotifier.

class Counter<T> extends ChangeNotifier {
    late int value;

    Counter() { value = 0; }

    int get getValue => value;

    void increase() {
        value += 1;
        notifyListeners();
    }
}

Passing different types should create different classes in memory that will manage the state of these different counters. Illustration:

class A {}
class B {}
class C {}

MultiProvider(
    providers: [
        ChangeNotifierProvider(
            create: (_) => Counter<A>()),
        ChangeNotifierProvider(
            create: (_) => Counter<B>()),
        ChangeNotifierProvider(
            create: (_) => Counter<C>()),
    ],
    child: Home(),
);
chrisrn
  • 13
  • 2