0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Flutter】バグを生み出してしまうcontextの使い方

Posted at

1.前書き

何気なくcontextを使ってしまうと引き起こしてしまう バグや、挙動の違いなどについて紹介しています。 contextについてあまり分からないと言った方は
Flutterのcontextとは何者か。
の記事で解説してます。

2.BottomNavigationを使用した際のモーダル

ここにサンプルアプリがあります。
内容は至って単純で
①BottomNavigation機能
②モーダルの表示
のみです。
画面内の「push」ボタンを押すと下からモーダルが表示されます。
モーダルを表示させる際に 「context」を引数としてonPressed関数に渡して
showModalBottomSheetに渡しています。
この際に、useRootNavigatorをfalseにするかtrueにするかで挙動が変わります。

useRootNavigator true false
挙動

以下はソースコードになります。

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

void main() => runApp(const MyApp());

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

  void onPressed(BuildContext context) async {
    await showModalBottomSheet(
      useRootNavigator: false, //←useRootNavigatorがtrueかfalseで挙動が変わる
      context: context,
      builder: (context) => Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          ListTile(
            title: const Text('POP'),
            onTap: () => Navigator.of(context).pop('delete'),
          ),
          const Divider(),
          ListTile(
            title: const Text('POP'),
            onTap: () => Navigator.of(context).pop('delete'),
          ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: CupertinoTabScaffold(
            tabBar: CupertinoTabBar(items: const [
              BottomNavigationBarItem(
                icon: Icon(Icons.music_note),
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.settings),
              ),
            ]),
            tabBuilder: (context, index) {
              switch (index) {
                case 0:
                  return CupertinoTabView(
                    builder: (context) {
                      return CupertinoPageScaffold(
                        child: Center(
                          child: ElevatedButton(
                            child: const Text('push'),
                            onPressed: () => onPressed(context),
                          ),
                        ),
                      );
                    },
                  );
                case 1:
                  return CupertinoTabView(
                    builder: (context) {
                      return const CupertinoPageScaffold(
                        child: Center(child: Text('tab 2')),
                      );
                    },
                  );
              }
              return Container();
            }));
  }
}

userootNavigatorがtrueとfalseの場合では
画面遷移を管理するNavigatorStateの取得先が異なります。
正確に言うと、
userootNavigatorがtrueの場合は
trueの場合はfindRootAncestorStateOfTypeメソッド
falseの場合はfindAncestorStateOfTypeメソッドが呼ばれる。
それぞれのメソッドの違いが親のたどり方になります。
分かりやすく図解で説明します。
(こちらの記事ではコードベースで詳細を追っています。)


useRootNavigator navigatorState取得の動き
true
false

Flutterではデフォルトで画面遷移を管理するNavigatorStateが用意されており
BottomNavigationなどの機能を追加した際は
NavigatorStateがネストされている状態となります。
デフォルトで画面遷移を管理するNavigatorStateに対して操作したい場合は
useRootNavigatorをtrueにすることで
画面全体に対してモーダルなどを被せることが出来ます。
逆にfalseの場合は、BottomNavigationが管理するNavigationStateを操作しているので
BottomNavigationで管理している内側の画面にのみモーダルが表示されます。
ここは、理解していないと意図しないバグを生み出す可能性があるのでしっかり理解しておきましょう!

3.contextが変わってしまう問題

これは間違った再帰処理の方法によりcontextが変わってしまう問題。
ユーザ登録処理でユーザはエラーの失敗により3回までリトライ処理を行うことが出来るとする。
まず、
1.OnTappedResisterButtonにcontextを渡す。
2,3.ユーザ登録処理に失敗
4.ダイアログの【OK】ボタンタップ時などに実行する関数にOnTappedResisterButtonを渡す。
 この際に渡すcontextは【context_1】
5.ユーザはダイアログの【OK】ボタンをタップして再度ユーザ登録処理を行う
6.その際に、一回目の登録処理では行われなかった処理がifなどで実行され 
 stateが更新されpresentation側でリビルドが走る
 この際に、context_1の生成元であるwidgetにリビルドがかかり、
 widgetが持つcontextがcontext_2になる
7,8.ユーザ登録処理に失敗
9.エラーダイアログ表示

最後に表示されるダイアログがPOPする際に使用するcontextが【context_1】の場合、presentationではそんなcontextのwidgetはどこにもな!と言われのPOP時にエラーが発生してしまう。

image.png

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?