Flutter use ProxyProvider

Viewed 603

Description

I have a page(InitialElementResultScreen) that has to wait for several operations to be done in the InitialElementResultNotifier to display the rest of the page.

Code that works

My InitialElementResultScreen:

class InitialElementResultScreen extends StatelessWidget {
  final String title;
  InitialElementResultScreen({
    Key key,
    @required this.title,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBarWidget.withoutMenu(title: title),
      body: _buildBody(context),
    );
  }

  Widget _buildBody(BuildContext context){

    // => For use variable in my screen I use the notifier.
    var _initialElementResultProvider = Provider.of<InitialElementResultNotifier>(context);

    return SingleChildScrollView(
      child: Column(
        children: [
          ContainerComponent(
            colorContainer: AppColors.backgroundLightBasic,
            children: [
              FutureBuilder(
                  future: _initialElementResultProvider.myFuture,
                  builder: (context, snapshot){
                    if(!snapshot.hasData) {
                      return Text('loading');
                    }else{
                      return Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          // ... some widgets
                        ],
                      );
                    }
                  }
              )
            ],
          ),
        ],
      ),
    );
  }
}

When I call my InitialElementResultScreen :

ChangeNotifierProvider<InitialElementResultNotifier>(
    create: (BuildContext context) => InitialElementResultNotifier(getData: data)
)

My InitialElementResultNotifier receives external data directly in its constructor. It converts them to objects so that I can do operations on them.

My InitialElementResultNotifier :

class InitialElementResultNotifier with ChangeNotifier{

  // External Notifier
  // ---------------------------------------------------------------------------

  // Variables
  // ---------------------------------------------------------------------------
  FormInitialElementModel dataReceived;
  Future myFuture;

  // Constructor
  // ---------------------------------------------------------------------------
  InitialElementResultNotifier({
    @required String getData,
  }){
    _initialise(getData);
  }

  // Initialisation
  // ---------------------------------------------------------------------------
  Future _initialise(String getData) async{
    print('--- initialise');

    dataReceived = await _convertDataReceived(getData);
    myFuture = loadingInitialElementData();
  }

  // Functions public
  // ---------------------------------------------------------------------------
  Future<void> loadingInitialElementData() async
  {
    print('---------------------- dataReceived');
    print(dataReceived); // => I see my data in the console
    // ... some operations
  }
}

Code that does not work

In my function which loadingInitialElementData (), I would like to have access to another notifier. The notifier LoaderNotifier manages the display of different messages depending on its state that I will change according to my operations in the loadingInitialElementData() function.

For this I will use a proxy Provider. So now I call my InitialElementResultScreen like that:

ChangeNotifierProvider<LoaderNotifier>(
    create: (BuildContext context) => LoaderNotifier()
),
ProxyProvider<LoaderNotifier, InitialElementResultNotifier>(
    update: (BuildContext context, LoaderNotifier loaderNotifier, InitialElementResultNotifier initialElementResultNotifier) {
      return InitialElementResultNotifier(
          getData: data,
          loaderNotifier: loaderNotifier
      );
    }
),

I will update my InitialElementResultNotifier:

All the code is the same except 3 lines to be able to call on my other Notifier. I marked the lines as comments in the code

class InitialElementResultNotifier with ChangeNotifier{

  // External Notifier
  // ---------------------------------------------------------------------------
  LoaderNotifier _loaderNotifier; // => New line !

  // Variables
  // ---------------------------------------------------------------------------
  FormInitialElementModel dataReceived;
  Future myFuture;

  // Constructor
  // ---------------------------------------------------------------------------
  InitialElementResultNotifier({
    @required String getData,
    LoaderNotifier loaderNotifier, // => New line !
  }){
    _loaderNotifier = loaderNotifier; // => New line !
    _initialise(getData);
  }

  // Initialisation
  // ---------------------------------------------------------------------------
  Future _initialise(String getData) async{
    print('--- initialise');

    dataReceived = await _convertDataReceived(getData);
    myFuture = loadingInitialElementData();
  }

  // Functions public
  // ---------------------------------------------------------------------------
  Future<void> loadingInitialElementData() async
  {
    print('---------------------- dataReceived');
    print(dataReceived); // => I see my data in the console
    // ... some operations
  }
}

Error

I don't understand why I've this error, what is my mistake ?

======== Exception caught by widgets library =======================================================
The following assertion was thrown building InitialElementResultScreen(dirty, dependencies: [_InheritedProviderScope<InitialElementResultNotifier>]):
Tried to use Provider with a subtype of Listenable/Stream (InitialElementResultNotifier).


This is likely a mistake, as Provider will not automatically update dependents
when InitialElementResultNotifier is updated. Instead, consider changing Provider for more specific
implementation that handles the update mechanism, such as:

- ListenableProvider
- ChangeNotifierProvider
- ValueListenableProvider
- StreamProvider

Alternatively, if you are making your own provider, consider using InheritedProvider.

If you think that this is not an error, you can disable this check by setting
Provider.debugCheckInvalidValueType to `null` in your main file:
1 Answers

I would like to help you, but I feel I'm having a hard time following along. One thing I noticed however that might help is that in InitialElementResultNotifier myFuture is declared, but not initialized, and no where in that class you are calling notifyListeners therefore in InitialElementResultScreen build will not be called when the dataReceived and myFuture finally receive values or change. But the whole idea of using a FutureBuilder in conjunction with Provider feels very wrong to me. If you are to proceed with this particular design you need to look into Completers but I advice you against it.

Related