Отзывчивая веб-разработка и разработка рабочего стола с помощью Flutter

Опубликовано: 2022-03-10
Краткое резюме ↬ Flutter уже произвел настоящий фурор на сцене разработки мобильных приложений. Теперь он распространяется и на более крупные устройства. Вот что вам нужно знать, чтобы быть готовым взять на себя задачу разработки веб-приложений и приложений для настольных компьютеров с использованием этой замечательной кроссплатформенной среды.

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

  1. Текущее состояние немобильной разработки Flutter и то, как вы можете запускать код Flutter в браузере, на настольном или портативном компьютере;
  2. Как создавать адаптивные приложения с помощью Flutter, чтобы вы могли увидеть его мощь — особенно в качестве веб-фреймворка — во всей красе, заканчивая примечанием о маршрутизации на основе URL.

Давайте погрузимся в это!

Что такое флаттер, почему он важен, во что он превратился и куда движется

Flutter — это новейшая платформа разработки приложений Google. Google предполагает, что он будет всеобъемлющим: он позволит выполнять один и тот же код на смартфонах всех марок, планшетах, настольных компьютерах и ноутбуках в виде собственных приложений или веб-страниц.

Это очень амбициозный проект, но до сих пор Google добился невероятного успеха, особенно в двух аспектах: в создании действительно независимой от платформы платформы для нативных приложений для Android и iOS, которая отлично работает и полностью готова к производственному использованию, и в создании впечатляющего фронта. -конечная веб-инфраструктура, которая может совместно использовать 100% кода с совместимым приложением Flutter.

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

Еще после прыжка! Продолжить чтение ниже ↓

Немобильная разработка с Flutter

Немобильная разработка с Flutter была впервые широко освещена на Google I/O 2019. Этот раздел посвящен тому, как заставить ее работать и когда она работает.

Как включить веб- и десктоп-разработку

Чтобы включить веб-разработку, вы должны сначала быть на бета-канале Flutter. Есть два способа добраться до этой точки:

  • Установите Flutter прямо на бета-канале, загрузив соответствующую последнюю бета-версию из архива SDK.
  • Если у вас уже установлен Flutter, переключитесь на бета-канал с помощью $ flutter channel beta , а затем выполните само переключение, обновив свою версию Flutter (которая на самом деле является git pull в папке установки Flutter) с помощью $ flutter upgrade .

После этого вы можете запустить это:

 $ flutter config --enable-web

Поддержка рабочего стола является гораздо более экспериментальной, особенно из-за отсутствия инструментов для Linux и Windows, что делает разработку плагинов особенно серьезной проблемой, а также из-за того, что используемые для этого API-интерфейсы предназначены для использования в качестве проверки концепции, а не для производство. Это отличается от веб-разработки, которая использует проверенный и проверенный компилятор dart2js для выпускных сборок, которые даже не поддерживаются для собственных настольных приложений Windows и Linux.

Примечание . Поддержка macOS немного лучше, чем поддержка Windows и Linux, но все же не так хороша, как поддержка Интернета, и далеко не так хороша, как полная поддержка мобильных платформ.

Чтобы включить поддержку разработки для настольных компьютеров, вам необходимо переключиться на master канал выпуска, выполнив те же действия, которые были описаны ранее для beta -канала. Затем выполните следующее, заменив <OS_NAME> на linux , windows или macos :

 $ flutter config --enable-<OS_NAME>-desktop

На данный момент, если у вас есть проблемы с любым из следующих шагов, которые я буду описывать, потому что инструмент Flutter не делает то, что я говорю, он должен делать, вот некоторые общие шаги по устранению неполадок:

  • Запустите flutter doctor , чтобы проверить наличие проблем. Побочным эффектом этой команды Flutter является то, что она должна загружать все необходимые инструменты, которых у нее нет.
  • Запустите flutter upgrade .
  • Выключите его и снова включите. Старый ответ технической поддержки уровня 1 на перезагрузку компьютера может быть именно тем, что вам нужно, чтобы вы могли насладиться всеми богатствами Flutter.

Запуск и создание веб-приложений Flutter

Веб-поддержка Flutter неплоха, и это отражается на простоте разработки для Интернета.

Запуск этого…

 $ flutter devices

… должен сразу показать запись для чего-то вроде этого:

 Web Server • web-server • web-javascript • Flutter Tools

