Flutter – это полноценная SDK, позволяющая писать нативные приложения на ненативном языке (dart), имеющем высокую скорость работы (выше конкурентов, но немного ниже натива), отклика/отрисовки. Этот набор средств разработки позволяет писать достаточно сложные с точки зрения интерфейса и бизнес-логики приложения и имеет, что самое главное, достаточно низкий порог вхождения при высокой скорости прототипирования.

Всем доброго дня. Меня зовут Сурен, и я разработчик.

Примерно последние лет 8 я занимаюсь разработкой ПО в компании Digital Design. Я уже писал статьи на Хабр, про Sitefinity, .NET и школу разработки.

Основное моё направление работы – .NET, и меня платформа устраивает как инструмент разработки распределенных систем, Backend для SPA/Mobile, но мне все время не хватало самого Mobile… В свое время пробовал Xamarin, но не зашло. Да, у него есть огромный плюс – разработчик пишет на том же языке и в той же среде, и можно переиспользовать код … Но так ли это важно? Особенно учитывая, что логика мобильного приложения сильно отличается от логики бека (серверной части). Да и в целом минусов и ограничений полно, для этого есть отдельные статьи на Хабр. Я здесь с другой целью.

Существует множество способов/технологий разработки мобильных приложений, кроссплатформенных и не очень. Но для меня как для backend разработчика на .NET все они были достаточно сложными и с высоким уровнем вхождения. Я думал, что более-менее знаю JavaScript, поскольку в web-разработке всегда приходится что-то исправлять и не всегда есть свободные «фронт-разработчики», а проекты, как известно, имеют свойство гореть. 

Когда я открыл код существующего приложения на React Native, то понял, что не знаю JavaScript =) Даже фронт, написанный на React, более понятен, чем мобилка на React Native (для меня). Помню, спросил у разработчика, владеющего обеими технологиями, можно ли перевести front, написанный на React, на React Native-приложение и сколько это займет времени. Он мне ответил: «Давай лучше перепишем на Flutter». Я тогда не был сведущ в современных тенденциях мобильной разработки и сначала подумал, что это очередной фреймворк на js. Через некоторое время наткнулся на статью о кроссплатформенной разработке в мире мобильных приложений, из которой узнал, что появилось что-то новое и имя ему – Flutter.

В чем удобство?

При дебаге на устройство уходит виртуальная машина Dart VM, и код исполняется благодаря JIT-компиляции, можно поставить точку останова и отловить необходимые данные в случае непонятной ошибки, можно в режиме реального времени менять код и видеть изменения на экране телефона. При релизе dart-код компилируется в нативную сборку той платформы, для которой собирается приложение благодаря AOT компиляции.

Интерфейс

Приложение на Flutter представляет собой single-activity|storyboard приложение, то есть приложение с одним экраном, на котором располагается окно отрисовки собственного 2d графического движка, способного рендерить с частотой до 120Hz. При этом минимизируется зависимость от платформы, и приложение без зависимостей от платформенных особенностей будет выглядеть одинаково на любой версии Android или iOS.

У приложения единый UI на всех платформах. Единая логика. Вы просто пишите приложение для одной платформы, а оно будет так же работать на другой. Если упрощать, то Flutter – это UI фреймворк, построенный на принципе “It`s all widgets”, что можно переиначить как «все, что вы видите – это виджеты». UI Flutter`а построен на виджетах, маленьких кирпичиках платформы, которые встроены друг в друга. У всех виджетов есть дети (дочерние виджеты), у каких-то виджетов (row, column, listView, ...) это массив, у каких-то один-единственный, который также может включать своих детей – по сути, дерево. Виджеты-родители являются оберткой для виджета-ребенка, к примеру:

Center располагает вложенный элемент по центру.

Column располагает элементы вертикально, в виде столбика.

Row – соответственно горизонтально.

Виджеты во Flutter можно поделить на 3 группы: стандартные общие виджеты, виджеты, реализующие гайдлайн дизайна Материал (Android style) и Cupertino widgets (iOS style).

У Flutter есть 2 базовых класса для создания своих виджетов, а именно:

StatelessWidget, не требующий изменяемого состояния, и StatefullWidget, имеющий изменяемое состояние. Отличаются они, по сути, тем, что первый рендерится целиком и, если вам надо поменять какой-то элемент в нем, то придется перестраивать виджет полностью извне. Второй же может отрисовывать конкретные изменения внутри себя без перерисовки всего виджета.

Еще один плюс дерева виджетов – это его схожесть с деревом фреймов в Figma, в которой проектируют интерфейсы UX, а также достаточно мощная система стилизации виджетов, позволяющая «переносить» дизайн из Figma в приложение, а не рисовать его с нуля.

HelloWorld на flutter

Это весь файл main.dart, который выведет “Hello world! “ по центру экрана устройства.

Про низкий порог вхождения

Dart — язык программирования, созданный Google. Он позиционируется как замена/альтернатива JavaScript.

