Начинаем работать с Flutter

Изучаем фреймворк Flutter и создаем кроссплатформенное приложение под iOS и Android, используя единый исходный код, сформированный в редакторе VS Code.

Скачать файлы проекта

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

За последнее десятилетие было выпущено несколько средств кроссплатформенной разработки: компания Adobe представила веб-сервис PhoneGap, Microsoft – фреймворк Xamarin, Facebook – пакет React Native. У каждого из этих решений есть свои преимущества и недостатки, и они используются в мобильной индустрии с разной степенью успешности.

Самая свежая на сегодняшний день кроссплатформенная среда разработки – фреймворк Flutter, созданный компанией Google. Этот пакет обеспечивает ускорение процесса разработки, быстрый рендеринг графики, уникальный дизайн интерфейса и нативную скорость работы на двух наиболее популярных мобильных платформах.

Введение в концепцию Flutter

Приложения, создаваемые с помощью фреймворка Flutter. используют язык программирования Dart, созданный и поддерживаемый корпорацией Google. Dart отвечает стандартам ECMA, во многом похож на языки Kotlin и Swift, а также может быть скомпилирован в JavaScript- код.

Будучи кроссплатформенным фреймворком, Flutter имеет очень много общего со средой разработки React Native и позволяет использовать реактивный и декларативный стиль программирования. Однако в отличие от React Native, Flutter не нуждается в мосте для взаимодействия с Javascript, что ускоряет запуск приложений и увеличивает их производительность. Язык программирования Dart вместо Javascript-моста использует технологию AOT – компиляцию перед исполнением.

Другая уникальная черта языка программирования Dart – использование JIT-компиляции, в ходе которой промежуточный код компилируется напрямую в бинарный во время его выполнения. Эта концепция позволяет выполнять горячую перезагрузку для мгновенного обновления интерфейса – без необходимости создания новой сборки.

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

Помимо разработки кроссплатформенных приложений для iOS и Android, изучение фреймворка Flutter позволит вам изучить особенности Fuchsia – экспериментальной операционной системы, разрабатываемой корпорацией Google.

В этом руководстве мы рассмотрим создание приложения, которое запрашивает имена участников платформы через API портала GitHub и отображает их в прокручиваемом списке.

Введение в концепцию Flutter

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

  • настраивать среду разработки;
  • создавать новые проекты;
  • выполнять горячую перезагрузку;
  • импортировать файлы и пакеты;
  • использовать готовые виджеты и создавать собственные;
  • выполнять сетевые вызовы;
  • отображать элементы в списке;
  • добавлять тему оформления приложения.

Также в ходе реализации проекта вы познакомитесь с основами языка программирования Dart.

Приступаем к работе

Фреймворк Flutter устанавливается на Windows, macOS и Linux. Писать код для Flutter можно в любом редакторе, однако специальные плагины для сред разработки IntelliJ IDEA, Android Studio и Visual Studio Code значительно упрощают работу. Мы воспользуемся редактором VS Code.

Настройка среды разработки

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

  1. Скачайте установочный пакет, соответствующий вашей операционной системе.
  2. Извлеките установочные файлы в нужную папку.
  3. Добавьте путь к директории Flutter в переменную среды PATH.
  4. Выполните команду flutter doctor, которая устанавливает фреймворк, интерпретатор Dart, а также уведомляет об отсутствии необходимых системных компонентов.
  5. Установите отсутствующие компоненты.
  6. Добавьте в свою интегрированную среду разработки плагин или расширение для Flutter.
  7. Проверьте работоспособность среды разработки запуском тестового приложения.

Инструкция на официальном сайте Flutter очень подробная и позволяет с легкостью установить этот фреймворк на любую платформу. Это руководство предполагает, что вы установили плагин для Flutter в редактор VS Code, а также все недостающие системные компоненты, указанные flutter doctor.

