Использование Flutter от Google для действительно кроссплатформенной мобильной разработки
Опубликовано: 2022-03-10Flutter — это кроссплатформенная платформа для разработки мобильных приложений с открытым исходным кодом от Google. Он позволяет создавать высокопроизводительные красивые приложения для iOS и Android из единой базы кода. Это также платформа для разработки будущей операционной системы Google Fuchsia. Кроме того, он разработан таким образом, что его можно перенести на другие платформы с помощью пользовательских встроенных модулей Flutter.
Для чего был создан Flutter и почему вы должны его использовать
Кроссплатформенные наборы инструментов исторически использовали один из двух подходов:
- Они оборачивают веб-представление в собственное приложение и создают приложение, как если бы это был веб-сайт.
- Они обертывают собственные элементы управления платформой и обеспечивают некоторую межплатформенную абстракцию над ними.
Flutter использует другой подход, пытаясь улучшить мобильную разработку. Он предоставляет платформу, с которой работают разработчики приложений, и механизм с переносимой средой выполнения для размещения приложений. Фреймворк основан на графической библиотеке Skia, предоставляя виджеты, которые фактически визуализируются, а не являются просто оболочками для собственных элементов управления.
Этот подход дает гибкость для создания кросс-платформенного приложения полностью настраиваемым образом, как это обеспечивает опция веб-оболочки, но в то же время обеспечивает плавную работу. Между тем, богатая библиотека виджетов, которая поставляется с Flutter, наряду с множеством виджетов с открытым исходным кодом, делает его очень многофункциональной платформой для работы. Проще говоря, Flutter — это самое близкое, что у мобильных разработчиков было для кроссплатформенной разработки практически без компромиссов.
Дартс
Приложения Flutter написаны на Dart, языке программирования, первоначально разработанном Google. Dart — это объектно-ориентированный язык, который поддерживает как предварительную, так и своевременную компиляцию, что делает его хорошо подходящим для создания нативных приложений, а также обеспечивает эффективный рабочий процесс разработки с горячей перезагрузкой Flutter. Flutter недавно также перешел на Dart версии 2.0.
Язык Dart предлагает многие функции, которые есть в других языках, включая сборку мусора, асинхронное ожидание, строгую типизацию, дженерики, а также богатую стандартную библиотеку.
Dart предлагает набор функций, которые должны быть знакомы разработчикам, использующим различные языки, такие как C#, JavaScript, F#, Swift и Java. Кроме того, Dart может компилироваться в Javascript. В сочетании с Flutter это позволяет совместно использовать код на веб- и мобильных платформах.
Историческая хронология событий
- апрель 2015 г.
Flutter (первоначально под кодовым названием Sky) показан на саммите разработчиков Dart - ноябрь 2015 г.
Sky переименован во Flutter - февраль 2018
Первая бета-версия Flutter анонсирована на Mobile World Congress 2018 - апрель 2018 г.
Анонсирована бета-версия Flutter 2 - май 2018 г.
Третью бета-версию Flutter анонсировали на Google I/O. Google объявляет, что Flutter готов для производственных приложений
Сравнение с другими платформами разработки
Apple/Android Собственный
Нативные приложения предлагают наименьшие трудности при внедрении новых функций. У них, как правило, пользовательский опыт больше соответствует данной платформе, поскольку приложения создаются с использованием элементов управления от самих поставщиков платформ (Apple или Google) и часто следуют рекомендациям по дизайну, установленным этими поставщиками. В большинстве случаев собственные приложения будут работать лучше, чем приложения, созданные с использованием кроссплатформенных предложений, хотя во многих случаях разница может быть незначительной в зависимости от лежащей в основе кроссплатформенной технологии.
Одним из больших преимуществ нативных приложений является то, что они могут при желании немедленно внедрять новые технологии, которые Apple и Google создают в бета-версии, не дожидаясь интеграции со сторонними поставщиками. Основным недостатком создания нативных приложений является отсутствие повторного использования кода на разных платформах, что может сделать разработку дорогостоящей, если она нацелена на iOS и Android.
Реагировать на родной
React Native позволяет создавать собственные приложения с использованием JavaScript. Фактические элементы управления, используемые приложением, являются собственными элементами управления платформы, поэтому конечный пользователь получает ощущение собственного приложения. Для приложений, которые требуют настройки помимо того, что обеспечивает абстракция React Native, все еще может потребоваться собственная разработка. В тех случаях, когда объем требуемой настройки значителен, преимущества работы на уровне абстракции React Native уменьшаются до такой степени, что в некоторых случаях разработка приложения в исходном виде была бы более выгодной.
Ксамарин
При обсуждении Xamarin необходимо оценить два разных подхода. Для их наиболее кроссплатформенного подхода есть Xamarin.Forms. Хотя эта технология сильно отличается от React Native, концептуально она предлагает аналогичный подход, поскольку абстрагирует собственные элементы управления. Точно так же у него есть аналогичные недостатки в отношении настройки.
Во-вторых, есть то, что многие называют Xamarin-classic. В этом подходе продукты Xamarin для iOS и Android используются независимо друг от друга для создания функций, зависящих от платформы, точно так же, как при непосредственном использовании нативных приложений Apple/Android, только с использованием C# или F# в случае Xamarin. Преимущество Xamarin заключается в том, что код, не зависящий от платформы, такие вещи, как сеть, доступ к данным, веб-службы и т. д., могут быть общими.
В отличие от этих альтернатив, Flutter пытается предоставить разработчикам более полное кросс-платформенное решение с повторным использованием кода, высокопроизводительными гибкими пользовательскими интерфейсами и отличными инструментами.
Обзор приложения Flutter
Создание приложения
После установки Flutter создать приложение с Flutter так же просто, как открыть командную строку и ввести flutter create [app_name]
, выбрать команду «Flutter: New Project» в VS Code или выбрать «Start a new Flutter Project» в Android. Студия или IntelliJ.
Независимо от того, решите ли вы использовать IDE или командную строку вместе с вашим предпочтительным редактором, новый шаблон приложения Flutter дает вам хорошую отправную точку для приложения.
Приложение содержит пакет flutter
/ material.dart
, предлагающий некоторые базовые каркасы для приложения, такие как строка заголовка, значки материалов и темы. Он также устанавливает виджет с отслеживанием состояния, чтобы продемонстрировать, как обновлять пользовательский интерфейс при изменении состояния приложения.

