Search code examples
flutterflutter-provider

How to persist data after an update with ChangeNotifierProxyProvider in Flutter?


Here are 2 providers Person and Job. Job depends on the person.

class Person with ChangeNotifier {
  Person({required this.name, required this.age});

  final String name;
  int age;

  void increaseAge() {
    age++;
    notifyListeners();
  }
}

class Job with ChangeNotifier {
  Job(this.person);

  String career = "na";

  addCareer(value) {
    career = value;
    notifyListeners();
  }

  final Person person;
  String get title {
    return '${person.name}, $career, ${person.age}';
  }
}

Here is how these two providers are initialized with ChangeNotifierProxyProvider

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider<Person>(create: (_) => Person(name: 'John', age: 25)),
        ChangeNotifierProxyProvider<Person, Job>(
          create: (BuildContext context) => Job(Provider.of<Person>(context, listen: false)),
          update: (BuildContext context, Person person, Job? job) => Job(person),
        ),
      ],
      child: const MyApp(),
    ),
  );
}

Here is the initState function that initializes the career field in the job provider.

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

  final job = Provider.of<Job>(context, listen: false);
  job.addCareer("Accountant");
}

Now when these lines are executed, it prints "title: John, Accountant, 25"

String title = context.watch<Job>().title;
print("title: $title");

Then if this line is executed, it updates the age in Person but the career in Job is lost. It seems like the Job provider is reinitialized and didn't hold on the latest data.

context.read<Person>().increaseAge();

Executing these lines again, will give "title: John, na, 26", The age is increased to 26, but the career is lost.

String title = context.watch<Job>().title;
print("title: $title");

How can I persist the career in Job provider when the age in Person provider is increased?


Solution

  • The update of Job recreates the Job object:

              update: (BuildContext context, Person person, Job? job) => Job(person),
    

    So you might want something like this:

              update: (BuildContext context, Person person, Job? job) => job?.setPerson(person),
    

    And add a setPerson method to the Job class.