Кроме того, запуск браузера Chrome должен привести к тому, что Flutter также покажет запись для него. Запуск flutter run в совместимом проекте Flutter (подробнее об этом позже), когда единственным отображаемым «подключенным устройством» является веб-сервер, приведет к тому, что Flutter запустит веб-сервер на localhost:<RANDOM_PORT> , что позволит вам получить доступ к вашему Flutter. веб-приложение из любого браузера.

Если вы установили Chrome, но он не отображается, вам нужно установить переменную среды CHROME_EXECUTABLE на путь к исполняемому файлу Chrome.

Запуск и создание настольных приложений Flutter

После того, как вы включили поддержку рабочего стола Flutter, вы можете запустить приложение Flutter на своей рабочей станции для разработки с помощью flutter run -d <OS_NAME> , заменив <OS_NAME> тем же значением, которое вы использовали при включении поддержки рабочего стола. Вы также можете создавать двоичные файлы в каталоге build с помощью flutter build <OS_NAME> .

Прежде чем вы сможете сделать что-либо из этого, вам нужно иметь каталог, содержащий то, что Flutter нужно создать для вашей платформы. Он будет создан автоматически при создании нового проекта, но вам нужно будет создать его для существующего проекта с помощью flutter create . . Кроме того, API-интерфейсы Linux и Windows нестабильны, поэтому вам может потребоваться повторно сгенерировать их для этих платформ, если приложение перестанет работать после обновления Flutter.

Когда приложение совместимо?

Что я имел в виду, когда упоминал, что приложение Flutter должно быть «совместимым проектом», чтобы оно работало на рабочем столе или в Интернете? Проще говоря, я имею в виду, что он не должен использовать какой-либо плагин, который не имеет реализации для платформы, на которой вы пытаетесь построить.

Чтобы сделать этот момент абсолютно ясным для всех и избежать недопонимания, обратите внимание, что плагин Flutter — это особый пакет Flutter, который содержит специфичный для платформы код, необходимый для предоставления его функций.

Например, вы можете сколько угодно использовать разработанный Google пакет url_launcher (и, возможно, захотите, учитывая, что сеть построена на гиперссылках).

Примером пакета, разработанного Google, использование которого препятствует веб-разработке, является path_provider , который используется для получения пути к локальному хранилищу для сохранения файлов. Это пример пакета, который, между прочим, бесполезен для веб-приложения, поэтому неспособность использовать его на самом деле не является обломом, за исключением того факта, что вам нужно изменить свой код, чтобы это для работы в Интернете, если вы его используете.

Например, вы можете использовать пакет shared_preferences, который использует HTML localStorage в Интернете.

Аналогичные предостережения действительны в отношении настольных платформ: очень немногие плагины совместимы с настольными платформами, и, поскольку это повторяющаяся тема, над ней нужно проделать гораздо больше работы на стороне рабочего стола, чем это действительно необходимо для Flutter для Интернета.

Создание адаптивных макетов во Flutter

Из-за того, что я описал выше, а также для простоты, в оставшейся части этого поста я буду предполагать, что вашей целевой платформой является Интернет, но основные концепции применимы и к настольной разработке.

Поддержка Интернета имеет преимущества и обязанности. Быть в значительной степени вынужденной поддерживать разные размеры экрана может показаться недостатком, но учтите, что запуск приложения в веб-браузерах позволяет очень легко увидеть, как ваше приложение будет выглядеть на экранах разных размеров и соотношений сторон, без необходимости запуска отдельных эмуляторы мобильных устройств.

Теперь поговорим о коде. Как вы можете сделать свое приложение отзывчивым?

Есть две точки зрения, с которых проводится этот анализ:

  1. «Какие виджеты я использую или могу использовать, которые могут или должны адаптироваться к экранам разных размеров?»
  2. «Как я могу получить информацию о размере экрана и как я могу использовать ее при написании кода пользовательского интерфейса?»

На первый вопрос ответим позже. Давайте сначала поговорим о последнем, потому что с ним очень легко справиться, и он лежит в основе проблемы. Есть два способа сделать это:

  1. Один из способов — взять информацию из MediaQueryData корня MediaQuery InheritedWidget , которая должна существовать в дереве виджетов, чтобы приложение Flutter работало (это часть MaterialApp/WidgetsApp/CupertinoApp ), которую вы можете получить, как любой другой InheritedWidget с MediaQuery.of(context) , у которого есть свойство size , которое имеет тип Size и, следовательно, имеет два свойства width и height типа double .
  2. Другой способ — использовать LayoutBuilder , который представляет собой виджет-конструктор (точно так же, как StreamBuilder или FutureBuilder ), который передает функции- builder (вместе с context ) объект BoxConstraints со свойствами minHeight , maxHeight , minWidth и maxWidth .