Опции инструментов
Flutter предлагает невероятную гибкость в отношении инструментов. Приложения можно так же легко разрабатывать из командной строки вместе с любым редактором, как и из поддерживаемой IDE, такой как VS Code, Android Studio или IntelliJ. Выбор подхода во многом зависит от предпочтений разработчика.
Android Studio предлагает большинство функций, таких как Flutter Inspector для анализа виджетов запущенного приложения, а также для мониторинга производительности приложения. Он также предлагает несколько рефакторингов, которые удобны при разработке иерархии виджетов.
VS Code предлагает более легкий опыт разработки, поскольку он обычно запускается быстрее, чем Android Studio/IntelliJ. Каждая IDE предлагает встроенные помощники по редактированию, такие как завершение кода, позволяющие исследовать различные API, а также хорошую поддержку отладки.
Командная строка также хорошо поддерживается с помощью команды flutter
, которая упрощает создание, обновление и запуск приложения без каких-либо других зависимостей от инструментов, кроме редактора.

Горячая перезагрузка
Независимо от инструментов, Flutter отлично поддерживает горячую перезагрузку приложения. Это позволяет во многих случаях изменять работающее приложение, сохраняя его состояние, без необходимости останавливать приложение, перестраивать и повторно развертывать.
Горячая перезагрузка значительно повышает эффективность разработки за счет более быстрой итерации. Это действительно делает платформу приятной для работы.
Тестирование
Flutter включает утилиту WidgetTester
для взаимодействия с виджетами из теста. Новый шаблон приложения включает образец теста, чтобы продемонстрировать, как его использовать при разработке теста, как показано ниже:
// Test included with the new Flutter application template import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:myapp/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. await tester.pumpWidget(new MyApp()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget); expect(find.text('1'), findsNothing); // Tap the '+' icon and trigger a frame. await tester.tap(find.byIcon(Icons.add)); await tester.pump(); // Verify that our counter has incremented. expect(find.text('0'), findsNothing); expect(find.text('1'), findsOneWidget); }); }
Использование пакетов и плагинов
Flutter только начинается, но уже есть богатая экосистема разработчиков: для разработчиков уже доступно множество пакетов и плагинов.
Чтобы добавить пакет или подключаемый модуль, просто включите зависимость в файл pubspec.yaml в корневом каталоге приложения. Затем запустите flutter packages get
либо из командной строки, либо через IDE, и инструменты Flutter доставят все необходимые зависимости.
Например, чтобы использовать популярный плагин для выбора изображений для Flutter, в pubspec.yaml нужно только указать его как зависимость, например так:
dependencies: image_picker: "^0.4.1"
Затем запуск flutter packages get
все, что вам нужно для его использования, после чего его можно импортировать и использовать в Dart:
import 'package:image_picker/image_picker.dart';
Виджеты
Все во Flutter — это виджеты. Это включает в себя элементы пользовательского интерфейса, такие как ListView
, TextBox
и Image
, а также другие части платформы, включая макет, анимацию, распознавание жестов и темы, и это лишь некоторые из них.
Поскольку все является виджетом, все приложение, которое, кстати, также является виджетом, может быть представлено в иерархии виджетов. Наличие архитектуры, в которой все является виджетом, дает понять, откуда берутся определенные атрибуты и поведение, применяемые к части приложения. Это отличается от большинства других платформ приложений, которые непоследовательно связывают свойства и поведение, иногда присоединяя их к другим компонентам в иерархии, а иногда к самому элементу управления.
Пример простого виджета пользовательского интерфейса
Точка входа в приложение Flutter является основной функцией. Чтобы поместить виджет для элемента пользовательского интерфейса на экран, в main()
вызовите runApp()
и передайте ему виджет, который будет служить корнем иерархии виджетов.
import 'package:flutter/material.dart'; void main() { runApp( Container(color: Colors.lightBlue) ); }
В результате появляется светло-голубой виджет Container
, который заполняет весь экран:


