'ProxyProvider - how to call proxy from its sub-providers?

What would be the correct way to call (and pass values to) ProxyProvider from its "sub"providers? Currently I'm passing a callback function to "sub"provider as a parameter, storing it as a Function and then I can call it when needed. It works in a sense that ProxyProvider is called (and value is passed), but at the same time it breaks notifyListeners(), which is called next - searches getter in "sub"provider (and can't find it) despite that Consumer is used just for ProxyProvider.

This is the error I receive:

error: org-dartlang-debug:synthetic_debug_expression:1:1: Error: The getter 'audInd' isn't defined for the class 'AudioModel'.

  • 'AudioModel' is from 'package:quiz_game_new/models/audioModel.dart' ('lib/models/audioModel.dart'). Try correcting the name to the name of an existing getter, or defining a getter or field named 'audInd'. audInd ^^^^^^

Code

Provider (audioModel.dart):

class AudioModel extends ChangeNotifier {
    int _audioIndex = -1;
    Function? audioIndexChanged;
      
    void setCallbacks(Function _audioPlaybackCompleted, Function _audioIndexChanged) {
        audioPlaybackCompleted = _audioPlaybackCompleted;
        audioIndexChanged = _audioIndexChanged;
    }
    
    //Some code that changes _audioIndex and afterwards calls audioIndexChanged!(_audioIndex)
}

ProxyProvider (commonModel.dart)

class CommonModel extends ChangeNotifier {
    CommonModel(this.audioModel);
    final AudioModel audioModel;
    
    int _audioIndex = -1;
    int get audioIndex => _audioIndex;
    
    void setCallbacksForAudioPlayback() {
        audioModel.setCallbacks(audioPlaybackCompleted, audioIndexChanged);
    }

    void audioIndexChanged(int audInd) {
        _audioIndex = audInd;
        notifyListeners();
    }
}

Initialization:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
        providers: [
          ChangeNotifierProvider<STTModel>(create: (context) => STTModel()),
          ChangeNotifierProvider<QuestionModel>(
              create: (context) => QuestionModel()),
          ChangeNotifierProvider<AudioModel>(create: (context) => AudioModel()),
          ChangeNotifierProxyProvider3<STTModel, QuestionModel, AudioModel,
                  CommonModel>(
              create: (BuildContext context) => CommonModel(
                  Provider.of<STTModel>(context, listen: false),
                  Provider.of<QuestionModel>(context, listen: false),
                  Provider.of<AudioModel>(context, listen: false)),
              update:
                  (context, sttModel, questionModel, audioModel, commonModel) =>
                      CommonModel(sttModel, questionModel, audioModel))
        ],
        child: MaterialApp(
            title: 'Flutter Demo',
            initialRoute: '/',
            routes: {
              '/': (context) => ScreenMainMenu(),
              '/game': (context) => ScreenGame(),
            }));
  }
}


Solution 1:[1]

What would be the correct way to call (and pass values to) ProxyProvider from its "sub"providers?

I'm not a big fan of "nested" Providers : it often leads to this kind of issues and doesn't ease the readability.
In my projects, I usually use a Provider for each Feature, which I declare and Consume at the lowest level possible. In your case, I guess I'd juste have used your STTModel, QuestionModel and AudioModel and would have forgotten the idea of a CommonModel (whom only job is is to merge all your Providers I guess?).

You can still keep your logic, but you should take in consideration the following :

In your AudioModel class, update the method where the _audioIndex and add a notifyListeners()

class AudioModel extends ChangeNotifier {
  //...
  int get audioIndex => _audioIndex;

  void updateIndex(int index) {
    _audioIndex = index; 
    //The rest of your code
    notifyListeners();
  } 

  //...
}

The creation of your Providers looks alright, but consider updating the update method of your ChangeNotifierProxyProvider for something like that :

update: (_, sttModel, questionModel, audioModel) => 
commonModel!..update(sttModel, questionModel, audioModel),

and in your CommonModel

 void update(SttModel sttModelUpdate, QuestionModel questionModelUpdate, AudioModel audioModelUpdate) {
    audioModel = audioModelUpdate;
    questionModel = questionModelUpdate;
    sttModel = sttModelUpdate;
    
    //Retrieve the index here from your audioModel
    _audioIndex = audioModel.audioIndex;

    notifyListeners();
  }

This way, whenever you call your updateIndex method in your AudioModel class, the notifyListeners() will update the CommonModel and you'll have the _audioIndex up to date.
And then it should work fine, no need for your callback methods anymore !

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 FDuhen