Вот пример DartPad, использующий MediaQuery для получения ограничений, код для которого следующий:

 import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(context) => MaterialApp( home: MyHomePage() ); } class MyHomePage extends StatelessWidget { @override Widget build(context) => Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Text( "Width: ${MediaQuery.of(context).size.width}", style: Theme.of(context).textTheme.headline4 ), Text( "Height: ${MediaQuery.of(context).size.height}", style: Theme.of(context).textTheme.headline4 ) ] ) ) ); }

А вот один из них, использующий LayoutBuilder для того же самого:

 import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(context) => MaterialApp( home: MyHomePage() ); } class MyHomePage extends StatelessWidget { @override Widget build(context) => Scaffold( body: LayoutBuilder( builder: (context, constraints) => Center( child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Text( "Width: ${constraints.maxWidth}", style: Theme.of(context).textTheme.headline4 ), Text( "Height: ${constraints.maxHeight}", style: Theme.of(context).textTheme.headline4 ) ] ) ) ) ); }

Теперь давайте подумаем, какие виджеты могут адаптироваться к ограничениям.

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

Виджет, который легче всего адаптируется, — это GridView . На самом деле GridView , созданный с помощью конструктора GridView.extent , даже не требует вашего участия, чтобы сделать его отзывчивым, как вы можете видеть в этом очень простом примере:

 import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(context) => MaterialApp( home: MyHomePage() ); } class MyHomePage extends StatelessWidget { final List elements = [ "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "A Million Billion Trillion", "A much, much longer text that will still fit" ]; @override Widget build(context) => Scaffold( body: GridView.extent( maxCrossAxisExtent: 130.0, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, children: elements.map((el) => Card(child: Center(child: Padding(padding: EdgeInsets.all(8.0), child: Text(el))))).toList() ) ); } import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(context) => MaterialApp( home: MyHomePage() ); } class MyHomePage extends StatelessWidget { final List elements = [ "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "A Million Billion Trillion", "A much, much longer text that will still fit" ]; @override Widget build(context) => Scaffold( body: GridView.extent( maxCrossAxisExtent: 130.0, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, children: elements.map((el) => Card(child: Center(child: Padding(padding: EdgeInsets.all(8.0), child: Text(el))))).toList() ) ); }

Вы можете разместить содержимое разных размеров, изменив maxCrossAxisExtent .

