0

I have a problem with the Flutter Provider pattern, I need to access Provides from Class where I don't have Context.

Providers :

import 'package:flutter/foundation.dart';
class TokenProvider with ChangeNotifier {
  TokenService tokenService = TokenService();
  String _accessToken = '';

  String get accessToken {
    return _accessToken;
  }

  dynamic setAccessToken(data) async {

    _accessToken = data;

  }
}

Class :


import '../constants/constants.dart';
import '../models/models.dart';
import './network-call/base-service.dart';

class TokenService extends BaseService {
  Future<String> getToken() async {
    final dynamic response = await serviceCall(
        url: ApiName().apiName(api: ServiceName.TOKEN),
        method: ApiMethod.POST,
        queryParameters: {
          'id': Preferences().env['id'],
          'secret': Preferences().env['secret'],
          'type': 'rrrr'
        });
Need to set this responce Data in Providers
  }

}

Thank you.

2 Answers2

0

try to add "notifyListeners();"

  dynamic setAccessToken(data) async {

    _accessToken = data;
    notifyListeners();
  }
Kevin Lin
  • 87
  • 7
0

I use to pass the context when needed, as dlohani suggests in the comment at the question, but I found myself in the same situation and applied a solution inspired by the communication pattern used between isolates: messages exchange.

The Provider class need to have a ReceiverPort field which listens to request messages. Ones the message reaches this listener you're inside the Provider, so you can retrieve data and send them back, again in the ReceiverPort's fashion, that is using the sendPort of the ReceiverPort registered in the requesting class.

In code below I suppose messages are Maps to clarify the type of data exchanged:

class SomeProvider with ChangeNotifier {
  var _innerData = yourData;
  var _providerReceiverPort = ReceiverPort();

  SomeProvider() {
    // The registration is necessary to "publish" the receiver port 
    IsolateNameServer.registerPortWithName(
      _providerReceiverPort, "SomeProviderPort");

    _providerReceiverPort.listen(
      (message) {
        // I retrieve the port to send the response to
        var port = message["sendPort"];
        // The answer follows the rules of messaging: maps and lists are ok
        port.send({"data": _innerData.getSomething()});
      }
    );
  }
}

class SomeClient {
  var _clientReceiverPort = ReceiverPort();
  
  someFunction(){
    // First step: prepare the receiver to obtain data
    _clientReceiverPort.listen(
      (message) {
        // Data are stored in the map containing a "data" key
        var response = message["data"];
        ...
      }
    );

    // Now I can retrieve the provider port by using the same name it uses to publish it
    var providerPort = IsolateNameServer.lookupPortByName("SomeProviderPort");
    // The message must include the sendPort to permit the provider to address the response
    providerPort.send({"sendPort": _clientReceiverPort.sendPort});
  }
}

The drawback of this solution is that the Provider doesn't work as a provider for the SomeClient class. You can obviously notify if any change in the listener is important for the subscribers: for example, I use this pattern to update data in the provider from a background isolate.

As I said, this is a workaround, any suggestion to improve is welcome.

marcolav
  • 405
  • 1
  • 6
  • 17