Если вы используете среду разработки Android Studio, у вас не должно возникнуть затруднений при прохождении этого руководства. Вам также понадобится запустить симулятор iOS, или эмулятор Android, либо использовать мобильное устройство с операционной системой iOS или Android.

Примечание: для создания сборки и запуска приложения на iOS-устройстве или на iOS-симуляторе вам потребуется операционная система macOS с установленной средой Xcode.

Создание нового проекта

В редакторе VS Code с установленным расширением Flutter откройте командную палитру, выбрав «Обзор» - «Командная палитра», или использовав комбинацию клавиш Cmd-Shift-P на macOS, или Ctrl-Shift-P на Windows и Linux. Наберите в строке Flutter: New Project и нажмите Enter.

Создание нового проекта

В качестве названия проекта введите ghflutter и нажмите Enter. Выберите директорию для сохранения проекта, и подождите, пока Flutter завершит создание рабочих файлов в VS Code. Когда все будет готово, файл main.dartоткроется в окне редактора.

Создание нового проекта - 2

В редакторе VS Code в левой части экрана отображается структура вашего проекта. Там есть папки для файлов iOS и Android, а также директория lib, в которой содержится файл main.dart с кодом, предназначенным для обеих платформ. В этом руководстве мы будем работать только с папкой lib.

Замените код в файле main.dart на приведенный ниже:

import 'package:flutter/material.dart';
void main() => runApp(GHFlutterApp());
class GHFlutterApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'GHFlutter',
      home: Scaffold(
        appBar: AppBar(
          title: Text('GHFlutter'),
        ),
        body: Center(
          child: Text('GHFlutter'),
        ),
      ),
    );
  }
}

Функция main() в начале программы использует оператор => для запуска приложения GHFlutterApp. Так же называется класс GHFlutterApp, используемый программой.

Из приведенного выше кода видно, что приложение является виджетом StatelessWidget – без сохранения состояния. Почти все элементы в приложениях, созданных с помощью фреймворка Flutter, представляют собой виджеты и относятся к двум типам – с сохранением состояния и без сохранения состояния. Для создания виджета нашего приложения мы переопределяем метод build(). Мы используем виджет MaterialApp, содержащий набор компонентов, используемых для оформления интерфейса приложений в стиле Material Design.

В нашем проекте тестовый файл widget_test.dart, расположенный в папке test, не потребуется – кликните по нему правой кнопкой мыши, выберите команду «Удалить» из контекстного меню и подтвердите удаление.

Если вы работаете в macOS, запустите iOS-симулятор. Также на macOS, Linux и Windows можно использовать эмулятор Android. Можно запустить и симулятор, и эмулятор сразу и переключаться между ними, используя меню, доступное в правом нижнем углу окна редактора VS Code.

Для сборки и запуска проекта потребуется настройка конфигурации запуска. Переключитесь в режим отладки, нажав на иконку с изображением перечеркнутого жука, доступную на левой панели.

Обратите внимание на то, что конфигурация не определена. Нажмите на «Нет конфигурации» в выберите в выпадающем меню «Добавить конфигурацию».

При этом редактор VS Code создаст файл launch.json следующего вида:

Примечание: этот файл создается автоматически после нажатия кнопки «Добавить конфигурацию». В этом руководстве вносить изменения в код этого файла не требуется.

Теперь все готово – запустите проект нажатием клавиши F5 на клавиатуре или выбором пункта «Запуск отладки» в меню «Отладка», или нажатием зеленой кнопки «Пуск». После этого откроется консоль отладки, либо, при использовании iOS, запустится среда Xcode. В случае использования Android запустится Gradle.

При использовании симулятора iOS приложение будет выглядеть следующим образом:

А в случае использования эмулятора Android окно выглядит, так как показано ниже:

Ярлык slow mode («медленный режим») означает, что приложение выполняется в режиме отладки.

Выполнение приложения можно остановить нажатием кнопки «Стоп», расположенной с правой стороны верхней панели инструментов в окне редактора VS Code.