Этот пример в основном служил для демонстрации существования конструктора GridView.extent GridView , но гораздо более разумным способом сделать это было бы использование GridView.builder с SliverGridDelegateWithMaxCrossAxisExtent , в данном случае, когда виджеты должны отображаться в сетке. динамически создаются из другой структуры данных, как вы можете видеть в этом примере:

 import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(context) => MaterialApp( home: MyHomePage() ); } class MyHomePage extends StatelessWidget { final List<String> elements = ["Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "A Million Billion Trillion", "A much, much longer text that will still fit"]; @override Widget build(context) => Scaffold( body: GridView.builder( itemCount: elements.length, gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 130.0, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, ), itemBuilder: (context, i) => Card( child: Center( child: Padding( padding: EdgeInsets.all(8.0), child: Text(elements[i]) ) ) ) ) ); }

Примером адаптации GridView к разным экранам является моя личная целевая страница, которая представляет собой очень простое веб-приложение Flutter, состоящее из GridView с кучей Cards , точно так же, как код предыдущего примера, за исключением того, что Cards немного сложнее и крупнее. .

Очень простое изменение, которое можно было бы внести в приложения, предназначенные для телефонов, — заменить Drawer на постоянное меню слева, когда есть место.

Например, у нас может быть ListView виджетов, как показано ниже, который используется для навигации:

 class Menu extends StatelessWidget { @override Widget build(context) => ListView( children: [ FlatButton( onPressed: () {}, child: ListTile( leading: Icon(Icons.looks_one), title: Text("First Link"), ) ), FlatButton( onPressed: () {}, child: ListTile( leading: Icon(Icons.looks_two), title: Text("Second Link"), ) ) ] ); }

На смартфоне это обычное место для использования внутри Drawer (также известного как гамбургер-меню).

Альтернативой этому может быть BottomNavigationBar или TabBar в сочетании с TabBarView , но с обоими нам придется внести больше изменений, чем требуется для ящика, поэтому мы будем придерживаться ящика.

Чтобы показать Drawer , содержащий только Menu , которое мы видели ранее на небольших экранах, вы должны написать код, похожий на следующий фрагмент, проверяя ширину с помощью MediaQuery.of(context) и передавая объект Drawer в Scaffold , только если он меньше некоторого значения ширины, которое мы считаем подходящим для нашего приложения:

 Scaffold( appBar: AppBar(/* ... \*/), drawer: MediaQuery.of(context).size.width < 500 ? Drawer( child: Menu(), ) : null, body: /* ... \*/ )

Теперь давайте подумаем о body Scaffold . В качестве примера основного содержимого нашего приложения мы будем использовать GridView , созданный ранее, который мы храним в отдельном виджете с именем Content , чтобы избежать путаницы:

 class Content extends StatelessWidget { final List elements = ["Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "A Million Billion Trillion", "A much, much longer text that will still fit"]; @override Widget build(context) => GridView.builder( itemCount: elements.length, gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 130.0, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, ), itemBuilder: (context, i) => Card( child: Center( child: Padding( padding: EdgeInsets.all(8.0), child: Text(elements[i]) ) ) ) ); } class Content extends StatelessWidget { final List elements = ["Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "A Million Billion Trillion", "A much, much longer text that will still fit"]; @override Widget build(context) => GridView.builder( itemCount: elements.length, gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 130.0, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, ), itemBuilder: (context, i) => Card( child: Center( child: Padding( padding: EdgeInsets.all(8.0), child: Text(elements[i]) ) ) ) ); }

На больших экранах само тело может быть Row , которая показывает два виджета: Menu , которое ограничено фиксированной шириной, и Content , заполняющее остальную часть экрана.

На небольших экранах все body будет Content .

Мы поместим все в SafeArea и Center , потому что иногда виджеты веб-приложений Flutter, особенно при использовании Row и Column , оказываются за пределами видимой области экрана, и это исправлено с помощью SafeArea и/или Center .

Это означает, что body Scaffold будет следующим:

 SafeArea( child:Center( child: MediaQuery.of(context).size.width < 500 ? Content() : Row( children: [ Container( width: 200.0, child: Menu() ), Container( width: MediaQuery.of(context).size.width-200.0, child: Content() ) ] ) ) )

Вот все это вместе:

(Большой превью)
 import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(context) => MaterialApp( home: HomePage() ); } class HomePage extends StatelessWidget { @override Widget build(context) => Scaffold( appBar: AppBar(title: Text("test")), drawer: MediaQuery.of(context).size.width < 500 ? Drawer( child: Menu(), ) : null, body: SafeArea( child:Center( child: MediaQuery.of(context).size.width < 500 ? Content() : Row( children: [ Container( width: 200.0, child: Menu() ), Container( width: MediaQuery.of(context).size.width-200.0, child: Content() ) ] ) ) ) ); } class Menu extends StatelessWidget { @override Widget build(context) => ListView( children: [ FlatButton( onPressed: () {}, child: ListTile( leading: Icon(Icons.looks_one), title: Text("First Link"), ) ), FlatButton( onPressed: () {}, child: ListTile( leading: Icon(Icons.looks_two), title: Text("Second Link"), ) ) ] ); } class Content extends StatelessWidget { final List<String> elements = ["Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "A Million Billion Trillion", "A much, much longer text that will still fit"]; @override Widget build(context) => GridView.builder( itemCount: elements.length, gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 130.0, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, ), itemBuilder: (context, i) => Card( child: Center( child: Padding( padding: EdgeInsets.all(8.0), child: Text(elements[i]) ) ) ) ); }

Это большая часть того, что вам понадобится в качестве общего введения в адаптивный пользовательский интерфейс во Flutter. Большая часть его применения будет зависеть от конкретного пользовательского интерфейса вашего приложения, и трудно точно определить, что вы можете сделать, чтобы сделать ваше приложение отзывчивым, и вы можете использовать множество подходов в зависимости от ваших предпочтений. Однако теперь давайте посмотрим, как мы можем превратить более полный пример в адаптивное приложение, учитывая общие элементы приложения и потоки пользовательского интерфейса.

В контексте: сделать приложение отзывчивым

Пока у нас есть только экран. Давайте расширим это до двухэкранного приложения с работающей навигацией на основе URL!

Создание адаптивной страницы входа

Скорее всего, ваше приложение имеет страницу входа. Как мы можем сделать это отзывчивым?

Экраны входа в систему на мобильных устройствах обычно очень похожи друг на друга. Доступного места не так много; обычно это просто Column с некоторым Padding вокруг его виджетов, и он содержит TextField для ввода имени пользователя и пароля и кнопку для входа в систему. Таким образом, довольно стандартный (хотя и не работающий, поскольку это потребовало бы, среди прочего , TextEditingController для каждого TextField ) страница входа в мобильное приложение может быть следующей:

 Scaffold( body: Container( padding: const EdgeInsets.symmetric( vertical: 30.0, horizontal: 25.0 ), child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Text("Welcome to the app, please log in"), TextField( decoration: InputDecoration( labelText: "username" ) ), TextField( obscureText: true, decoration: InputDecoration( labelText: "password" ) ), RaisedButton( color: Colors.blue, child: Text("Log in", style: TextStyle(color: Colors.white)), onPressed: () {} ) ] ), ), )