Одна и задач разработчиков языка гласит: «Сделать язык похожим на существующие для упрощения обучения», и они справились на все 100%. Мне как .NET-чику не пришлось учить язык, чтобы писать на нем: посмотрел пару примеров, а дальше писал «по наитию», предполагая наличие того либо иного свойства, либо метода у различных объектов.

К примеру, способ пройти по всем заданиям и вытащить картинки для отправки на сервер, полученные с камеры, похож на методологический linq в C#:

Меняем Select на map, а SelectMany – на expand =)

Future помечаются асинхронные методы, как Task в C#, ключевое слово async идет не в начале метода, а перед его телом.

  Future _sendPhotos(Bypass bypass) async {
    var attaches = bypass.jobs
        .where((job) => job.photos.any((element) => element.id == null))
        .map((e) => JobFiles(
            e.id,
            e.photos
                .where((element) => element.id == null)
                .map((e) => File(e.localPath))
                .toList()))
        .toList();

    for (var i = 0; i < attaches.length; i++) {
      await _apiRepository.addPhotosToJobResult(
          attaches[i].jobId, attaches[i].files);
    }
  }

На сайте платформы (flutter.dev) изложен урок из четырех шагов, который учит, как сделать приложение с бесконечным списком. Второй урок расширяет его до списка избранного с ленивой загрузкой и вторым экраном. По сути это уже позволяет понять, как писать на нем.

Также на Google Codelabs есть уйма уроков на все случаи жизни. У кого сложности с английским языком, есть альтернатива на русском (flutter.su). Там за 7 уроков вы освоите все необходимое для разработки приложения. Главное – помнить, что на официальном сайте информация обновляется быстрее, на сторонних ресурсах представленные примеры кода могут оказаться нерабочими.

Опыт

Теперь о нашем опыте. Приложение MVP я разработал в течении месяца, при том, что на старте знаний платформы или языка у меня не было. Задача была реализовать интерфейс, спроектированный нашими UX-дизайнерами, (около 5 экранов и состояний) и реализовать бизнес-процесс прохождения чек-листа (чек-листы/задания/блоки параметров). На текущий момент приложение из MVP переросло в полноценный проект, и релиз приложения внедрен у заказчика. О трудозатратах судить сложно, но что мы вынесли из нашего опыта, разработка на Flutter для обоих платформ составляет примерно ¾  от разработки 2-х нативных приложений (iOS и Android) с рисками ;-)

Но есть и ложка дегтя: приложение, которое писалось для iOS, спустя 4 месяца на Android не заработало, так как за время разработки ушел в небытие один из публичных репозиториев (привет, JCenter) =) и пришлось еще обновить gradle. Вообще чтобы с первого раза все запустилось на обеих платформах, при кроссплатформенной разработке все же нужно понимать, как на каждой платформе устроен менеджер пакетов, иметь обновленные инструменты. В конечном счете после манипуляций с пакетами и обновления всего и вся (1,5 дня) у нас все запустилось и сейчас работает без проблем. Но самое главное – мы не столкнулись с ситуацией, когда на одной платформе баги есть, а на другой нет, все манипуляции не затронули код проекта, только окружение.

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

Ограничения

