Операция Navigator запрашивается с контекстом, который не включает в себя навигатор
Я пытаюсь запустить новый экран внутри onTap, но я получаю следующую ошибку:
Операция Navigator запрашивается с контекстом, который не включает навигатор.
Код, который я использую для навигации:
onTap: () { Navigator.of(context).pushNamed('/settings'); },
Я установил маршрут в своем приложении следующим образом:
routes: <String, WidgetBuilder>{
'/settings': (BuildContext context) => new SettingsPage(),
},
Я попытался скопировать код, используя приложение образца запасов. Я просмотрел документацию Navigator и Route и не могу понять, как можно использовать контекст для включения Navigator. context
, используемый в onTap, ссылается на параметр, переданный в метод сборки:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
SettingsPage - это класс следующим образом:
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),
);
}
}
Ответы
Ответ 1
TL;DR: оберните виджет, который должен получить доступ к Navigator
в Builder
или извлеките это поддерево в класс. И используйте новый BuildContext
для доступа к Navigator
.
Эта ошибка не связана с местом назначения. Это происходит потому, что вы использовали context
, который не содержит экземпляр Navigator
качестве родителя.
Как мне создать экземпляр Navigator?
Обычно это делается путем вставки в дерево виджета MaterialApp
или WidgetApp
. Хотя вы можете сделать это вручную, используя Navigator
напрямую, но менее рекомендуется. Затем все дочерние элементы такого виджета могут получить доступ к NavigatorState
используя Navigator.of(context)
.
Подождите, у меня уже есть MaterialApp
/WidgetApp
!
Это, скорее всего, случай. Но эта ошибка может WidgetApp
если вы используете context
, являющийся родителем MaterialApp
/WidgetApp
.
Это происходит потому, что когда вы выполняете Navigator.of(context)
, он начинается с виджета, связанного с используемым context
. И затем идите вверх в дереве виджета, пока он не найдет Navigator
или больше не будет виджета.
В первом случае все в порядке. Во втором -
Операция Navigator запрашивается с контекстом, который не включает навигатор.
Итак, как я могу это исправить?
Во-первых, позвольте воспроизвести эту ошибку:
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, "/"),
),
),
);
}
}
В этом примере создается кнопка, которая пытается перейти на "/" при щелчке, но вместо этого выдает исключение.
Обратите внимание, что в
onPressed: () => Navigator.pushNamed(context, "/"),
мы использовали context
переданный для build
MyApp
.
Проблема в том, что MyApp
на самом деле является родителем MaterialApp
. Как это виджет, который создает экземпляр MaterialApp
! Поэтому MyApp
BuildContext
не имеет MaterialApp
качестве родителя!
Чтобы решить эту проблему, нам нужно использовать другой context
.
В этой ситуации самым простым решением является введение нового виджета в качестве дочернего элемента MaterialApp
. И затем используйте этот контекст виджета, чтобы выполнить вызов Navigator
.
Есть несколько способов добиться этого. Вы можете извлечь home
в пользовательский класс:
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, "/"),
),
);
}
}
Или вы можете использовать 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, "/"),
),
),
),
);
}
}
Ответ 2
Я установил этот простой пример для маршрутизации в приложении флаттера:
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')
)
);
}
}
Обратите внимание, что SettingsPage расширяет StatelessWidget, а не Navigator. Я не могу воспроизвести вашу ошибку.
Помогает ли этот пример при создании приложения? Дай мне знать, смогу ли я помочь тебе чем-нибудь еще.
Ответ 3
Полное и проверенное решение:
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,
)
);
}
}
Ответ 4
Привет, ребята, у меня та же проблема. Это происходит для меня. Решение, которое я нашел, очень простое. Только то, что я сделал, в простом коде:
void main() {
runApp(MaterialApp(
home: YOURAPP() ,
),
);
}
Надеюсь было полезно.
Ответ 5
Убедитесь, что ваш текущий родительский виджет не совпадает с уровнем MaterialApp
Неправильный путь
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Title'),
),
body: Center(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
child: RaisedButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => ScanScreen()));
},
child: const Text('SCAN')),
)),
),
);
}
}
Правильный путь
void main() => runApp(MaterialApp(
title: "App",
home: HomeScreen(),
));
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Title'),
),
body: Center(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
child: RaisedButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => ScanScreen()));
},
child: const Text('SCAN')),
)),
);
}
}