На мобильном устройстве это выглядит нормально, но эти очень широкие TextField начинают раздражать на планшете, не говоря уже о большом экране. Однако мы не можем просто принять решение о фиксированной ширине, потому что телефоны имеют разные размеры экрана, и мы должны сохранять некоторую гибкость.

Например, путем экспериментов мы могли бы обнаружить, что максимальная ширина должна быть 500. Что ж, мы бы установили constraints Container на 500 (в предыдущем примере я использовал Container вместо Padding , потому что знал, куда шел с этим ) и мы готовы идти, не так ли? Не совсем, потому что это приведет к тому, что виджеты входа будут прилипать к левой стороне экрана, что может быть даже хуже, чем растягивание всего. Итак, мы оборачиваем виджет Center , вот так:

 Center( child: Container( constraints: BoxConstraints(maxWidth: 500), padding: const EdgeInsets.symmetric( vertical: 30.0, horizontal: 25.0 ), child: Column(/* ... \*/) ) )

Это уже выглядит нормально, и нам даже не пришлось использовать ни LayoutBuilder , ни MediaQuery.of MediaQuery.of(context).size . Однако давайте сделаем еще один шаг, чтобы это выглядело очень хорошо. На мой взгляд, было бы лучше, если бы часть переднего плана была каким-то образом отделена от фона. Мы можем добиться этого, задав цвет фона тому, что находится за Container , с помощью виджетов ввода и оставив Container переднего плана белым. Чтобы он выглядел немного лучше, давайте не будем растягивать Container до верхней и нижней части экрана на больших устройствах, придадим ему закругленные углы и добавим красивый анимированный переход между двумя макетами.

Для всего этого теперь требуется LayoutBuilder и внешний Container , чтобы установить цвет фона и добавить отступы вокруг Container , а не только по бокам, только на больших экранах. Кроме того, чтобы анимировать изменение величины заполнения, нам просто нужно превратить этот внешний Container в AnimatedContainer , для которого требуется duration анимации, которую мы установим на полсекунды, то есть Duration(milliseconds: 500) в код.

Вот пример адаптивной страницы входа:

(Большой превью)
 class LoginPage extends StatelessWidget { @override Widget build(context) => Scaffold( body: LayoutBuilder( builder: (context, constraints) { return AnimatedContainer( duration: Duration(milliseconds: 500), color: Colors.lightGreen[200], padding: constraints.maxWidth < 500 ? EdgeInsets.zero : EdgeInsets.all(30.0), child: Center( child: Container( padding: EdgeInsets.symmetric( vertical: 30.0, horizontal: 25.0 ), constraints: BoxConstraints( maxWidth: 500, ), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(5.0), ), child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Text("Welcome to the app, please log in"), TextField( decoration: InputDecoration( labelText: "username" ) ), TextField( obscureText: true, decoration: InputDecoration( labelText: "password" ) ), RaisedButton( color: Colors.blue, child: Text("Log in", style: TextStyle(color: Colors.white)), onPressed: () { Navigator.pushReplacement( context, MaterialPageRoute( builder: (context) => HomePage() ) ); } ) ] ), ), ) ); } ) ); }

Как видите, я также изменил RaisedButton onPressed на обратный вызов, который направляет нас на экран с именем HomePage (который может быть, например, представлением, которое мы создали ранее с помощью GridView и меню или ящика). Теперь, однако, именно на этой части навигации мы и сосредоточимся.

Именованные маршруты: сделать навигацию вашего приложения более похожей на правильное веб-приложение