На мой взгляд, если вы пишете приложение, активно использующее внутренние возможности платформы, будь то видео звонки, криптографическое шифрование туннеля или дополненная реальность, то возможностей Flutter вам может не хватить. Тут на помощь придут каналы платформы (platform channels) – компонент, позволяющий из dart-кода вызывать нативный код на java/kotlin для Android и на objective-c/swift на iOS. Даже есть удобный пакет Pigeon, который позволяет по классу на dart`е генерировать код на обоих платформах. Тут есть очень большой минус: нужно либо владеть нативными языками для каждой из используемых платформ, либо привлекать нативных специалистов, что мы и сделали, когда писали пакет по поддержке криптографических алгоритмов.

Также Flutter не подойдет для game-dev, поскольку движок у него 2d и рассчитан в основном на рендер интерфейса, полноценных мобильных игр на нем не напишешь.

Вместо вывода

На мой взгляд, Google удалось сделать то, что долгое время не удавалось никому, так как благодаря Flutter в нише разработки приложений на iOS/Android появился достойный конкурент нативной разработке. Не лишенный недостатков, конечно, но имеющий все для их преодоления. Приложения получаются компактные и быстрые как в разработке, так и в работе. Это позволяет неплохо так сэкономить на этой самой разработке.

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

Комментарии (21)


  1. darkmon
    09.11.2021 13:59

    Хорошая статья. Что под капотом, как реализовано и короткими словами — спасибо.
    Думаю, 3/4 трудозатрат от разработки 2 нативных это не слишком профитно. Но, если билдить еще и десктопные приложения под 3 оси, то выигрыш должен быть уже бескомпромиссным?


    1. avenumDev
      09.11.2021 15:09

      на самом деле 3/4 это с учетом погружения в технологию, первый опыт первого приложения

      по факту экономия порядка 30% по мнению более опытных команд, достигается за счет того что тестировать и выкатывать то все равно надо 2 приложения а разрабатывать по сути одно приложение одной командой =)

      если увеличить количество платформ, то все равно добавится тестирование и выкладка на них, но не будет затрат на разработку, это да


  1. roboter
    09.11.2021 14:00

    "У приложения единый UI на всех платформах." - с каких пор это плюс? у всех свои гайдлайны да и пользователь чувствует не родное приложение.
    Ionic - позволяет существующее приложение переиспользовать, React, Angular, Vue.


    1. avenumDev
      09.11.2021 15:21

      ну ionic и другие Web-фреймворки рендерят пользовательский интерфейс на WebView,

      тут скорее про другое, никто не мешает для IOS делать ui на Cupertino widgets, а для Android на Material

      просто у приложений будет общий UI слой, так-же это полезно когда стиль приложения не привязан к гайдлайну IOS/Android

      зато благодаря движку flutter может гарантировать одинаковую работу приложения на всех версиях OS устройств


      1. roboter
        09.11.2021 17:30

        Да, WebView, не нужно другой язык изучать. тот же js, html,css.
        Еще бы про xamarin хочется послушать, особенно от дотнетчика, вроде microsoft его ещё не убила.


        1. avenumDev
          09.11.2021 17:54

          У webView есть определенные проблемы с производительностью графики

          С xamarin работал только давно, и он имел тогда кучу ограничений, формс не давал необходимой гибкости кастомизации а нативный ui убивал кроссплатформенность


    1. DVF
      10.11.2021 19:56
      +1

      Можно разветвить, можно использовать готовые пакеты, меняющие интерфейс в зависимости от платформы. Я предпочёл сделать свои класы с if для часто используемых элементов.

      final bool isApple = !kIsWeb && (Platform.isMacOS || Platform.isIOS);
      
      /// Button.
      class MpButton extends StatelessWidget {
        const MpButton({Key? key, this.label, this.onPressed}) : super(key: key);
        final String? label;
        final Function? onPressed;
      
        @override
        Widget build(BuildContext context) {
          if (isApple) {
            return CupertinoButton.filled(
              child: Text(label!),
              onPressed: onPressed as void Function()?,
            );
          } else {
            return ElevatedButton(
              child: Text(
                label!,
                style: const TextStyle(color: Colors.white),
              ),
              onPressed: onPressed as void Function()?,
            );
          }
        }
      }


      1. avenumDev
        10.11.2021 20:18

        Да спасибо, это и имел ввиду


    1. DVF
      10.11.2021 19:57
      +1

      P.S. В SDK есть Material и iOS виджеты, в сторонних пакетах Mac, Windows и Ubuntu.


      1. avenumDev
        10.11.2021 20:19

        Ооо, я эти платформы ещё не юзал, спасибо за информацию


        1. DVF
          10.11.2021 21:30

          В dart:io определены все поддерживаемые платформы:

            static final bool isLinux = (_operatingSystem == "linux");
            static final bool isMacOS = (_operatingSystem == "macos");
            static final bool isWindows = (_operatingSystem == "windows");
            static final bool isAndroid = (_operatingSystem == "android");
            static final bool isIOS = (_operatingSystem == "ios");
            static final bool isFuchsia = (_operatingSystem == "fuchsia");

          Плюс можно узнать, например, запущен ли Flutter for web на десктопе или мобиле.

          bool get isMobileDevice => !kIsWeb && (Platform.isIOS || Platform.isAndroid);
          bool get isDesktopDevice =>
               !kIsWeb && (Platform.isMacOS || Platform.isWindows || Platform.isLinux);
          bool get isMobileDeviceOrWeb => kIsWeb || isMobileDevice;
          bool get isDesktopDeviceOrWeb => kIsWeb || isDesktopDevice;


          1. avenumDev
            15.11.2021 10:40

            слушай, а стало интересно, а можно определить на вебе мобила на ios или на android?

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

            =)


            1. DVF
              15.11.2021 16:22

              Есть пакет только под web, он даже классы CSS позволяет подставлять в зависимости от операционки. Но я сам не пробовал, вообще я не лучшего мнения о производительности флаттера в вебе, даже не смотря на все последние улучшения. Если только для MVP использовать.

              https://pub.dev/packages/platform_detect


              1. avenumDev
                15.11.2021 18:52

                Согласен с Вашим мнением


  1. webwork
    09.11.2021 21:14

    Всегда был kiwi на python, работает по тем же принципам, что и Flutter. Так что говорить, что удалось создать - это громко и не похоже на правду. А вот то, что удалось продвинуть - это да.


    1. avenumDev
      09.11.2021 21:27
      +1

      Не хочу разжигать войн, но один из разработчиков kivyMD в статье на хабре указал что flutter быстрее чем kivy для мобильных платформ.

      https://habr.com/ru/post/546684/

      От себя же хочу добавить, что о Kivy узнал только что, а флаттер на слуху, возможно дело в том что kivy разрабатывает сообщество, а flutter это openSource разработанный, продвигаемый и поддерживаемый Google. Так что да Вы правы продвинуть его получилось однозначно лучше.