Rohan Kapur
Rohan Kapur

Reputation: 139

Is there a way for multiple FutureBuilders to use the same future from a ChangeNotifier?

I have a class (that extends ChangeNotifier - Provider package) that has a function that returns a Future. What I'm trying to do is have it so that multiple futureBuilders in my UI code can receive values from this function but without having to call that function once per FutureBuilder.

However, I the function itself gets run again and again with every FutureBuilder I use. I know there must be a way to expose the Future itself through the Provider package but I can't seem to figure out how.

Here is the class that extends ChangeNotifier and has the future in it:

class ApiService extends ChangeNotifier {

  CurrencyTicker _data;

  CurrencyTicker get getdata => _data;

  set setdata(CurrencyTicker data) {
    _data = data;
  }

  Future<CurrencyTicker> fetchBaseData() async {
    final response =
    await http.get(API_URL_HERE); // url removed for stackoverflow

    if (response.statusCode == 200) {
    print('1 call logged');
    setdata =  CurrencyTicker.fromJson(json.decode(response.body));
    return CurrencyTicker.fromJson(json.decode(response.body));
    } else {
    throw Exception('Request failed: ' + response.statusCode.toString());
    }
  }
}

Here is the UI code (it's just a FutureBuilder):

class MyBody extends StatelessWidget {
  const MyBody({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final provider = Provider.of<ApiService>(context);

    return Center(
      child: FutureBuilder(
        future: provider.fetchBaseData(),
        initialData: CurrencyTicker().price,
        builder: (BuildContext context, AsyncSnapshot snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            return Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: <Widget>[
                Text(snapshot.data.company),
              ],
            );
          } else {
            return LinearProgressIndicator();
          }
        },
      ),
    );
  }
}

I haven't included the MultiProvider thats at the "top level" in the widget tree since I don't see why I'd have to. I haven't included the Model for the CurrencyTicker class. I can provide both of these if necessary.

Would appreciate any input at all here

Upvotes: 0

Views: 1173

Answers (1)

R&#233;mi Rousselet
R&#233;mi Rousselet

Reputation: 277077

You don't want to do the HTTP call directly inside the build method of your consumers.

Instead of exposing a method on your ChangeNotifier, you should expose a property:

class MyNotifier with ChangeNotifier {
  Future<Foo> foo;
}

That foo is a variable that stores the result of your last fetchData call.

Depending on your needs, you can then:

  • call fetchData in the constructor, if immediately needed
class MyNotifier with ChangeNotifier {
  MyNotifier() {
    foo = fetchData();
  }
  Future<Foo> foo;
}
  • lazy load it using a custom getter:
class MyNotifier with ChangeNotifier {
  Future<Foo> _foo;
  Future<Foo> get foo => _foo ??= fetchData();
}

Upvotes: 2

Related Questions