🙄

【解決】Drawerで表示されているページを再度呼び出すとFutureProviderがloadingにならない

2023/06/29に公開

どうしたもんかなと思っています。

Drawerのアイテムをタップして、FirstPageというページを出します。初回呼び出しの時は、Widget Treeにそのページがないので、FutureProviderがLoadingになり、dataが取得できると画面が切り替わります。

ただ、FirstPageが表示されている状態で、DrawerよりFirstPageを呼び出すコードを書くと、Widget TreeにFirstPageが残ってしまうためかautoDisposeされないようで、FutureProviderのデータ再取得が走らない。

Drawerで遷移する時にinvalidateしてみると、FirstPageが表示されている場合、loadingにはならないが、データの再取得は実行された。いやいや、AsyncValueのloadingからやり直してほしいんです!

以下が Minimal Reproducible Exampleです。
Drawerで同じページを何度も呼び出す時にRefreshしたいは結構ある要件だと思うけど、みんなどうしているんだろうか。

追記

skipLoadingOnRefresh: false で解決しました・・・ ドキュメントはちゃんと読もう。AsyncValueの。

main.dart
import 'package:drawer_refresh_sample/drawer.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

void main() {
  runApp(const ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  
  Widget build(BuildContext context) {
    return Scaffold(
      drawer: const SampleDrawer(),
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(title),
      ),
      body: const Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Good Morning',
            ),
          ],
        ),
      ),
    );
  }
}
drawer.dart
import 'package:drawer_refresh_sample/first_page.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

class SampleDrawer extends ConsumerWidget {
  const SampleDrawer({super.key});

  
  Widget build(BuildContext context, WidgetRef ref) {
    return Drawer(
      child: ListView(
        children: <Widget>[
          ListTile(
            title: const Text("Page 1"),
            trailing: const Icon(Icons.arrow_forward),
            onTap: () {
              Navigator.of(context).pop();
              ref.invalidate(helloProvider);
	      // 以下でも現象は同じ
	      // ref.refresh(helloProvider.future);
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) => const FirstPage()));
            },
          ),
        ],
      ),
    );
  }
}
first_page.dart
import 'package:drawer_refresh_sample/sample_provider.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

import 'drawer.dart';

class FirstPage extends ConsumerWidget {
  const FirstPage({super.key});

  
  Widget build(BuildContext context, WidgetRef ref) {
    final asyncHello = ref.watch(helloProvider);
    return asyncHello.when(
        data: (data) => Scaffold(
            appBar: AppBar(
              title: const Text("First Page"),
            ),
            drawer: const SampleDrawer(),
            body: SafeArea(child: Center(child: Text(data)))),
        error: (error, stackTrace) => Text(stackTrace.toString()),
        loading: () => const Center(child: CircularProgressIndicator.adaptive()));
  }
}
sample_provider.dart
import "dart:math";

import 'package:flutter/material.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'sample_provider.g.dart';


Future<String> hello(HelloRef ref) {
  return Future<String>.delayed(const Duration(seconds: 2), () {
    return "Hello${Random().nextInt(10)}";
  });
}

Discussion