После анализа различных вопросов, связанных с Flutter в течение некоторого времени, я внезапно обнаружил, что многие новички, недавно начавшие работать с Flutter, имеют различные заблуждения относительно этой технологии. Повторное объяснение этих моментов занимает много времени, поэтому я решил написать статью, чтобы сделать некоторые выводы.
Содержание может быть достаточно объемным, но я уверен, что это поможет вам лучше понять Flutter.
Происхождение Flutter довольно интересно. Flutter зародился как внутренний эксперимент команды Chrome. Эксперты Google перед тем, как удалить некоторые "нестабильные" нормы фронтенда, заметили, что производительность увеличилась в 20 раз. Это случайное открытие привело к созданию Flutter.
Исходя из этого, можно сказать, что Flutter родился из мира фронтенда, а его происхождение указывает на то, что Flutter сам по себе не имеет большого количества синтаксического сахара, как фреймворк он является "консервативным", использует консервативный язык Dart.
Его модель программирования и синтаксис имеют явно фронтендовый характер, хотя первоначально он был применён в мобильной разработке.
Поэтому когда Flutter был представлен миру, ему пришлось столкнуться со следующими проблемами:- Для разработчиков нативных клиентских приложений декларативный способ разработки сразу вызывает дискомфорт, поскольку они привыкли к разделению кода и макета (Java/Kotlin + XML) и процедурному объектному программированию; декларативный подход требует дополнительных усилий для обучения; они также считают, что вложенность в Flutter "отвратительна".- Для разработчиков фронтенда установка среды Flutter очень сложна: помимо VSCode и Flutter SDK требуется установка таких нативных инструментов, как Java, Gradle, Android SDK, Xcode и других "вне круга" переменных окружения (часто возникают проблемы с сетью); знания необходимых для работы с Flutter нативных платформ для фронтендера являются неприятными; они также считают, что вложенность в Flutter "омерзительна".
Обратите внимание, что я не говорю о Dart как о затрудняющем обучение языке, так как для специалистов по JavaScript, Java, Kotlin или Swift Dart выглядит как "младший брат".
Кроме того, как фронтендеры, так и разработчики нативных приложений будут критиковать вложенность в Flutter, однако насколько серьёзна эта проблема? Мы поговорим об этом позже.
В целом, для начинающих фронтендеров или разработчиков нативных приложений, Flutter представляет собой определённые препятствия и психологический барьер. Но есть ли причины обязательно учиться Flutter для фронтендера или разработчика нативных приложений?### Причины учиться Flutter
При общении с новичками в Flutter, очень часто можно заметить, что они вынуждены использовать этот фреймворк, так как руководство требует его применения, поэтому приходится "неохотно" начинать обучение Flutter — это одна из самых весомых причин: "руководитель хочет", если вы не хотите менять работу.#### 1. Уровень личной конкурентоспособности
Разработка программного обеспечения — это интересная сфера, где мы часто сталкиваемся с тем, что после длительного использования одной технологии начинаем считать её популярной, а все остальные — устаревшими. Особенно это происходит под влиянием "СМИ", когда эффект "беременной женщины" приводит к ошибкам восприятия.
В середине прошлого года я провёл анализ данных по 53 образцам в статье "Анализ внедрения кросс-платформенных фреймворков мобильными гигантами Китая". В результате анализа были получены следующие данные: Flutter (19), Weex (17), React-Native (22). Ниже представлен график, отражающий количество производственных приложений, использующих Flutter, собранных со смартфона с помощью libChecker
.
Этот пример показывает, что Flutter уже не является малоизвестным фреймворком; за последние два года он стал одним из основных кросс-платформенных фреймворков.
Поэтому Flutter действительно может помочь вам найти работу, но я бы не рекомендовал начинать изучение именно с него, потому что Flutter представляет собой лишь кросс-платформенный UI-фреймворк.> Очень важно понять эту фразу, чтобы избежать навязывания бесполезного стресса, поскольку хотя Flutter поддерживает мобильные устройства, веб-браузеры и ПК, как UI-фреймворк он помогает решать проблемы с кросс-платформенным UI и частью бизнес-логики, но функции, связанные с платформой, такие как Bluetooth, взаимодействие с платформой, хранение данных и сборка, требуют поддержки native.На текущий момент все кросс-платформенные фреймворки, будь то Flutter, React-Native или Weex, имеют одну цель: решение проблем с кросс-платформенным UI и бизнес-логикой, и их развитие невозможно без поддержки native платформ.
Если native платформа прекратит существование, то говорить о кросс-платформенности будет бессмысленно. Например, кто сейчас говорит о необходимости кросс-платформенного развития для Windows Phone? Поэтому Flutter и native платформы должны развиваться вместе, а не противостоять друг другу, они находятся в состоянии паразитизма и симбиоза, и без опыта работы с native платформами сложно добиться удовлетворения от использования Flutter. Но сейчас Flutter действительно может помочь в вашем карьерном развитии, так как он позволяет расширять ваши возможности в области бизнес-разработки, позволяя участвовать в разработке на различных платформах, будь то фронтенд или KPI. Конечно, такие технологии, как React Native и Uni-app, тоже могут это предоставить, возможно даже с меньшими затратами для фронтенд-разработчиков, но почему стоит выбрать Flutter?
На самом деле есть еще один интересный момент: знание Flutter для нативной разработки Android равноценно знанию более чем 70% Jetpack Compose.
Потому что Flutter является UI-фреймворком, он действительно кросс-платформенный! Почему важно подчеркнуть "настоящую кросс-платформенность"? Потому что в отличие от React Native и Weex, компоненты Flutter не создаются через нативные компоненты, а используются платформенно-независимые возможности рендера, предоставленные движком Flutter. То есть компоненты Flutter не зависят от платформы.
Проще говоря, нативная платформа предоставляет
Surface
, который используется как холст, а затем все остальное рендерится Flutter, и этот процесс завершается сборкой в виде бинарного файла AOT.
Поэтому UI-компоненты Flutter могут обеспечивать консистентность во всех аспектах, что очень важно для меня. Почему именно так? Для этого стоит сравнить его с React Native.
Потому что React Native использует преобразование компонентов JavaScript в нативные компоненты для рендера, поэтому компоненты React Native зависят от нативных компонентов платформы. Из-за различий между нативными компонентами различных систем и различиями свойств и эффектов одного компонента в разных версиях одной системы, объединённые вместе, они становятся значительной стоимостью обслуживания при дальнейшей разработке.
Во время моего опыта работы с React Native мне часто приходилось сталкиваться с ситуациями:
Конечно, эти проблемы можно решить с помощью условных операторов и создания собственных компонентов для каждой платформы, но по мере развития проекта такой подход противоречит целям использования кросс-платформенного решения. Характеристики компонентов Flutter делают его свободным от таких проблем, я часто использую только iOS симулятор для тестирования всех логических интерфейсов без опасений относительно совместимости с Android, хотя адаптация экрана под различные размеры все же необходима.
В некотором роде можно сказать, что Flutter больше похож на легкий игровой движок вроде Unity, но он предоставляет 2D компоненты.
Конечно, такой подход у Flutter имеет и недостатки, в частности, когда требуется использование компонентов платформы при гибридной разработке, затраты и опыт использования Flutter значительно увеличиваются, а здесь react-native имеет значительное преимущество.
После быстрой настройки окружения и базового понимания API Flutter, я считаю, что обучение этому фреймворку должно сосредоточиться на двух ключевых моментах: реактивном программировании и том, что находится за спиной каждого виджета.
Реактивное программирование должно быть знакомо любому фронтендеру, эту тему для фронтенд-разработчиков можно пропустить, поскольку реактивное программирование также известно как декларативное программирование, которое стало основным направлением развития фронтенд-разработки. Это также тренд в клиентских технологиях, таких как Jetpack Compose и SwiftUI.
Подобие между Jetpack Compose и Flutter точно удивит вас.
Что такое реактивное программирование? Проще говоря, это то, что вам не нужно самостоятельно обновлять интерфейсы; достаточно "объявить" интерфейс через код, установить связь между данными и интерфейсом, и при обновлении данных интерфейс автоматически обновится. С точки зрения кода, для нативного программирования в реактивной разработке отсутствуют XML-шаблоны для размещения, все размещение полностью выполняется с помощью кода, то есть вы видите то, что получаете, при этом вам не требуется использовать объекты интерфейса для присваивания значений и обновлений; вам просто нужно настроить отношения между данными и интерфейсом.Пример:
TextView
, получить этот объект через findViewById
и установить значение с помощью setText
;Widget Text
и передать данные, такие как data.title
, прямо этому Text
; когда данные меняются, содержимое отображаемого текста также обновляется;Для разработки на Android некоторые могут подумать, что это аналогично MVVM
и Data Binding
. Однако это немного отличается. Более наглядный пример можно взять из презентации босса "扔物线" на конференции Google I/O о Jetpack Compose, почему Data Binding
не является реактивной разработкой:
Потому что
Data Binding
(будь то конкретная библиотека или подход к программированию) не может обеспечить «объявление UI», другими словами, объявление UI — это более мощная форма данных, чем простое связывание данных. Например, в Compose вы можете не только связывать значения строк, но и использовать логические типы данных для управления существованием элементов интерфейса. Например, создайте ещё одну логическую переменную и используйте её для управления отображением вашего текста:
> Обратите внимание, когда
show
сначала равно true
, а затем становится false
, это не значит, что было установлено setVisibility(GONE)
. Вместо этого Text()
исчезает из кода интерфейса, каждый раз, когда данные изменяются, обновление интерфейса происходит так, будто он был закрыт и снова открыт с новыми данными. Это называется декларативной моделью UI, которую нельзя реализовать с помощью Data Binding.
Конечно, Compose не реинициализирует весь интерфейс заново, он обновляет только те части, которые действительно требуют обновления, таким образом гарантируя, что автоматическое обновление интерфейса будет столь же эффективным, как и ручное обновление. В Flutter ситуация аналогична: когда вы используете такие значения как
true
иfalse
для управления расположением, это непосредственно влияет на структуру дерева виджетов и более глубокую логику рендера. Поэтому людям, переходящим с Android на Flutter, следует привыкнуть к этому подходу и "отказаться" от идеи сохранения или удерживания какого-либо компонента интерфейса после получения данных. Кроме того, в Flutter удерживание какого-либо виджета для его модификации большинством случаев не имеет смысла, и это тема нашего следующего обсуждения. #### 2. Внешняя оболочка виджетаВ Flutter всё являетсяWidget
,Widget
является неизменяемым (immutable), каждыйWidget
представляет состояние кадра.
Понимание этого очень важно, так как это часто вызывает путаницу у начинающих разработчиков Flutter, поскольку все отображаемые интерфейсы в коде представлены через Widget
.
Widget
является неизменяемым, что означает, что при изменении страницы Widget
обязательно будет перестроен. Неподвижное состояние Widget
представляет собой статичное изображение кадра, а когда изображение меняется, соответствующий Widget
также изменится.
Рассмотрим пример, который я часто привожу. Ниже показана реализация TestWidget
, которая принимает входные параметры title
и count
и отображает их с помощью Text
. При этом если значение count
больше 99, то отображается только 99.
/// Предупреждение
/// Этот класс помечен как '@immutable', но один или более его экземплярных полей не являются final
class TestWidget extends StatelessWidget {
final String title;
int count;
TestWidget({this.title, this.count});
@override
Widget build(BuildContext context) {
this.count = (count > 99) ? 99 : count;
return Container(
child: new Text("$title $count"),
);
}
}
Этот код выглядит корректным и работает правильно, однако компилятор выдает предупреждение "This class is marked as '@immutable', but one or more of its instance fields are not final", потому что внутренний член count
класса TestWidget
не объявлен как final
, что может вызвать путаницу на уровне кода.> Поскольку было указано, что Widget
является immutable
, каждое изменение приведёт к тому, что Widget
будет перестроен заново. Это значит, что член count
внутри TestWidget
фактически не сохраняется и не используется повторно.
Как показано выше, если член count
не объявлен как final
, теоретически можно дважды модифицировать и переопределить значение count
, создающую иллюзию того, что он сохраняется и используется повторно внутри TestWidget
, что может вызывать путаницу, особенно если рассматривать widget.count
в некоторых случаях. Поэтому использование ключевого слова final
помогает понять логику неизменяемости Widget
.
Если заменить StatelessWidget
на StatefulWidget
и переместить метод build
внутрь State
, то член count
внутри State
сможет сохраняться между кадрами. Основной идеей здесь является понимание неизменяемого свойства Widget
, а также осознание того, как можно использовать State
для хранения и восстановления данных между Widgets
.
Это связано с ещё одним важным понятием в Flutter — тем, что находится за кадром Widget
. В действительности Widget
в Flutter не являются реальными компонентами управления. Widget
скорее всего выступает в роли конфигурационного файла, тогда как Element
, RenderObject
и Layer
выполняют реальные операции.
Основное внимание следует уделять
Element
,RenderObject
иLayer
.Примером может служить следующий код, гдеText
с именемtestUseAll
используется трижды на одной странице, и при этом код корректно отображается. Если бы это был настоящийView
, он не смог бы использоваться одновременно несколькими местами на одной странице.
В Flutter Widget
представляет собой конфигурацию, которая сообщает Flutter, как именно должна происходить отрисовка. Widget
проходит через Element
, RenderObject
и даже до уровня Layer
, прежде чем происходит окончательная отрисовка. Поэтому Widget
может быть помечен как @immutable
, так как каждый раз при обновлении состояния он будет перестраиваться заново.
Поэтому вернёмся к первому вопросу: "Наследование в Flutter очень сложное?" Да, настройки Flutter действительно приводят к тому, что есть объективные факты наследования, но когда вы начинаете рассматривать Widget
как конфигурационный файл, вы можете лучше организовать свой код, например, Container
в Flutter является абстрактным шаблоном конфигурации.
Изучите
Container
, чтобы узнать первый шаг организации логики кода в Flutter.Кроме того, посколькуWidget
не выполняет реальных операций, то на самом деле "наслоение" не является наслоениемViews
, поэтому обычно наслоениеWidgets
не вызывает проблем производительности, так как они не выполняют реальные операции, и наслоение не приводит к значительному снижению производительности. Приведу пример: когда вы загружаете несколькоWidget
, то при первой загрузке создаетсяElement
. В последующих загрузкахElement
хранит в себеWidget
иRenderObject
.Кратко говоря, обычно изменения экрана представляют собой обновление состоянияWidget
, которое затем передаетсяRenderObject
. В Flutter состояние (State
) можно сохранять между кадрами благодаря тому, что оно находится внутриElement
. Это позволяет сохранять данные между различнымиWidget
.
Таким образом, вложенность
Widget
обычно не приводит к проблемам производительности, так как каждыйWidget
представляет собой состояние одного кадра. Можно сказать, что это "конфигурационная информация", которая отражает текущее состояние экрана. За каждымWidget
скрывается вложение таких компонентов, какPadding
иAlign
, которые в конечном итоге сводятся к простому смещению на канвасе.
Поэтому важно понимать Widget
: они не являются настоящими представлениями (Views
), а лишь содержат конфигурацию. Только осознав это, вы сможете лучше понять широкие возможности Flutter, такие как:
Element
;Widget
, следует обратиться к его RenderObject
;Layers
;Widget
имеют RenderObject
? Какова связь между Widget
, Element
, RenderObject
и Layer
?**Это именно те вещи, которые вам нужно понять и объединить, чтобы стать экспертом в Flutter. Когда вы поймёте сложную логическую основу за Widget
, вы заметите, насколько просто Flutter может решать задачи создания сложных компонентов. Возможности Canvas
действительно великолепны.**Конечно, подробное рассмотрение этих тем требует более глубокого анализа, чем можно было бы сделать здесь. Эти вопросы рассматриваются в третьей и четвертой главах моей книги "Практическое руководство по разработке приложений на Flutter", где они представлены как ключевые моменты всего учебника. Эта информация не устареет даже при переходе на новые версии Flutter.А это уже реклама?
И последнее, о недостатках Flutter. Никакой фреймворк не идеален, если бы он был совершенен, наша конкурентоспособность была бы ниже.
Именно поэтому разработка для Android и iOS была очень популярна вначале, но теперь рынок клиентских приложений становится более рациональным. Этот сектор стал намного зрелее, и потому стало больше конкуренции. Действительно, я всегда считал, что использование фреймворков само по себе не приносит особой ценности, а решение проблем, возникающих при использовании этих фреймворков, является нашей уникальной ценностью.
У Flutter тоже есть свои проблемы, такие как:- Проблемы с WebView
: Уникальная система UI Flutter требует специального подхода для интеграции компонентов, таких как WebView
, MapView
. Это приводит к различным проблемам производительности, работы клавиатуры, полей ввода и других технических вопросов после интеграции. Подробнее можно прочитать здесь: «Глубокий анализ Hybrid Composition» и «Проблемы с Android PlatformView и клавиатурой».- Обработка и загрузка изображений: Способность Flutter обрабатывать и загружать изображения явно недостаточна, особенно когда дело доходит до загрузки одного большого изображения или отображения множества изображений. Flutter часто сталкивается с проблемами переполнения памяти и частичного переполнения GPU. Эти проблемы сложны в решении, и если требуется использование native платформы для решения, это требует использования внешних текстур, что увеличивает стоимость поддержки.
flutter_boost
и flutter_thrio
не полностью решают основные проблемы гибридного развития, поэтому Flutter также испытывает трудности в этом отношении.Однако фактические вопросы, связанные с Flutter, зачастую имеют мало отношения к самому фреймворку, например:
flutter doctor
процесс зависает»flutter run
происходит ошибка»flutter pub get
почему выдается сообщение о неправильной версии Dart?»Хотя Flutter имеет свои недостатки, но в целом он остаётся самым подходящим UI фреймворком для меня на данном этапе.### В конце
Долго не писал такого объёма материала, обычно людей, которые читают такие длинные статьи до конца, немного. Но надеюсь, что эта статья поможет вам более полно понять Flutter или направит вас в правильном направлении при обучении этому фреймворку. В заключение хочу процитировать слова одного известного специалиста:
"Технологии, которые могут широко использоваться в промышленности, не требуют слишком высокого IQ, иначе они бы не смогли масштабироваться. Некоторым программистам следует отказаться от своего беспочвенного самомнения."
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )