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

Причем для последней ОС – Fuchsia – это пока единственный способ создать приложение.
Flutter на протяжении долгого времени, с 2015 года, был представлен только в альфа и бета версиях. Релиз первой стабильной версии состоялся 4 декабря 2018 года.


Flutter активно продвигается Google, постепенно набирает популярность и, скорее всего, в дальнейшем будет теснить другие, используемые сейчас средства кроссплатформенной разработки (React Native, Xamarin), особенно при условии широкого распространения Fuchsia. С учетом того, что Google позиционирует данную операционную систему как замену Android, рано или поздно Flutter вытеснит нативную разработку под Android. Поэтому перспективность и активное развитие – основные плюсы Flutter.


+ Перспективность и активное развитие


Давайте разберемся, как это работает.


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


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


Заметим, что отсюда следует один из минусов Flutter:


— Конечный установочный пакет больше, так как в него добавляется виртуальная машина Dart.


Таким образом, есть файлы Flutter и есть виртуальные машины, которые добавляются в зависимости от того, что компилируется – iOS или Android.


В составе виртуальной машины есть собственный графический движок, он рисует интерфейс приложения со всеми переходами между экранами, диалогами, фрагментами и т.д. В этом разработка под Flutter значительно отличается от разработки с Xamarin и React Native, которые используют реальные Android и iOS компоненты. В случае с ними невозможно использовать специфические для платформы компоненты (если такая необходимость есть, приходится создавать два варианта UI). С Flutter при выборе дизайна достаточно ориентироваться на одну платформу (например, Android). При сборке проекта под iOS вы увидите стандартный Android интерфейс. Это будет выглядеть немного странно и неожиданно, но вполне работоспособно (впоследствии интерфейс можно доработать).


+ Собственный графический движок (нет необходимости делать интерфейс отдельно для Android и iOS)


Теперь о впечатлениях.


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


Первое, что бросается в глаза – это способ создания экранов, который значительно отличается от используемых на Android и iOS. В Android разделена логика и интерфейс: логика задается кодом, а интерфейс – версткой в xml. На Flutter все это задается с помощью кода. Хотя здесь для интерфейса используется особый стиль – элементы интерфейса создаются вложенными друг в друга. Это немного похоже на верстку, очень похожий способ действует в React Native. При этом отсутствует возможность прямого доступа к элементам. Чтобы что-то изменить на экране, нужно либо обновить весь экран, либо воспользоваться специальными контроллерами, заблаговременно добавленными к виджету во время его создания.


— Интерфейс создается с помощью кода, из-за чего грань между логикой и дизайном гораздо тоньше.


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


+ Интерфейс легко разбивается на отдельные модули


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


Сделаем три вкладки:


1) Первая – с текстом и ползунками для настройки размера и цвета текста
2) На вторую добавим загружаемую картинку (с индикатором прогресса)
3) На третьей поместим пример списка




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

Теперь рассмотрим исходный код, который реализует такую верстку:


void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
 // This widget is the root of your application.
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     title: 'Flutter Demo',
     theme: ThemeData(
       primarySwatch: Colors.blue,
     ),
     home: MyHomePage(title: 'Home Page'),
   );
 }
}

class MyHomePage extends StatefulWidget {
 MyHomePage({Key key, this.title}) : super(key: key);

 final String title;

 @override
 _MyHomePageState createState() => _MyHomePageState();
}

Данный фрагмент кода является стандартным для практически любого Flutter приложения (он создается вместе с проектом).


MyApp – это класс самого приложения, в котором при создании MaterialApp описываются общие параметры: название приложения, шрифты, цвета и стили. Также здесь указывается основной экран приложения (для нас это MyHomePage).


Сделаем важное замечание: во Flutter виджеты разделяются на два типа:


1) StatefulWidget
2) StatelessWidget


Для описания StatefulWidget требуется два класса: класс самого виджета и класс его состояния (в котором и будет происходить основная работа).


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


Теперь рассмотрим _MyHomePageState:


class _MyHomePageState extends State<MyHomePage> {
 int _currentIndex = 0;
 double _size = 14;
 double _r = 0;
 double _g = 0;
 double _b = 0;

 @override
 Widget build(BuildContext context) {
   return Scaffold(
       appBar: AppBar(
         title: Text(widget.title),
       ),
       body: <Widget>[

Для простоты восприятия красным цветом помечена вкладка с текстом, зеленым – вкладка с картинкой, синим – вкладка со списком, а желтым – навигационное меню. Как можно заметить, интерфейс описывается как множество вложенных друг в друга виджетов (и их массивов):










Используемые функции:
 void _onTapped(int index) {
   setState(() {
     _currentIndex = index;
   });
 }

 void _setTextStyle(
     {double size = -1, double r = -1, double g = -1, double b = -1}) {
   setState(() {
     if (size > 0) {
       _size = size;
     }
     if (r > 0) {
       _r = r;
     }
     if (g > 0) {
       _g = g;
     }
     if (b > 0) {
       _b = b;
     }
   });
 }
}

Рассмотрим их немного поподробнее:


onTapped – функция, вызываемая при переключении вкладки в нижнем меню. В ней вызывается специальная функция setState, которая позволяет обновить текущий виджет с новыми данными (а мы обновили переменную _currentIndex).


Посмотрим, где она применяется:


body: <Widget>[
        текст
        картинка
        список
        ][_currentIndex]

Здесь мы имеем дело с массивом, откуда с помощью _currentIndex выбирается один из вариантов разметки экрана, и он подставляется как одна из вкладок.


Далее идет функция _setTextStyle. Она имеет весьма необычное объявление для C-подобных языков.


void _setTextStyle({double size = -1, double r = -1,
                       double g = -1,double b = -1})

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


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


_setTextStyle(size: 24, b: 255)

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


Первая вкладка:


class TextWidget extends StatefulWidget {
 @override
 _TextWidgetState createState() => _TextWidgetState();
}

class _TextWidgetState extends State<TextWidget> {
 double _size = 14;
 double _r = 0;
 double _g = 0;
 double _b = 0;

 @override
 Widget build(BuildContext context) {
   return Column(
     children: <Widget>[
       Text("Example String",
           style: TextStyle(
               fontSize: _size,
               color: Color.fromRGBO(_r.toInt(), _g.toInt(), _b.toInt(), 1))),
       Container(constraints: BoxConstraints.expand(height: 32.0)),
       Slider(
           label: "${_size.toInt()} sp",
           value: _size,
           min: 10,
           max: 48,
           divisions: 38,
           activeColor: Colors.black,
           inactiveColor: Colors.grey,
           onChanged: (val) => _setTextStyle(size: val)),
       Slider(
         label: _r.toInt().toString(),
         value: _r,
         min: 0,
         max: 255,
         divisions: 255,
         activeColor: Colors.red,
         inactiveColor: Colors.grey,
         onChanged: (val) => _setTextStyle(r: val),
       ),
       Slider(
         label: _g.toInt().toString(),
         value: _g,
         min: 0,
         max: 255,
         divisions: 255,
         activeColor: Colors.green,
         inactiveColor: Colors.grey,
         onChanged: (val) => _setTextStyle(g: val),
       ),
       Slider(
         label: _b.toInt().toString(),
         value: _b,
         min: 0,
         max: 255,
         divisions: 256,
         activeColor: Colors.blue,
         inactiveColor: Colors.grey,
         onChanged: (val) => _setTextStyle(b: val),
       ),
     ],
   );
 }
}

Так как виджет необходимо обновить (метод _setTextStyle), мы используем StatefulWidget.
Для следующих двух вкладок нет необходимости в обновлении, поэтому будем использовать StatelessWidget.


Вторая вкладка:


class ImageWidget extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return Stack(
     children: <Widget>[
       Center(child: CircularProgressIndicator()),
       Center(
         child: FadeInImage.memoryNetwork(
           placeholder: kTransparentImage,
           image: 'https://picsum.photos/250?image=9',
         ),
       ),
     ],
   );
 }
}

Третья вкладка:


class ListWidget extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return ListView.builder(
     itemCount: 25,
     itemBuilder: (BuildContext context, int index) {
       return Container(
         child: Text(
           'entry $index',
           style: TextStyle(color: Colors.white),
         ),
         margin: EdgeInsets.all(16.0),
         padding: EdgeInsets.all(16.0),
         decoration: BoxDecoration(
           color: Colors.blue,
           borderRadius: BorderRadius.all(
             Radius.circular(16.0),
           ),
         ),
       );
     },
   );
 }
}

Измененный код состояния основного экрана:


class _MyHomePageState extends State<MyHomePage> {
 int _currentIndex = 0;

