Операция 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')),
      )),
    );
  }
}