Виджеты без состояния и виджеты с состоянием
Виджеты бывают двух видов: без сохранения состояния и с сохранением состояния. Виджеты без состояния не меняют своего содержимого после создания и инициализации, в то время как виджеты с состоянием сохраняют некоторое состояние, которое может измениться во время работы приложения, например, в ответ на действия пользователя.
В этом примере на экран выводятся виджет FlatButton
и виджет Text
. Виджет Text
начинается с некоторой String
по умолчанию для своего состояния. Нажатие кнопки приводит к изменению состояния, которое приводит к обновлению Text
виджета и отображению новой String
.
Чтобы инкапсулировать виджет, создайте класс, производный от StatelessWidget
или StatefulWidget
. Например, светло-голубой Container
можно записать следующим образом:
class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Container(color: Colors.lightBlue); } }
Flutter вызовет метод сборки виджета, когда он будет вставлен в дерево виджетов, чтобы можно было отобразить эту часть пользовательского интерфейса.
Для виджета с отслеживанием состояния наследуйте от StatefulWidget
:
class MyStatefulWidget extends StatefulWidget { MyStatefulWidget(); @override State createState() { return MyWidgetState(); } }
class MyStatefulWidget extends StatefulWidget { MyStatefulWidget(); @override State createState() { return MyWidgetState(); } }
Виджеты с состоянием возвращают класс State
, который отвечает за построение дерева виджетов для заданного состояния. При изменении состояния соответствующая часть дерева виджетов перестраивается.
В следующем коде класс State
обновляет String
при нажатии кнопки:
class MyWidgetState extends State { String text = "some text"; @override Widget build(BuildContext context) { return Container( color: Colors.lightBlue, child: Padding( padding: const EdgeInsets.all(50.0), child: Directionality( textDirection: TextDirection.ltr, child: Column( children: [ FlatButton( child: Text('Set State'), onPressed: () { setState(() { text = "some new text"; }); }, ), Text( text, style: TextStyle(fontSize: 20.0)), ], ) ) ) ); } }
class MyWidgetState extends State { String text = "some text"; @override Widget build(BuildContext context) { return Container( color: Colors.lightBlue, child: Padding( padding: const EdgeInsets.all(50.0), child: Directionality( textDirection: TextDirection.ltr, child: Column( children: [ FlatButton( child: Text('Set State'), onPressed: () { setState(() { text = "some new text"; }); }, ), Text( text, style: TextStyle(fontSize: 20.0)), ], ) ) ) ); } }
Состояние обновляется в функции, которая передается в setState()
. При setState()
эта функция может установить любое внутреннее состояние, например строку в этом примере. Затем будет вызван метод build
, обновляющий дерево виджетов с отслеживанием состояния.

