3

Given the following Dart/Flutter class structure:

import 'package:flutter/material.dart';

class A with ChangeNotifier {
  B _element1;
  B _element2;

  B get element1 => _element1;
  B get element2 => _element2;

  set element1(B value) {
    _element1 = value;
    notifyListeners();
  }

  set element2(B value) {
    _element2 = value;
    notifyListeners();
  }
}

class B {
  String x;
  String y;
}

I am trying to listen to a change of A.element1.x but the problem is, the setter of class B can't call the notifyListeners() of the class A, so either I am Listening to A and won't notice a change or I am listening to B and I am losing the context to A.

I am using the Provider package in my Flutter project. But I am not sure, if I am misunderstanding the concept of Provider package or ChangeListeners. Either way I am not able to find a elegant solution.

Is there a possibility to overwrite the setter of class B from class A? I could obviously implement a function for each element1 and element2 fields(x,y). But this is not good code style I guess.

halfer
  • 19,824
  • 17
  • 99
  • 186
THE-E
  • 193
  • 3
  • 4
  • 21
  • 1
    There’s multiple (as always;)) ways in which you could solve this: you could register a callback inside class B which then calls a function of class A, so that class A knows when to notify listeners because of changes in B. Or use a Stream, exposed by B and listened to by A. But maybe best is to look to ProxyProvider package. I don’t have experience with it but I think it could suit your use case. Here’s an [article](https://link.medium.com/bSnwfYDr55) on it. – JJuice Apr 29 '20 at 19:07
  • Could you help me out with the callback function? I am not to familiar with callback functions. I looked into the article but could not see a solution for my particular problem. I also looked into the documentation, but couldn't figure out how to use ProxyProvider, to solve my problem as well. – THE-E Apr 29 '20 at 20:08
  • Posted an example below. This is how the callback calls the `notifyListeners` inside A. I am not sure if this is better than the alternative of just calling a method on A, which then sets the property on B and calls `notifyListeners` directly. – JJuice Apr 30 '20 at 08:28

1 Answers1

2

Here's an example on how you could use callback functions. Without the callback, A won't call notifyListeners and your Home widget doesnt get rebuild. Here's a short video on what a VoidCallback is: https://www.youtube.com/watch?v=fWlPwj1Pp7U

Main function and a simple Home view:

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

import 'a.dart';

void main() {
  runApp(
    ChangeNotifierProvider<A>(
      create: (context) => A(),
      child: MaterialApp(
        home: Home(),
      ),
    )
  );
}

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<A>(
      builder: (context, model, child) {
        return Scaffold(
          body: Container(
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Text('${model.element1.x}'),
                  RaisedButton(
                    child: Text("Set x of element1."),
                    onPressed: () {
                      Provider.of<A>(context, listen: false).element1.setX = 'Set to new value';
                    },
                  ),
                ],
              ),
            ),
          )
        );
      },
    );
  }
}

And then Class A:

import 'package:flutter/material.dart';
import 'package:tryout/b.dart';

class A extends ChangeNotifier {
  B _element1 = B();
  B _element2 = B();
  
  B get element1 => _element1;
  B get element2 => _element2;

  A() {
    _element1.callback = () => notifyListeners();
    _element2.callback = () => notifyListeners();
  }

  set element1(B value) {
    _element1 = value;
    _element1.callback = () => notifyListeners();
    notifyListeners();
  }

  set element2(B value) {
    _element2 = value;
    _element2.callback = () => notifyListeners();
    notifyListeners();
  }
}

And class B:

import 'package:flutter/cupertino.dart';

class B {
  String x = "";
  String y = "";
  VoidCallback? callback;

  B({
    this.callback
  });

  set setX(String newValueX) {
    x = newValueX;
    if(callback != null) callback!();
  }

  void setY(String newValueY) {
    y = newValueY;
    if(callback != null) callback!();
  }
}
Fernando Rocha
  • 2,416
  • 3
  • 15
  • 28
JJuice
  • 1,015
  • 2
  • 12
  • 21