Fonctionnement du navigateur demandé avec un contexte qui ne comprend pas un navigateur
j'essaie de démarrer un nouvel écran dans un onTap mais j'obtiens l'erreur suivante:
navigation demandée avec un contexte qui ne comprend pas Navigator.
le code que j'utilise pour naviguer est:
onTap: () { Navigator.of(context).pushNamed('/settings'); },
j'ai mis en place une route dans mon application comme suit:
routes: <String, WidgetBuilder>{
'/settings': (BuildContext context) => new SettingsPage(),
},
j'ai essayé de copier le code en utilisant l'application stock sample. J'ai j'ai regardé la documentation du navigateur et de la Route et je ne vois pas comment le contexte peut être fait pour inclure un navigateur. Le context
utilisé dans l'onTap est référencé à partir du paramètre passé dans la méthode de construction:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
SettingsPage est une classe comme suit:
class SettingsPage extends Navigator {
Widget buildAppBar(BuildContext context) {
return new AppBar(
title: const Text('Settings')
);
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: buildAppBar(context),
);
}
}
3 réponses
j'ai mis en place cet exemple simple pour le routage dans une application de flutter:
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: new MyHomePage(),
routes: <String, WidgetBuilder>{
'/settings': (BuildContext context) => new SettingsPage(),
},
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('TestProject'),
),
body: new Center(
child: new FlatButton(
child: const Text('Go to Settings'),
onPressed: () => Navigator.of(context).pushNamed('/settings')
)
)
);
}
}
class SettingsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('SettingsPage'),
),
body: new Center(
child: new Text('Settings')
)
);
}
}
Note, que la page Settling étend StatelessWidget et non Navigator. Je ne peux pas reproduire votre erreur.
cet exemple vous aide-t-il à construire votre application? Laissez-moi savoir si je peux vous aider avec quoi que ce soit d'autre.
cette erreur n'est pas liée à la destination. Cela se produit parce que vous avez utilisé un context
qui ne contient pas d'instance Navigator
comme parent.
comment créer une instance Navigator alors ?
ceci est habituellement fait en insérant dans votre arbre à widget un MaterialApp
ou WidgetApp
. Bien que vous pouvez le faire manuellement en utilisant Navigator
directement mais moins recommandé. Ensuite, tous les enfants d'un tel widget peut accéder à NavigatorState
en utilisant Navigator.of(context)
.
attendez, j'ai déjà un MaterialApp
/ WidgetApp
!
C'est très probablement le cas. Mais cette erreur peut encore se produire lorsque vous utilisez un context
qui est un parent de MaterialApp
/ WidgetApp
.
cela se produit parce que lorsque vous faites Navigator.of(context)
, cela va commencer à partir du widget associé au context
utilisé. Et puis aller vers le haut dans le widget tree jusqu'à ce qu'il trouve un Navigator
ou il n'y a plus de widget.
dans le premier cas, tout va bien. Dans la seconde, il lance un
Navigator opération demandée avec un contexte qui ne comprennent pas un Navigateur.
alors, comment puis-je le réparer ?
d'abord, reproduisons cette erreur:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Center(
child: RaisedButton(
child: Text("Foo"),
onPressed: () => Navigator.pushNamed(context, "/"),
),
),
);
}
}
cet exemple crée un bouton qui tente d'aller à '/' sur le clic mais lancera à la place une exception.
notez ici que dans le
onPressed: () => Navigator.pushNamed(context, "/"),
nous avons utilisé context
passé à build
de MyApp
.
le problème est, MyApp
est en fait un parent de MaterialApp
. Comme c'est le widget qui instancie MaterialApp
! Par conséquent, MyApp
's BuildContext
n'a pas un MaterialApp
en tant que parent!
Pour résoudre ce problème, nous avons besoin d'utiliser un autre context
.
dans cette situation, la solution la plus simple est d'introduire un nouveau widget comme enfant de MaterialApp
. Puis utilisez le contexte de ce widget pour faire l'appel Navigator
.
il y a plusieurs façons d'y parvenir. Vous pouvez extraire home
dans une classe personnalisée :
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHome()
);
}
}
class MyHome extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
child: Text("Foo"),
onPressed: () => Navigator.pushNamed(context, "/"),
),
);
}
}
Ou vous pouvez utiliser Builder
:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Builder(
builder: (context) => Center(
child: RaisedButton(
child: Text("Foo"),
onPressed: () => Navigator.pushNamed(context, "/"),
),
),
),
);
}
}
une solution complète et éprouvée:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:my-app/view/main-view.dart';
class SplashView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: Builder(
builder: (context) => new _SplashContent(),
),
routes: <String, WidgetBuilder>{
'/main': (BuildContext context) => new MainView()}
);
}
}
class _SplashContent extends StatefulWidget{
@override
_SplashContentState createState() => new _SplashContentState();
}
class _SplashContentState extends State<_SplashContent>
with SingleTickerProviderStateMixin {
var _iconAnimationController;
var _iconAnimation;
startTimeout() async {
var duration = const Duration(seconds: 3);
return new Timer(duration, handleTimeout);
}
void handleTimeout() {
Navigator.pushReplacementNamed(context, "/main");
}
@override
void initState() {
super.initState();
_iconAnimationController = new AnimationController(
vsync: this, duration: new Duration(milliseconds: 2000));
_iconAnimation = new CurvedAnimation(
parent: _iconAnimationController, curve: Curves.easeIn);
_iconAnimation.addListener(() => this.setState(() {}));
_iconAnimationController.forward();
startTimeout();
}
@override
Widget build(BuildContext context) {
return new Center(
child: new Image(
image: new AssetImage("images/logo.png"),
width: _iconAnimation.value * 100,
height: _iconAnimation.value * 100,
)
);
}
}