Flutter. Переход между окнами (Navigator).

Предисловие.

В этой статье вы узнаете, как переключаться между несколькими окнами в Flutter.

Переход между окнами.

Скопируйте данный код себе в main.dart:

Исходный код

void main() {
  runApp(
    MaterialApp(
      home: NewWidget(),
    ),
  );
}

class NewWidget extends StatelessWidget {
  const NewWidget({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Page 1"),
      ),
      body: Container(
        color: Colors.greenAccent,
        child: Center(
          child: TextButton(
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) {
                    return NewWidget2();
                  },
                ),
              );
            },
            child: Text("Next page"),
          ),
        ),
      ),
    );
  }
}

class NewWidget2 extends StatelessWidget {
  const NewWidget2({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Page 2"),
      ),
      body: Container(
        color: Colors.yellowAccent,
        child: Center(
          child: TextButton(
            onPressed: () {
              Navigator.pop(context);
            },
            child: Text("Prev page"),
          ),
        ),
      ),
    );
  }
}

Переход на другое окно, делается с помощью Navigator.push().

Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) {
      return NewWidget2();
    },
  ),
);

Данный метод, принимает context и route

Navigator.push(context, route);
  • context — указывает реальное местонахождение виджета в дереве виджетов.
  • route — отвечает, за анимацию перехода на другое окно (виджет), также там указано, на какое окно нужно перейти.

Если вам интересно узнать подробнее про context, то посмотрите видео с официального ютуб канала Flutter. Видео на английском, но там есть русские субтитры.

В качестве route, я использовал MaterialPageRoute (простая анимация перехода), и в builder, вернул виджет нового окна.

 MaterialPageRoute(
    builder: (context) {
      return NewWidget2();
    },
 ),

Во втором окне (NewWidget2), я закрыл текущее окно, с помощью Navigator.pop и вернулся к начальному (NewWidget).

child: TextButton(
  onPressed: () {
    Navigator.pop(context);
  },
  child: Text("Prev page"),
),

Все остальное я разбирал в предыдущих статьях.

Теперь, запустите.

В итоге, получили переход между двумя окнами.

Переход между окнами, с помощью путей.

Замените свой код, на это.

Исходный код


void main() {
  runApp(
    MaterialApp(
      routes: {
        '/': (BuildContext context) {
          return NewWidget();
        },
        '/second': (BuildContext context) {
          return NewWidget2();
        },
      },
    ),
  );
}

class NewWidget extends StatelessWidget {
  const NewWidget({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Page 1"),
      ),
      body: Container(
        color: Colors.greenAccent,
        child: Center(
          child: TextButton(
            onPressed: () {
              Navigator.pushNamed(context, '/second');
            },
            child: Text("Next page"),
          ),
        ),
      ),
    );
  }
}

class NewWidget2 extends StatelessWidget {
  const NewWidget2({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Page 2"),
      ),
      body: Container(
        color: Colors.yellowAccent,
        child: Center(
          child: TextButton(
            onPressed: () {
              Navigator.pop(context);
            },
            child: Text("Prev page"),
          ),
        ),
      ),
    );
  }
}


Чтобы воспользоваться данным способом, нужно прописать пути до виджетов в MaterialApp в параметре routes.

MaterialApp(
  routes: {
    '/': (BuildContext context) {
      return NewWidget();
    },
    '/second': (BuildContext context) {
      return NewWidget2();
    },
  },
),

Следующий код.

  • к начальному путю /, привязываем окно NewWidget. Кстати, путь /, аналогичен аргументу home: NewWidget.
  • к путю /second, привязываем NewWidget2

Обратите внимание, использование ‘/‘ пути и аргумента home, приведет к ошибке.

MaterialApp(
  routes: {
    '/': (BuildContext context) {
      return NewWidget();
    },
    '/second': (BuildContext context) {
      return NewWidget2();
    },
  },
  home: NewWidget()
),

Далее, с помощью Navigator.pushNamed, открываем окно по пути /second.

Navigator.pushNamed(context, '/second');

Результат тот же что и в прошлом варианте.

Передаем значение в окно.

Измените Navigator.pushNamed в первом окне таким образом:

Navigator.pushNamed(context, '/second', arguments: {"someKey": "someValue"});

Данные из аргумента arguments, будут переданы во второе окно.

Чтобы их получить, в методе build второго окна, нужно написать следующее:

@override
Widget build(BuildContext context) {
  final arg = ModalRoute.of(context)!.settings.arguments as Map; //Получаем аргументы с предыдущего окна

Теперь, вы можете вытащить данные из карты arg. Например так:

child: TextButton(
            onPressed: () {
              Navigator.pop(context);
            },
            child: Text("Prev page ${arg['someKey']}"),
          ),

В примере, я изменил текст кнопки во втором окне.

Возвращаем значения из окна.

Подобное может понадобится когда, например, нужно открыть окно с настройками и из него достать данные.

Измените свой код, на это.

Код приложения

class NewWidget extends StatelessWidget {
  const NewWidget({
    Key? key,
  }) : super(key: key);

  _waitForData(BuildContext context) async {
    final result = await Navigator.pushNamed(context, '/second');
    print(result);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Page 1"),
      ),
      body: Container(
        color: Colors.greenAccent,
        child: Center(
          child: TextButton(
            onPressed: () {
              _waitForData(context);
            },
            child: Text("Next page"),
          ),
        ),
      ),
    );
  }
}

class NewWidget2 extends StatelessWidget {
  const NewWidget2({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Page 2"),
      ),
      body: Container(
        color: Colors.yellowAccent,
        child: Center(
          child: TextButton(
            onPressed: () {
              Navigator.pop(context, "Data from page 2");
            },
            child: Text("Prev page"),
          ),
        ),
      ),
    );
  }
}

В первом окне (NewWidget), есть асинхронная, приватная функция _waitForData.

_waitForData(BuildContext context) async {
    final result = await Navigator.pushNamed(context, '/second');
    print(result);
}

Которая, записывает значение из 2-го окна в переменную result.

final result = await Navigator.pushNamed(context, '/second');

И выводит значение result, в консоль.

print(result);

Дальше, метод _waitForData я повесил на кнопку.

onPressed: () {
  _waitForData(context);
},

Во втором окне, при нажатии на кнопку.

onPressed: () {
  Navigator.pop(context, "Data from page 2");
},

Идет возврат на предыдущее окно (Navigator.pop), и возвращаются из него данные «Data from page 2».

Все, разобрали. Запустите приложение. Теперь, при переходе с 2-го окна на 1-е, появится строка в консоли.

Итоговый результат.