Вернуться к проекту можно нажатием иконки, расположенной в верхнем левом углу окна редактора VS Code, или выбором опции «Обозреватель» в меню «Обзор».

Горячая перезагрузка

Одно из главных преимуществ работы с фреймворком Flutter – возможность горячей перезагрузки после внесения каких-либо изменений в код. Эта опция напоминает мгновенный запуск с применением внесенных изменений в среде разработки Android Studio.

Запустите сборку и выполнение приложения в симуляторе или эмуляторе.

Горячая перезагрузка

Теперь, не останавливая программу, измените строку AppBar, например, так:

appBar: (
  title: Text('GHFlutter App'),
),

После этого нажмите кнопку горячей перезагрузки на панели инструментов или просто сохраните файл main.dart:

Горячая перезагрузка - 2

В течение 1-2 секунд вы увидите изменения в создаваемом приложении:

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

Импорт файла

Чтобы не хранить весь код в одном файле main.dart, нужно реализовать импорт кода из других пользовательских классов. Рассмотрим пример импорта, который поможет локализовать строки, обращенные к пользователю.

Создайте файл с именем strings.dart в папке lib, кликнув правой кнопкой мыши и выбрав в контекстном меню опцию «Новый файл»:

Импорт файла

Добавьте приведенный ниже класс в только что созданный файл:

class Strings {
  static String appTitle = "GHFlutter";
}

Теперь добавьте следующую строку в начало файла main.dart:

import 'strings.dart';

Внесите изменения в свой виджет для использования нового строкового класса – для этого класс GHFlutterAppдолжен выглядеть следующим образом:

class GHFlutterApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: Strings.appTitle,
      home: Scaffold(
        appBar: AppBar(
          title: Text(Strings.appTitle),
        ),
        body: Center(
          child: Text(Strings.appTitle),
        ),
      ),
    );
  }
}

Запустите приложение нажатием клавиши F5 на клавиатуре – внешних изменений не видно, но теперь программа использует строки из файла.

Виджеты

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

Все виджеты делятся на два типа:

  1. Без состояния: виджеты, которые зависят только от собственной конфигурации, например статические изображения.
  2. С сохранением состояния: виджеты, которые должны поддерживать динамическую передачу информации в ходе взаимодействия с объектом состояния State.

Оба типа виджетов обновляются в Flutter- приложениях в процессе рендеринга каждого фрейма. Различие заключается в том, что виджеты с сохранением состояния передают свою конфигурацию объекту State.

Чтобы создать собственный виджет, начните с определения нового класса внизу файла main.dart:

class GHFlutter extends StatefulWidget {
  @override
  createState() => GHFlutterState();
}

Приведенный выше код создает подкласс виджета StatefulWidget и переопределяет метод createState() для создания объекта состояния. Теперь разместите класс GHFlutterState перед GHFlutter:

class GHFlutterState extends State<GHFlutter> {
}

Теперь класс GHFlutterState расширяет объект State параметром GHFlutter.

Главная задача при создании нового виджета – переопределение метода build(), который вызывается при рендеринге компонента на экране. Добавьте переопределение build() в класс GHFlutterState:

@override
Widget build(BuildContext context) {
}
Заполните метод build(), как показано ниже:
@override
Widget build(BuildContext context) {
  return Scaffold (
    appBar: AppBar(
      title: Text(Strings.appTitle),
    ),
    body: Text(Strings.appTitle),
  );
}

Scaffold – контейнер, содержащий все виджеты стиля Material Design. Данный контейнер выполняет роль корневого каталога в иерархии виджетов. Приведенный выше код добавляет в контейнер панель AppBar и область body – они оба включают в себя виджеты для отображения текста.

Внесите изменения в класс GHFlutterApp для использования вашего собственного виджета GHFlutter вместо стандартного:

class GHFlutterApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: Strings.appTitle,
      home: GHFlutter(),
    );
  }
}