 Widget build(BuildContext context) {
   return Scaffold(
       appBar: AppBar(
         title: Text(widget.title),
         actions: <Widget>[
           IconButton(icon: Icon(Icons.navigate_next), onPressed: next)
         ],
       ),
       body: <Widget>[
        TextWidget(),
        ImageWidget(),
        ListWidget(),
    ][_currentIndex],
       bottomNavigationBar: BottomNavigationBar(
         currentIndex: _currentIndex,
         onTap: _onTapped,
         items: [
           BottomNavigationBarItem(
             icon: new Icon(Icons.text_format),
             title: new Text('Text'),
           ),
           BottomNavigationBarItem(
             icon: new Icon(Icons.image),
             title: new Text('Image'),
           ),
           BottomNavigationBarItem(
             icon: Icon(Icons.list),
             title: Text('ListView'),
           )
         ],
       ));
 }

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


И все же есть минимальные различия между виджетом, который используется как экран, и обычным виджетом. Корневым элементом для виджета экрана должен быть объект Scaffold (позволяет добавлять appBar, bottomNavigationBar, floatingActionButton, drawer и т.д.).


На обычные виджеты данное ограничение не распространяется, так как с помощью метода build они встроятся в основной экран, где уже есть Scaffold.


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


Также можно добавить SafeArea (чтобы обеспечить отступ для status bar). Получается следующее простое преобразование:


От:


@override
Widget build(BuildContext context) {
  return [код];
}

К:


@override
Widget build(BuildContext context) {
  return Scaffold(
    body: SafeArea(
    child: [код]
    ),
  );
}

Ну а теперь вернемся к обсуждению плюсов и минусов Flutter.


Flutter недавно вышел в релиз, поэтому баги встречаются достаточно часто. Особенно это заметно при обновлении Flutter – некоторые библиотеки начинают работать с ошибками.


— Нестабильность (совсем недавно вышел из beta)


Очевидно, что при использовании нового фреймворка в вашем распоряжении гораздо меньше библиотек, чем при нативной разработке по Android/iOS. Однако, библиотек для Flutter все равно довольно много, и они продолжают появляться с большой скоростью. Так, например, многие библиотеки были добавлены во второй половине 2018 года, судя по всему, в рамках подготовки к первому стабильному релизу, а важнейшие библиотеки (Google Analytics, Firebase, Maps и т.д.) существовали и до этого.


— Библиотек меньше, чем для нативной разработки
+ Важнейшие библиотеки уже есть, постоянно выходят новые


Самое время подвести итоги! Давайте вспомним все плюсы и минусы, расположив элементы от самых существенных плюсов до самых существенных минусов:


+ Кроссплатформенность
+ Перспективность и активное развитие
+ Важнейшие библиотеки уже есть, постоянно выходят новые
+ Собственный графический движок
+ Интерфейс легко разбивается на отдельные модули


— Конечный установочный пакет больше, так как в него добавляется виртуальная машина Dart
— Интерфейс создается с помощью кода, из-за чего грань между логикой и дизайном гораздо тоньше
— Библиотек (и информации) меньше, чем для нативной разработки
— Нестабильность (совсем недавно вышел из beta)


Спасибо за внимание!

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


  1. Skycaptain
    27.02.2019 10:31

    А где можно прочитать про то, как google позиционирует fuchsia?


    1. SSul Автор
      27.02.2019 11:29

      Представители Google осторожны в высказываниях, и официальных планов по развитию Fuchsia пока нет. «Google смотрит на эти эксперименты с открытым кодом как на инвестиции в инновации», — например, так ответили журналистам Bloomberg. По данным других источников Bloomberg, однажды Fuchsia заменит Android, но это вряд ли произойдёт раньше, чем через пять лет.


      1. Skycaptain
        27.02.2019 11:59

        я просто, например, читал, что google позиционирует fuchsia как os сугубо для loT устройств


  1. meta4
    27.02.2019 10:59

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


    1. SSul Автор
      27.02.2019 11:30

      Мы рады, что статья вам полезна! Да, мы пробовали Flutter на одном небольшом проекте.


      1. meta4
        27.02.2019 11:39

