gql-dart / ferry

Stream-based strongly typed GraphQL client for Dart
https://ferrygraphql.com/
MIT License
602 stars 116 forks source link

ferry_flutter: endless loading state after the cache has been cleared #586

Open ipg0 opened 6 months ago

ipg0 commented 6 months ago

I have implemented a global app-wide RefreshIndicator, which should refresh all data, that is fetched from the server by Operaton widgets (auth data and mutations are fetched / executed manually, so they shouldn't be affected).

My solution was to handle the refresh by clearing the Ferry cache (which is a HiveStore) and then rebuilding all children of the global app layout widget.

But, whenever I activate this callback, the active view goes into an endless loading state. If I navigate to a different view and then back, the view reloads as expected.

This is what the RefreshIndicator looks like:

Consumer<GraphqlProvider>(
  builder: (context, provider, child) => RefreshIndicator(
    onRefresh: () async {
      provider.client.cache.clear();
      _rebuildAllChildren(context);
    },
    // Pull from top to show refresh indicator.
    child: this.child,
  ),
),

And this is the _rebuildAllChildren function:

void _rebuildAllChildren(BuildContext context) {
  void rebuild(Element el) {
    el.markNeedsBuild();
    el.visitChildren(rebuild);
  }

  (context as Element).visitChildren(rebuild);
}

flutter doctor output:

[✓] Flutter (Channel stable, 3.19.0, on Ubuntu 23.10 6.5.0-25-generic, locale en_US.UTF-8)
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[✓] Chrome - develop for the web
[✓] Linux toolchain - develop for Linux desktop
[✓] Android Studio (version 2023.1)
[✓] VS Code (version 1.86.2)
[✓] Connected device (3 available)
[✓] Network resources
knaeckeKami commented 6 months ago

Yes, if you clear the cache, you'll need to manually execute the requests to populate it again.

Rebuilding all widgets does nothing here, as a mere rebuild keeps the state. If you really want to go with a solution like this, you will have to throw away the state of all stateful widgets, e.g. like this (though I would recommend a more granular approach, like keeping track of all currently watched graphql queries, and -re-executing them)