Запустите приложение, чтобы оценить новый виджет в действии.

Виджеты

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

Выполнение сетевых вызовов

Ранее мы импортировали strings.dart в проект. Точно также можно импортировать другие пакеты, входящие в состав фреймворка Flutter и языка программирования Dart. Например, теперь мы будем использовать пакет для сетевого вызова по протоколу HTTP и передачи полученных JSON данных объектам Dart. Добавьте два новых импорта в начале файла main.dart:

import 'dart:convert';
import 'package:http/http.dart' as http;

После этого вы получите сообщение о том, что пакет для HTTP вызова отсутствует – потому что он еще не добавлен в проект. Для добавления откройте файл pubspec.yaml и сразу под строкой cupertino_icons: ^0.1.2 добавьте строки, приведенные ниже:

  cupertino_icons: ^0.1.2
  # HTTP package
  http: ^0.12.0+2

Примечание: обратите внимание на отступы – они должны быть такими же, как и перед строкой cupertino_icons.

После сохранения файла pubspec.yaml расширение Flutter, установленное в редакторе VS Code, запустит команду flutter pub get, после чего пакет для работы с HTTP будет добавлен и во Flutter, и в наш проект main.dart. Вы увидите индикатор импорта в main.dart, который до этого был неактивным.

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

Мы собираемся реализовать асинхронный сетевой вызов для получения списка участников платформы GitHub. Добавьте пустой список в качестве свойства в верхнюю часть GHFlutterState. Также добавьте свойство для сохранения стиля текста:

var _members = [];
final _biggerFont = const TextStyle(fontSize: 18.0);

Подчеркивания в начале имен делают классы закрытыми. Для выполнения асинхронного HTTP-вызова, добавьте метод _loadData() в GHFlutterState:

_loadData() async {
  String dataURL = "https://api.github.com/orgs/raywenderlich/members";
  http.Response response = await http.get(dataURL);
  setState(() {
    _members = json.decode(response.body);
  });
}

Мы добавили ключ async в метод _loadData(), чтобы указать Dart на асинхронность; ключ await в http.get() выполняет блокирующую функцию. Значение dataUrl указывает на конечную точку API портала GitHub для извлечения списка участников платформы.

Когда HTTP вызов завершается, обратный вызов передается функции setState(), которая выполняется синхронно в потоке пользовательского интерфейса. В этом случае, данные JSON преобразуются в список _members.

Добавьте переопределение initState() в GHFlutterState, которое вызывает _loadData() при инициализации состояния:

@override
void initState() {
  super.initState();

  _loadData();
}

Отображение элементов списка

После того, как Dart получил список участников, нам нужен способ его отображения в пользовательском интерфейсе. В Dart входит виджет ListView, который отображает пользовательские данные в виде списка. Функциональность этого виджета аналогична RecyclerView в Android и UITableView в iOS – он точно так же обеспечивает плавную прокрутку списка.

Добавьте метод _buildRow() в GHFlutterState:

Widget _buildRow(int i) {
  return ListTile(
    title: Text("${_members[i]["login"]}", style: _biggerFont)
  );
}

Код, приведенный выше, возвращает содержимое виджета ListTile – логины участников платформы, извлеченные из JSON данных, и оформленные с применением текстового стиля, который мы создали ранее.

Отредактируйте метод build в GHFlutterState для использования в формировании списка:

body: ListView.builder(
  padding: const EdgeInsets.all(16.0),
  itemCount: _members.length,
  itemBuilder: (BuildContext context, int position) {
    return _buildRow(position);
  }),

Приведенный выше код устанавливает межстрочные интервалы, количество участников itemCount и позицию вывода списка _buildRow(). Можно попробовать горячую перезагрузку для обновления экрана приложения, но скорее всего вы получите сообщение о том, что требуется полный перезапуск. В этом случае нажмите клавишу F5 на клавиатуре для сборки и запуска программы:

Отображение элементов списка

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