        Тогда я готов завалить расспросами :)

        • Общее впечатление разработчиков — понравилось ли писать на Flutter? Подход-тулинг-язык?
        • Насколько удобно было писать (по сравнению с нативом)?
        • Насколько сильно приходилось if-ами прописывать разные виджеты для разных платформ?
        • С какими проблемами производительности-стабильности столкнулись?
        • Планируете ли использовать дальше на более крупных проекта?


        1. Tonn_Tamerlan
          27.02.2019 12:08

          1. Общее впечатление отличное. Как по мне, то порог входа достаточно низок, все интуитивно понятно, документация по самому фреймворку на достаточном уровне.
          2. Я лично до этого вообще не писал для моб, но со слов остальных членов команды Флаттер по удобности переплевывает натив.
          3. if нужны только если хочется отдельные дизайны для разных платформ. Если дизайн одинаковый, то кодовая база одна и никаких if.
          4. Скорее да.
          Как-то все слишком позитивно получилось, но нет, я не продвигаю Flutter если что:). Трудности были, но все они больше связаны с отстутствием опыта, периодической несовместимостью различных библиотек между собой, но это все есть и в других фреймворках


        1. SSul Автор
          27.02.2019 13:10
          +1

          Да, понравилось, особенно легкость, с которой интегрируется Flutter в AndroidStudio. Сам подход и язык тоже были признаны достаточно удобными, хотя и несколько непривычными после длительной нативной разработки. У Flutter отличается концепция верстки, но уже в течение первой недели разработки мы втянулись. В нашем случае мы переносили уже существующее приложение с Android на Flutter, оно выглядело идентично на Android и iOS. По производительности особых замечаний не было, по стабильности были вопросы, когда обновляли Flutter или какую-либо из сторонних библиотек (но это лечилось тщательной чисткой проекта). С крупными проектами – посмотрим, но сейчас у нас в работе еще один проект среднего масштаба на Flutter.


    1. Tonn_Tamerlan
      27.02.2019 11:36

      Попробовали Flutter на двух небольших проектах: один на хакатоне, второй коммерческий. Остались очень довольны выбором, т.к. в коммерческом проекте нужно было реализовать уникальный дизайн, который был бы одинаков на двух платформах и Flutter как раз в это умеет. К Dart быстро привыкаешь, библиотек уже хватает, hotreload is amasing ты мгновенно видешь все внесенные изменения с сохранением состояния, и прочие плюшки


      1. meta4
        27.02.2019 11:42

        А можно тогда и вас чуть подробнее расписать по списку вопросов из моего предыдущего коммента? habr.com/ru/company/simbirsoft/blog/441766/#comment_19812380


  1. AndreySu
    27.02.2019 11:25

    Конечный установочный пакет больше, так как в него добавляется виртуальная машина Dart.

    Вы наверное хотели сказать не виртуальная машина а рантайм? Это очень разные вещи.


    1. SSul Автор
      27.02.2019 12:27

      Реальная виртуальная машина там тоже есть, но добавляется только в debug сборку. А в release добавляется просто библиотека графического движка для отрисовки виджетов.


      1. MiT_73
        28.02.2019 10:47

        — Конечный установочный пакет больше, так как в него добавляется виртуальная машина Dart.

        Но это в режиме debug, а когда собирается конечный продукт (release) в нем отсутствует «виртуальная машина» и…
        добавляется просто библиотека графического движка для отрисовки виджетов

        А также приложение весит всего 4-5(mb) Android и 2-3(mb) IOS, и ещё в конечном результате приложение собирается в native версию.

        Почему тогда это относиться к минусу?
        ВМ, для разработки это большой плюс.


        1. SSul Автор
          28.02.2019 10:47

          Мы об этом уже писали в комментариях. Даже в release, по сравнению с Android, размер получается несколько больше. Хоть и не так критично, как в debug.


          1. MiT_73
            28.02.2019 14:21

            У вас смысл потерялся, вы написали что в

            — Конечный установочный пакет больше, так как в него добавляется виртуальная машина Dart.

            На самом деле в release версии виртуальной машины Dart там нет!


  1. kuza2000
    27.02.2019 11:36
    +1

    Вспоминаю электрон и не нахожу ответа на самый главный вопрос: как обстоят дела с потреблением памяти и ресурсов процессора?


    1. SSul Автор
      27.02.2019 12:28

      В этом отношении проблем не заметили.


  1. mkulesh
    27.02.2019 11:55

    Спасибо за статью, можно вопрос «от чайника»? Вот и Вашей статье, и в других статьях по Flutter всегда подчеркивают, что исходный код Dart компилируется в нативное приложение. Для iOS понятно. А что есть «нативное» для Андроида? Это действительно исполняемый файл Линукса или все-таки байт-код для виртуальной машины Андроида? Если это исполняемый файл Линукса, то как реализована платформенная зависимость х86/х86_64/arm и есть ли возможность прицепить сторонние динамические библиотеки (*.so)? Как реализованы права доступа к аппаратным ресурсам? Если я запустил такое приложение из под root, у приложения автоматически полный доступ по всему? Я всегда считал, что виртуальная машина — это есть одновременно и сердце и главное преимущество Андроида, а тут выглядит как шаг назад обратно к машинному коду. Или я ошибаюсь?


    1. SSul Автор
      27.02.2019 15:11

      Вопрос о прогрессивности Flutter вызвал у наших разработчиков оживленную дискуссию, поэтому, полагаем, на этот вопрос трудно ответить однозначно) Что касается исходного кода Dart, он компилируется в нативные библиотеки для ARM и x86 и подключается к Android проекту. Скорее всего, такой подход продиктован стремлением увеличить производительность и снизить зависимость от Android для возможного перехода на Fuchsia.


    1. Necessitudo
      27.02.2019 16:24

      Так там на выходе собирается apk-файл.


    1. KanuTaH
      27.02.2019 21:23
      +1

      В Android любые пользовательские приложения выполняются каждое в своей «песочнице», при этом неважно, скомпилировано ли приложение в платформенный машинный код или в платформонезависимый байткод типа Dalvik. Более того, со времен ART то, что вы называете «виртуальной машиной» практически отсутствует, потому что ART автоматически перекомпилирует байткод из APK в нативный платформенный код («исполняемый файл Линукса», если угодно) заранее после установки приложения либо во время первого запуска, а не занимается постоянной JIT компиляцией байткода во время его выполнения как Dalvik VM. Более того, никто не мешал и не мешает прицепить .so даже к байткоду в Dalvik VM — через JNI. Так что ваши представления о реализации механизмов защиты приложений в Android несколько… хм. Ну и Android NDK был с нами с первых версий Android и никуда пропадать не планирует, на его основе вполне себе пишутся и работают приложения в нативном коде под Android, я лично делаю это на Qt, вот сейчас и Flutter появился примерно с той же парадигмой. Это вовсе не «шаг назад», а скорее «шаг вперед», особенно в плане производительности.


      1. mkulesh
        27.02.2019 22:12

        Спасибо, развернуто и понятно написали. Так уж получилось, что все свои приложения я писал на Джаве, Котлин никогда серьёзно не рассматривал, так как по сути это та же самая VM, только синтаксис птичий. Поэтому фраза «компилируется в нативный код» меня и заинтриговала. Вот и пытаюсь разобраться. И, вроде, действительно привлекательно выглядит, чтобы на эту технологию переходить. Я автор микро-Математики (https://github.com/mkulesh/microMathematics) и просто пытаюсь понять, стоит ли её портировать на Flutter и насколько это сложно. Явные плюсы — производительность и iOS, но трудозатраты большие, что пугает.


        1. KanuTaH
          27.02.2019 22:29

          Ну если вы хотите iOS, то придется по-любому ваш продукт на чем-то переписывать — либо на ObjC/Swift (но тогда вам придется параллельно поддерживать обе, мягко говоря, не пересекающиеся кодовые базы), либо на чем-то кроссплатформенном, второе, по-моему, предпочтительнее. Правда, лично я все больше с Qt/QML имею опыт, Flutter не пробовал. Кстати, забавно, раньше при виде фразы «cross-platform mobile toolkit» в статье модно было плеваться в комментариях в стиле «фи, не нативно» (в том смысле, что контролы в UI выглядят не «родными»). Но в последнее время эта тенденция как-то затихает, и «собственный графический движок» у таких тулкитов и «единообразие внешнего вида приложения на всех платформах» уже начинает постепенно выставляться как плюс.


  1. Tahallus
    27.02.2019 12:45

    Конечный установочный пакет больше, так как в него добавляется виртуальная машина Dart
    опять, уже столько написано про это, вы пробовали собирать release версию, а не debug. В debug еще и тормозит бывает, а в release все летает.


    1. SSul Автор
      27.02.2019 12:46

      Debug тоже пробовали, это можно заметить на скриншотах. Но даже в release версию добавляется библиотека графического движка, из-за чего размер apk все равно больше (хоть и не так критично, как в debug).


  1. bugs2bugs
    27.02.2019 14:48

    А почему используется такое решение с навигацией, а не роутинг с поддержкой Back Button?


    Navigator.push / pushNamed etc.


    1. SSul Автор
      27.02.2019 14:48

      Этот фрагмент кода приведен в качестве примера. Мы решили не загромождать код объявлением routes.


      1. bugs2bugs
        27.02.2019 15:14

        По моему мнению роуты как раз и помогают легко разбивать на смысловые модули/страницы, что Вы выделяете, как плюс.

        А приходилось ли Вам уже писать Native модули, и если да, то на какие задачи?
        Или пока библиотек хватало с головой?
        Спасибо за ответы


        1. SSul Автор
          27.02.2019 15:37

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


  1. ErgoZru
    28.02.2019 02:07

    Чувствую заминусуют меня за этот коммент но… Вы пробовали использовать QT? Если да, то чем для вас он хуже флаттера? Мы, например, в команде попробовали флаттер и поняли, что на данном этапе это слишком сыро. Производительность заметно хуже, чем на QT, хотя и там и там отрисовка на том же вулкане. Плюс QT охватывает гораздо больше платформ, плюс это всё-таки C++, и с точки зрения оптимизации там все гораздо лучше и нет лишних виртуальных машин и тд. Да и библиотек под плюсы просто не пересчитать. При наличии хорошего дизайнера вполне можно получить красивое приложение и на кьюте. Ну и заодно я бы посмотрел, как вы будете подключать какую-нибудь библиотеку написанную на сях или плюсах к проекту на флаттере (например тот же linphone). В будущем может из флаттера и выйдет что-то интересное, но в данный момент для коммерческой разработки это слишком сыро и просто опасно использовать, особенно когда на кону серьезные деньги. имхо.


    П.С. жду флаттер на базе го :) вот это было бы интересно :)


    1. SSul Автор
      28.02.2019 10:49

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


      1. ErgoZru
        28.02.2019 11:08

        На счёт сложности — а в чем сложность? Как по мне скакание вокруг сырости и багов гораздо сложнее, чем у более менее стабильного решения. Сложность в языке, то что там c++? Да, во флаттере есть готовые визуальные составляющие, которые смотрятся симпатично, но если нужно делать под конкретный дизайн, то там тоже начинается веселье. И как писал выше — использование любой сторонней библиотеки это боль и страдания. И не сказал бы, что это прям совсем другая ниша, скорее QT умеет в разы больше, но никто не заставляет всем этим пользоваться, можно пользоваться только теми инструментами которые нужны, а потом мучительно не переписывать на другой язык если проект вырастает. Да и комьюнити у кьюта огромно, есть форумы где можно найти и баги, и воркэраунды для их обхода, да и обновления выходят довольно часто. :)


        1. KanuTaH
          28.02.2019 15:56

          В Qt приложение зачастую можно написать так, что от C++ там будет только штампованный main.cpp из десятка строчек, инициализирующий декларативную машинерию и подгружающий локализацию, и все. Интерфейс при этом будет написан на QML, который вполне по силам освоить даже дизайнеру (он для этого и разработан), а бизнес-логика — на JS. При этом все будет летать, потому что QML-файлы инстанцируются в Qt'шные классы, у которых все внутренности (кроме пользовательских обработчиков событий, пожалуй, которые в данном случае будут на JS) написаны на C++. Причём даже все это можно ускорить ещё больше, перенеся момент парсинга/инстанцирования с момента подгрузки QML-файла на момент сборки приложения с помощью Qt Quick Compiler.


          1. ErgoZru
            01.03.2019 01:08

            Кстати тоже отличный вариант :) Но в нашем случае приложение полностью на c++ без JS, архитектура viper, используется довольно много c++ библиотек (в том числе openssl), и отказались от qt quick. Тем не менее приложение работает очень шустро и выглядит вполне прилично, с анимашками и прочими финтифлюшками :) но согласен полностью, что можно пойти более простым путем и не усложнять себе жизнь, если того не требуют обстоятельства.


  1. AlexeyMoiseev
    28.02.2019 10:42

    Касались ли вы вопроса использования C++ в приложении, написанном на Flutter?


    1. SSul Автор
      28.02.2019 10:43

      Нет, пока мы пробовали на Flutter относительно лёгкие проекты малого и среднего размера. На них фреймворк показывал достаточную производительность.


  1. r0ck3r
    28.02.2019 11:07

    Хотел бы задать вопрос касательно архитектуры Flutter-приложений.
    Я разрабатываю некоторое приложение с Drawer, где в пределах одного Scaffold хочу обновлять body
    Каждый body — отдельный класс. У меня имеется необходимость менять appBar в Scaffold из body после завершения какого-либо действия, например загрузки данных из сети. Я решил проблему следующим образом:
    Создал абстрактный класс IPagedScaffold, в котором объявил метод updateTitle(String title) и реализовал его в State-классе своего Scaffold
    Создал абстрактный класс MaterialFragment extends StatefullWidget, от которого будут наследоваться body, устанавливаемые в Scaffold
    В классе MaterialFragment создал поле IPagesScaffold _scaffold и сделал для него геттер и сеттер. Сеттер я использовал в конструкторе State-класса моего Scaffold, вызывая в конструкторе метод body.setPagedScaffold(this);
    В классах State я мог установить заголовок через widget.getPagedScaffold().updateTitle('Some title') и все было хорошо.
    Но, заметив, что Android Studio подчеркивает MaterialFragment увидел сообщение, что классы Widget помечены как immutable и соответственно все поля должны быть объявлены как final. Это испортило все.

    Получается, что я могу установить Scaffold только в конструкторе, однако в момент инициализации body, Scaffold может быть еще не создан, например в случае если я добавляю на экран сразу Scaffold вместе с body: new MyPagedScaffold(new MyMaterialScreen());

    Попытался внутри класса State моего body сделать (Scaffold.of(context) as IPagedScaffold).updateTitle(«My title»), но получил сообщение об ошибке, что Scaffold не является экземпляром класса IPagedScaffold

    Пока вижу два решения:
    1) Оставить все как есть и не обращать внимания на предупреждение об иммутабельности Widget (мне видится это неправильным)
    2) Создать класс-хранилище, который будет хранить в статичном поле экземпляр моего IPagedScaffold (вижу это решение очень грязным)

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


    1. SSul Автор
      28.02.2019 12:32

      Обычно на проектах мы используем архитектуру redux, где для решения подобной проблемы можно создать событие на обновление title bar определенного экрана.
      Или более простой вариант (без смены архитектуры):
      stackoverflow.com/questions/48481590/how-to-set-update-sate-of-statefulwidget-from-other-statefulwidget-in-flutter
      В State можно создавать обычные — не final — переменные и геттеры, сеттеры к ним (без предупреждений от Android Studio)


      1. r0ck3r
        28.02.2019 13:36

        Да, то что в State можно создавать обычные поля я знаю. В варианте по ссылке в актуальном варианте решения предлагают передавать ссылку на State в конструктор виджета, откуда она записывается в собственное поле виджета _MyHomePageState parent:

        class Sub extends StatelessWidget {
        
          _MyHomePageState parent;
        
          Sub(this.parent);
        
          @override
          Widget build(BuildContext context) {
            return new InkResponse(
              child: new Container(
                  margin: globalMargin,
                  color: Colors.red,
                  child: new Center(
                    child: new Text(
                      "-",
                      style: textStyle,
                    ),
                  )),
              onTap: () {
                this.parent.setState(() {
                  this.parent.number --;
                });
              },
            );
          }
        }
        

        стоит отметить, что в этом примере они также не указали _MyHomePageState parent как final, на что будет жаловаться Idea (что не будет при этом мешать коду корректно(?) работать). Если подразумевается, что виджет, получивший ссылку на _MyHomePageState является StatefulWidget виджетом, то эту ссылку можно будет передать в State и там уже делать с ней что заблагорассудится
        Моя проблема немного в другом: на момент создания как Parent-виджета так и Sub-виджета их State не определены, так как я в конструктор Parent-виджета пытаюсь передать Sub-виджет. Я пытался так сделать, чтобы один и тот-же класс MyScaffold можно было использовать как для фрагментов (замены содержимого Scaffold), так и для новых Route, просто создав экземпляр класса MyScaffold и передав ему в конструктор новый экземпляр вложенного виджета. Скорее всего мне нужно пересматривать архитектуру, но ничего внятного пока в голову не лезет


  1. blantcat
    28.02.2019 13:17

    Есть ли у кого-то опыт перехода с React Native на Flutter? Интересно услышать, что послужило причиной, с какими проблемами/ограничениями столкнулись и к какому выводу пришли.