Также обратите внимание на использование виджета « Directionality
» для установки направления текста для любых виджетов в его поддереве, которым оно требуется, например виджетов « Text
». Примеры здесь строят код с нуля, поэтому Directionality
необходима где-то вверх по иерархии виджетов. Однако использование виджета MaterialApp
, например, с шаблоном приложения по умолчанию, неявно устанавливает направление текста.
Макет
Функция runApp
по умолчанию расширяет виджет, чтобы он заполнил экран. Для управления макетом виджета Flutter предлагает множество виджетов макета. Существуют виджеты для выполнения макетов, которые выравнивают дочерние виджеты по вертикали или горизонтали, расширяют виджеты, чтобы заполнить определенное пространство, ограничивают виджеты определенной областью, центрируют их на экране и позволяют виджетам перекрывать друг друга.
Двумя наиболее часто используемыми виджетами являются Row
и Column
. Эти виджеты выполняют макеты для отображения своих дочерних виджетов горизонтально (строка) или вертикально (столбец).
Использование этих виджетов макета просто включает в себя их обертывание вокруг списка дочерних виджетов. mainAxisAlignment
управляет тем, как виджеты располагаются вдоль оси макета: по центру, в начале, в конце или с различными вариантами интервалов.
В следующем коде показано, как выровнять несколько дочерних виджетов в Row
или Column
:
class MyStatelessWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Row( //change to Column for vertical layout mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.android, size: 30.0), Icon(Icons.pets, size: 10.0), Icon(Icons.stars, size: 75.0), Icon(Icons.rowing, size: 25.0), ], ); } }

Реакция на прикосновение
Сенсорное взаимодействие обрабатывается с помощью жестов, инкапсулированных в классе GestureDetector
. Поскольку это также виджет, добавить распознавание жестов так же просто, как обернуть дочерние виджеты в GestureDetector
.
Например, чтобы добавить обработку касания к Icon
, сделайте его дочерним элементом GestureDetector
и установите обработчики детектора для захвата желаемых жестов.
class MyStatelessWidget extends StatelessWidget { @override Widget build(BuildContext context) { return GestureDetector( onTap: () => print('you tapped the star'), onDoubleTap: () => print('you double tapped the star'), onLongPress: () => print('you long pressed the star'), child: Icon(Icons.stars, size: 200.0), ); } }
В этом случае при касании, двойном касании или длительном нажатии на значок печатается соответствующий текст:
To hot reload your app on the fly, press "r". To restart the app entirely, press "R". An Observatory debugger and profiler on iPhone X is available at: https://127.0.0.1:8100/ For a more detailed help message, press "h". To quit, press "q". flutter: you tapped the star flutter: you double tapped the star flutter: you long pressed the star
В дополнение к простым жестам касания существует множество распознавателей для всего, от панорамирования и масштабирования до перетаскивания. Это позволяет очень легко создавать интерактивные приложения.
Картина
Flutter также предлагает множество виджетов для рисования, в том числе те, которые изменяют непрозрачность, устанавливают обтравочные контуры и применяют украшения. Он даже поддерживает пользовательское рисование через виджет CustomPaint
и связанные с ним классы CustomPainter
и Canvas
.
Одним из примеров виджета рисования является DecoratedBox
, который может рисовать BoxDecoration
на экране. В следующем примере показано, как использовать это для заполнения экрана градиентной заливкой:
class MyStatelessWidget extends StatelessWidget { @override Widget build(BuildContext context) { return new DecoratedBox( child: Icon(Icons.stars, size: 200.0), decoration: new BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.red, Colors.blue, Colors.green], tileMode: TileMode.mirror ), ), ); } }

