flutter: the application of global context in navigator and overlay

Introduction: What is context

The concept of ‘everything is a widget’ in flutter. The UI layout of flutter is composed of superimposed widgets one by one, and each widget corresponds to an Element, and the BuildContext interface is implemented inside the Element class. In coding, the context we use points to the specific UI node in the widget tree.

Usage scenarios of context in the project

In flutter programming, we often use context to achieve the following functions:

1. navigator navigation to jump to the page.

Navigator.of(context).push(MaterialPageRoute(builder: (context) => PageA(),),)

2. Pop-up Dialog

showDialog(context: context, builder: (context) {return Dialog(child: Page(),);})

3. Get theme configuration

final themeData = Theme.of(context);

4. Add Overlay layer (commonly used for custom loading, toast and other capability support)

Overlay.of(context)?.insert(? extends OverlayEntry);

How to maintain a global context

Use the navigatorKey attribute of the MaterialApp node to maintain a global BuildContext. Where you need to use context, you can directly use the globally maintained context for page jump, showLoading, toast and other functions.

Create a tool class NavigatorProvider that maintains GlobalContext (can be used directly by copy)

import 'package:flutter/material.dart';

/// Used to provide a global navigatorContext
class NavigatorProvider {final GlobalKey<NavigatorState> _navigatorKey = new GlobalKey<NavigatorState>(debugLabel: 'Rex'); static final NavigatorProvider _instance = NavigatorProvider._();NavigatorProvider._();///Assign to the materialApp of the root layout /// navigatorKey.currentState.pushName('url') can be directly used to jump static GlobalKey<NavigatorState> get navigatorKey => _instance._navigatorKey;/// can be used to jump, overlay-insert (toast, loading) use static BuildContext? get navigatorContext => _instance._navigatorKey.currentState?.context;
}

Application registration navigatorKey on the root node of MaterialApp

void main() {runApp(MaterialApp( //Why there are two layers of MaterialApp nested here, we explain below home: MaterialApp(navigatorKey: NavigatorProvider.navigatorKey,routes: {'/pageA': (BuildContext context) => PageA(),},home: Scaffold(body: TestNavigatorWidget(),),),),),);
}

TestNavigatorWidget is a test page written by Xiaobian, which contains two function buttons (1. Jump page. 2. toast )

class TestNavigatorWidget extends StatelessWidget {const TestNavigatorWidget({Key? key}) : super(key: key);///jump page void _jumpPageA() {NavigatorProvider.navigatorKey.currentState?.pushNamed('/pageA') ;}///toastvoid _overLayToast() {final globalContext = NavigatorProvider.navigatorContext;if (globalContext != null) {ToastUtils.toast('toast on the home page', globalContext);}}@overrideWidget build(BuildContext context) {return MaterialApp (home: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [TextButton(onPressed: _overLayToast,child: Text('toast'),),TextButton(onPressed: _jumpPageA,child: Text('jump to the next page'),),],),),);}
}
class PageA extends StatelessWidget {const PageA({Key? key}) : super(key: key);///toastvoid _overLayToast() {final globalContext = NavigatorProvider.navigatorContext;if (globalContext != null) {ToastUtils. toast('PageA's toast', globalContext);}}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('PageA')),body: Center(child: TextButton(onPressed: _overLayToast, child: Text('PageA toast'),),),),);}
}

The renderings are as follows:

Code Analysis

  • 1. You can directly use the global context to jump to the page NavigatorProvider.navigatorKey.currentState?.pushNamed('/pageA')
  • 2. ToastUtils is a toast tool class encapsulated by the editor, with the help of the three-party library fluttertoast: ^8.0.9 using the principle of adding overlay layers . The context used in the tool class is the global context provided by the NavigatorProvider registered in the root node.
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';

class ToastUtils {late FToast _fToast; static ToastUtils _instance = ToastUtils._();ToastUtils._() {_fToast = FToast();}static void toast(String message, BuildContext context) {_instance._fToast.init(context); _instance._fToast.showToast(child: _ToastEntry(message),gravity: ToastGravity.BOTTOM,toastDuration: Duration(seconds: 2),);}

///UI used by toast
class _ToastEntry extends StatelessWidget {final String message;const _ToastEntry(this.message, {Key? key}) : super(key: key);@overrideWidget build(BuildContext context) {return Container(padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0,), decoration: BoxDecoration(borderRadius: BorderRadius.circular(25.0), color: Colors.greenAccent,), child: Row(mainAxisSize: MainAxisSize.min, children: [Icon(Icons.check), SizedBox (width: 12.0),Text(message),],),),);}
}

Students who use this code don’t forget to add the tripartite library dependencies in the pubspec.yaml file~

fluttertoast: ^8.0.9
  • 3. Careful students have discovered that in the main method, the root node of the app uses two layers of MaterialApp for nesting, which is caused by the added feature of Overlay.

The insert operation of the overlay will eventually be converted into a Stack layout, but in fact the layer added by the insert is operated on the parent node of the node corresponding to the provided context.

Because of this feature, if we want to use a global context to operate the overlay, then this global context needs to have a parent node.

If you just use the global context for navigation operations (jump, dialog), you don’t need to use two layers of MaterialApp for nesting.

PS: If you don’t want to use two layers of MaterialApp, we can also change the code into this, just ensure that the upper layer of the key has a parent node:

void main() {runApp(MaterialApp(home: MaterialApp(routes: {'/pageA': (BuildContext context) => PageA(),},home: Scaffold(key: NavigatorProvider.navigatorKeybody: TestNavigatorWidget(), ),),),),););
}
abstract class NavigatorProvider {static final GlobalKey _globalKey = GlobalKey();static GlobalKey get globalKey => _globalKey;static BuildContext? get context => _globalKey.currentContext;static NavigatorState get navigator =>Navigator.of(_globalKey.currentState!. context);
}

Last

Organized a set of “Interview Collection of Front-end Manufacturers”, including HTML, CSS, JavaScript, HTTP, TCP protocol, browser, VUE, React, data structure and algorithm, a total of 201 interview questions, and made an answer for each question Answer and analyze.

For friends in need, you can click the card at the end of the article to receive this document and share it for free

Part of the documentation shows:



The length of the article is limited, and the following content will not be displayed one by one

For friends in need, you can click the card below to get it for free