Отображение элементов списка - 2

Добавление разделителей

Для добавления разделителей в список нам нужно удвоить количество позиций, а затем вставить виджет Divider в каждую нечетную позицию. Отредактируйте метод build в GHFlutterState следующим образом:

body: ListView.builder(
  itemCount: _members.length * 2,
  itemBuilder: (BuildContext context, int position) {
    if (position.isOdd) return Divider();

    final index = position ~/ 2;
    
    return _buildRow(index);
  }),

Убедитесь, что добавили * 2 в количество позиций itemCount. При использовании разделителей определение межстрочных интервалов не требуется. В itemBuilder вы либо используете виджет-разделитель Divider (), либо вместо этого вычисляете новый индекс путем целочисленного деления и используете _buildRow() для создания элемента строки.

Используйте «горячую перезагрузку» и убедитесь, что теперь в списке есть разделители.

Добавление разделителей

Для добавления межстрочных интервалов к каждой строке используйте виджет Padding внутри _buildRow():

Widget _buildRow(int i) {
  return Padding(
    padding: const EdgeInsets.all(16.0),
    child: ListTile(
      title: Text("${_members[i]["login"]}", style: _biggerFont)
    )
  );
}

Теперь ListTile является дочерним виджетом родительского виджета Padding. Выполните горячую перезагрузку – после этого интервалы будут применены к строкам списка, но не к разделителям.

Использование пользовательских классов

На предыдущем этапе мы использовали обработчик данных, который извлекал логины участников платформы Github из документа JSON и вносил имена в список _members в качестве класса Map («схема»), аналогичного типу Map в Kotlin или Dictionary («словарю») в Swift.

Теперь мы будем использовать наш собственный тип данных. Добавьте новый класс Member («участник») в файл main.dart:

class Member {
  final String login;

  Member(this.login) {
    if (login == null) {
      throw ArgumentError("Логин или участник не могут быть пустыми. "
          "Received: '$login'");
    }
  }
}

Класс Member имеет свойство login и конструктор, который выводит сообщение об ошибке, если значение loginоказывается незаполненным.

Отредактируйте объявление _members в GHFlutterState для преобразования в список объектов класса Member:

var _members = <Member>[];

Обновите _buildRow() для использования свойства login вместо использования ключа login в схеме JSON данных:

title: Text("${_members[i].login}", style: _biggerFont)

Теперь отредактируйте функцию обратного вызова setState() в _loadData() для преобразования схемы данных в объект Member и добавления в список участников GitHub:

setState(() {
  final membersJSON = json.decode(response.body);

  for (var memberJSON in membersJSON) {
    final member = Member(memberJSON["login"]);
    _members.add(member);
  }
});

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

Загрузка изображений с использованием NetworkImage

Каждый участник платформы GitHub имеет аватар, а каждый аватар имеет свой URL-адрес. Теперь мы добавим ссылки на аватары в класс Member, чтобы изображения участников отображались в нашем приложении.

Отредактируйте класс Member и добавьте свойство avatarUrl с предупреждением, что оно не может быть незаполненным:

class Member {
  final String login;
  final String avatarUrl;

  Member(this.login, this.avatarUrl) {
    if (login == null) {
      throw ArgumentError("Логин или участник не могут быть пустыми. "
          "Received: '$login'");
    }
    if (avatarUrl == null) {
      throw ArgumentError("Ссылка на аватар не может быть пустой. "
          "Received: '$avatarUrl'");
    }
  }
}

Обновите _buildRow() для выведения аватара с помощью NetworkImage и виджета CircleAvatar:

Widget _buildRow(int i) {
  return Padding(
    padding: const EdgeInsets.all(16.0),
    child: ListTile(
      title: Text("${_members[i].login}", style: _biggerFont),
      leading: CircleAvatar(
        backgroundColor: Colors.green,
        backgroundImage: NetworkImage(_members[i].avatarUrl)
      ),
    )
  );
}

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