Анимация
Flutter включает класс AnimationController
, который управляет воспроизведением анимации во времени, включая запуск и остановку анимации, а также изменение значений анимации. Кроме того, есть виджет AnimatedBuilder
, который позволяет создавать анимацию в сочетании с AnimationController
.
Свойства любого виджета, например показанной ранее украшенной звезды, можно анимировать. Например, рефакторинг кода в StatefulWidget
, поскольку анимация — это изменение состояния, и передача AnimationController
классу State
позволяет использовать анимированное значение при построении виджета.
class StarWidget extends StatefulWidget { @override State createState() { return StarState(); } } class StarState extends State with SingleTickerProviderStateMixin { AnimationController _ac; final double _starSize = 300.0; @override void initState() { super.initState(); _ac = new AnimationController( duration: Duration(milliseconds: 750), vsync: this, ); _ac.forward(); } @override Widget build(BuildContext context) { return new AnimatedBuilder( animation: _ac, builder: (BuildContext context, Widget child) { return DecoratedBox( child: Icon(Icons.stars, size: _ac.value * _starSize), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.red, Colors.blue, Colors.green], tileMode: TileMode.mirror ), ), ); } ); } }
class StarWidget extends StatefulWidget { @override State createState() { return StarState(); } } class StarState extends State with SingleTickerProviderStateMixin { AnimationController _ac; final double _starSize = 300.0; @override void initState() { super.initState(); _ac = new AnimationController( duration: Duration(milliseconds: 750), vsync: this, ); _ac.forward(); } @override Widget build(BuildContext context) { return new AnimatedBuilder( animation: _ac, builder: (BuildContext context, Widget child) { return DecoratedBox( child: Icon(Icons.stars, size: _ac.value * _starSize), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.red, Colors.blue, Colors.green], tileMode: TileMode.mirror ), ), ); } ); } }
class StarWidget extends StatefulWidget { @override State createState() { return StarState(); } } class StarState extends State with SingleTickerProviderStateMixin { AnimationController _ac; final double _starSize = 300.0; @override void initState() { super.initState(); _ac = new AnimationController( duration: Duration(milliseconds: 750), vsync: this, ); _ac.forward(); } @override Widget build(BuildContext context) { return new AnimatedBuilder( animation: _ac, builder: (BuildContext context, Widget child) { return DecoratedBox( child: Icon(Icons.stars, size: _ac.value * _starSize), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.red, Colors.blue, Colors.green], tileMode: TileMode.mirror ), ), ); } ); } }
В этом случае значение используется для изменения размера виджета. Функция builder
вызывается всякий раз, когда изменяется анимированное значение, в результате чего размер звезды изменяется в течение 750 мс, создавая эффект масштаба:

Использование собственных функций
Каналы платформы
Чтобы обеспечить доступ к собственным API-интерфейсам платформы на Android и iOS, приложение Flutter может использовать каналы платформы. Это позволяет коду Flutter Dart отправлять сообщения в хостинговое приложение iOS или Android. Многие доступные плагины с открытым исходным кодом созданы с использованием обмена сообщениями по каналам платформы. Чтобы узнать, как работать с каналами платформы, в документации Flutter есть хороший документ, демонстрирующий доступ к собственным API-интерфейсам батареи.
Заключение
Даже в бета-версии Flutter предлагает отличное решение для создания кроссплатформенных приложений. Благодаря отличным инструментам и горячей перезагрузке он приносит очень приятный опыт разработки. Множество пакетов с открытым исходным кодом и отличная документация облегчают начало работы. В будущем разработчики Flutter смогут ориентироваться на Fuchsia в дополнение к iOS и Android. Учитывая расширяемость архитектуры движка, меня не удивит, если Flutter приземлится и на множество других платформ. С растущим сообществом самое время присоединиться.
Следующие шаги
- Установить флаттер
- Дартс языковой тур
- Флаттер Codelabs
- Курс флаттера Udacity
- Исходный код статьи