Обычная вещь для веб-приложений — это возможность менять экраны на основе URL-адреса. Например, переход на https://appurl/login должен дать вам что-то отличное от https://appurl/somethingelse . Flutter, по сути, поддерживает именованные маршруты , которые имеют две цели:

  1. В веб-приложении у них есть именно та функция, о которой я упоминал в предыдущем предложении.
  2. В любом приложении они позволяют вам предопределять маршруты для вашего приложения и давать им имена, а затем иметь возможность переходить к ним, просто указав их имя.

Для этого нам нужно изменить конструктор MaterialApp на следующий:

 MaterialApp( initialRoute: "/login", routes: { "/login": (context) => LoginPage(), "/home": (context) => HomePage() } );

И затем мы можем переключиться на другой маршрут, используя Navigator.pushNamed(context, routeName) и Navigator.pushReplacementNamed(context, routeName) вместо Navigator.push(context, route) и Navigator.pushReplacement(context, route) .

Вот что относится к гипотетическому приложению, которое мы создали в оставшейся части этой статьи. На самом деле вы не можете увидеть именованные маршруты в действии в DartPad, поэтому вы должны попробовать это на своей машине с помощью flutter run или проверить пример в действии:

(Большой превью)
 import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(context) => MaterialApp( initialRoute: "/login", routes: { "/login": (context) => LoginPage(), "/home": (context) => HomePage() } ); } class LoginPage extends StatelessWidget { @override Widget build(context) => Scaffold( body: LayoutBuilder( builder: (context, constraints) { return AnimatedContainer( duration: Duration(milliseconds: 500), color: Colors.lightGreen[200], padding: constraints.maxWidth < 500 ? EdgeInsets.zero : const EdgeInsets.all(30.0), child: Center( child: Container( padding: const EdgeInsets.symmetric( vertical: 30.0, horizontal: 25.0 ), constraints: BoxConstraints( maxWidth: 500, ), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(5.0), ), child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Text("Welcome to the app, please log in"), TextField( decoration: InputDecoration( labelText: "username" ) ), TextField( obscureText: true, decoration: InputDecoration( labelText: "password" ) ), RaisedButton( color: Colors.blue, child: Text("Log in", style: TextStyle(color: Colors.white)), onPressed: () { Navigator.pushReplacementNamed( context, "/home" ); } ) ] ), ), ) ); } ) ); } class HomePage extends StatelessWidget { @override Widget build(context) => Scaffold( appBar: AppBar(title: Text("test")), drawer: MediaQuery.of(context).size.width < 500 ? Drawer( child: Menu(), ) : null, body: SafeArea( child:Center( child: MediaQuery.of(context).size.width < 500 ? Content() : Row( children: [ Container( width: 200.0, child: Menu() ), Container( width: MediaQuery.of(context).size.width-200.0, child: Content() ) ] ) ) ) ); } class Menu extends StatelessWidget { @override Widget build(context) => ListView( children: [ FlatButton( onPressed: () {}, child: ListTile( leading: Icon(Icons.looks_one), title: Text("First Link"), ) ), FlatButton( onPressed: () {}, child: ListTile( leading: Icon(Icons.looks_two), title: Text("Second Link"), ) ), FlatButton( onPressed: () {Navigator.pushReplacementNamed( context, "/login");}, child: ListTile( leading: Icon(Icons.exit_to_app), title: Text("Log Out"), ) ) ] ); } class Content extends StatelessWidget { final List<String> elements = ["Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "A Million Billion Trillion", "A much, much longer text that will still fit"]; @override Widget build(context) => GridView.builder( itemCount: elements.length, gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 130.0, crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, ), itemBuilder: (context, i) => Card( child: Center( child: Padding( padding: EdgeInsets.all(8.0), child: Text(elements[i]) ) ) ) ); }

Вперед с вашим приключением Flutter

Это должно дать вам представление о том, что вы можете делать с Flutter на больших экранах, особенно в Интернете. Это прекрасный фреймворк, очень простой в использовании, а его исключительная кросс-платформенная поддержка делает его еще более важным для изучения и начала использования. Итак, начните доверять Flutter и для веб-приложений!

Дополнительные ресурсы

  • «Оболочки рабочего стола», GitHub
    Текущее, всегда актуальное состояние Flutter на рабочем столе
  • «Настольная поддержка Flutter», Flutter
    Информация о полностью поддерживаемых настольных платформах
  • «Веб-поддержка Flutter», Flutter
    Информация о Flutter для Интернета
  • «Все образцы», флаттер
    Кураторский список примеров и приложений Flutter