Теперь обновите _loadData() для использования значения avatar_url в схеме во время создания записи для нового участника:

final member = Member(memberJSON["login"], memberJSON["avatar_url"]);

Остановите и перезапустите приложение нажатием клавиши F5 на клавиатуре. После этого вы увидите аватары рядом с каждым участником, представленным в списке:

Загрузка изображений с использованием NetworkImage

Оптимизация кода

Большая часть нашего кода расположена в файле main.dart. Чтобы сделать код немного опрятнее, можно провести рефакторинг виджета и переместить созданные нами классы в отдельные файлы.

Создайте файлы member.dart и ghflutter.dart в папке lib. Сохраните код класса Member в файле member.dart, а код классов GHFlutterState и GHFlutter – в файле ghflutter.dart. Файл member.dart не требует добавления команд импорта, однако импорт в ghflutter.dart должен быть определен следующим образом:

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'member.dart';
import 'strings.dart'; 

Также необходимо обновить команды импорта в файле main.dart, так как показано ниже:

import 'package:flutter/material.dart';
import 'ghflutter.dart';
import 'strings.dart';
void main() => runApp(GHFlutterApp());
class GHFlutterApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: Strings.appTitle,
      home: GHFlutter(),
    );
  }
}  

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

Добавление темы оформления

Изменить тему оформления в создаваемом приложении можно добавлением атрибута theme в MaterialApp, которую мы создали в файле main.dart:

class GHFlutterApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: Strings.appTitle,
      theme: ThemeData(primaryColor: Colors.green.shade800), 
      home: GHFlutter(),
    );
  }
}  

Теперь в нашем приложении используется тема оформления на основе указанного оттенка зеленого цвета. Перезапустите программу нажатием клавиши F5 на клавиатуре, чтобы оценить новое цветовое оформление.

Добавление темы оформления

Большинство скриншотов, приведенных в этом руководстве, сделано в эмуляторе Android. Запустим приложение в новом оформлении в симуляторе iOS и увидим следующее:

Добавление темы оформления - 2

Вот это и есть настоящая кроссплатформенность!

Что делать дальше

Вы можете скачать все файлы проекта, кликнув по ссылке, приведенной в конце статьи. Файлы должны с одинаковым успехом открываться и в VS Code, и в Android Studio. В VS Code проект открывается из корневого каталога. Загрузить пакеты надо до запуска проекта – для этого откройте командную палитру с помощью комбинации клавиш Cmd+Shift+P и выполните команду Flutter: Get Packages.

Для открытия проекта в Android Studio выберите опцию «Открыть существующий проект» на экране «Добро пожаловать в Android Studio» и нажмите на корневую директорию готового проекта. Затем вам надо выбрать пункт «Открыть настройки Flutter» (выбрать Flutter также можно в «Настройках»), и указать путь к среде Flutter SDK, которую необходимо предварительно скачать из репозитория GitHub. Если понадобится, выберите пункт «Установить необходимые компоненты» в строке «Загрузка пакетов не запускалась». Выберите «Обзор проекта» на панели для просмотра файлов.

Если вы используйте macOS, запустите приложение в Android Studio, используя либо Android эмулятор, либо iOS симулятор. Попробуйте, это действительно здорово работает.

Вам еще многое предстоит узнать о Flutter и Dart. Лучшие ресурсы для обучения:

  1. Официальная документация Flutter на dev.
  2. Все доступные виджеты есть здесь.
  3. Здесь находится отличная инструкция для Android-разработчиков, переходящих на Flutter.
  4. Такая же инструкция для разработчиков, использующих React Native, расположена здесь.

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

Скачать файлы проекта

Пожалуйста, оставьте ваши отзывы по текущей теме статьи. Мы крайне благодарны вам за ваши комментарии, отклики, лайки, подписки, дизлайки!

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

Наталья Кайдаавтор-переводчик статьи «Getting Started With Flutter»