Автор Темитопе Ойеделе✏️
Flutter — это набор инструментов пользовательского интерфейса от Google, который позволяет пользователям создавать нативно скомпилированные приложения для веб, настольных и мобильных устройств. Он также встроен в единую кодовую базу, то есть, хотя Flutter охватывает различные платформы, он все равно происходит из единой кодовой базы.
И поскольку это инструментарий пользовательского интерфейса, он использует части пользовательского интерфейса, чтобы дать пользователям возможность создавать пользовательские интерфейсы без необходимости создавать их определенным образом при структурировании приложений; Flutter создает пользовательские интерфейсы как код.
В этой статье мы рассмотрим архитектуру Flutter и то, из чего она состоит. Одним из преимуществ изучения архитектуры Flutter является то, что она помогает при структурировании приложения, например, при обновлении состояния, построении виджетов или экранов, а также упрощает сопровождение приложения.
Знание архитектуры Flutter позволяет нам ознакомиться с основными концепциями, которые формируют архитектуру и помогают в быстрой компиляции и процессе модификации кода.
Слои в архитектуре Flutter
Как и любое другое нативное приложение, Flutter состоит из трех слоев:
- Embedder (самый нижний слой)
- Движок
- Framework (самый верхний слой)
Слои Flutter
Мы подробно рассмотрим каждый из них в следующих разделах.
Уровень эмбеддера
Точка входа обеспечивается встраиваемым модулем, специфичным для конкретной платформы, который координируется с базовой операционной системой для доступа к таким сервисам, как доступность, поверхности рендеринга и ввод.
Встраиваемый модуль написан на языке, специфичном для конкретной платформы, например, Java и C++ для Android, Objective-C/Objective-C++ для iOS и macOS, C++ для Windows и Linux.
Код Flutter может быть встроен в существующее приложение в виде модуля или в виде полного содержимого приложения с помощью embedder.
Слой движка
Уровень движка написан на C/C++, и он заботится о вводе, выводе, сетевых запросах и обрабатывает сложный перевод рендеринга, когда нужно отрисовать кадр.
Flutter использует Skia
в качестве движка рендеринга, и он раскрывается во фреймворке Flutter через [dart : ui]
, который оборачивает основной код C++ в классы Dart.
Слой фреймворка
Уровень фреймворка — это та часть, где большинство разработчиков могут взаимодействовать с Flutter. Фреймворк Flutter представляет собой реактивный и современный фреймворк, написанный на языке Dart.
Внутри слоя фреймворка есть следующее:
- Рендеринг
- Виджеты
- Материал и купертино
В нем также есть основополагающие классы и сервисы строительных блоков, такие как анимация, рисование и жесты, которые необходимы для написания приложения Flutter.
Мы рассмотрим все эти аспекты более подробно в следующих разделах.
Виджеты Flutter
Первое, что следует отметить, — это то, что во Flutter все является виджетом. Виджет — это просто инструкция, которую вы размещаете в своем коде, и они являются основными строительными блоками пользовательского интерфейса приложения Flutter. Виджеты указывают, как их текущая конфигурация и состояние должны отображаться на экране.
Когда состояние виджета меняется, он перестраивает свое описание, которое фреймворк сравнивает с предыдущим описанием, чтобы увидеть, что изменилось в базовом дереве рендеринга для перехода из одного состояния в другое.
Виджет может быть в виде кнопки, изображения, иконки или макета, а размещение виджетов вместе создает дерево виджетов.
Дерево виджетов
Дерево виджетов — это бесконечная цепочка родительских и дочерних виджетов, которая создает визуальный макет на экране.
Макеты создаются путем вложения виджетов друг в друга в иерархии «родитель-ребенок». Поначалу дерево виджетов «родитель-ребенок» может показаться пугающим, но с практикой вы сможете освоить его.
Например, представьте себе приложение с пользовательским интерфейсом входа в систему, как на рисунке ниже:
Дерево виджетов для этого экрана входа в систему можно проиллюстрировать с помощью следующей диаграммы:
Виджет root
является главным виджетом. Мы также можем назвать его родителем всех остальных виджетов в дереве. Затем у нас есть виджет material
, который является темой нашего приложения, за ним следует scaffold
, который является телом нашего приложения.
Здесь дочерний виджет имеет форму виджета container
с виджетом column
в качестве дочернего. Дочерние элементы виджета column
включают image
, textfield
, raisedbutton
, container
и column
.
Вот как это может быть структурировано в нашем коде, начиная с scaffold
:
scaffold(
body: Container(
child: Column(
children: [
Image(),
TextField(),
TextField(),
Container(
child: Column(
children: [
RaisedButton(),
RaisedButton(),
]
),
),
RaisedButton()
]
),
),
);
Во Flutter виджеты можно разделить на такие категории, как виджеты компоновки, виджеты, специфичные для платформы, платформонезависимые виджеты и виджеты поддержания состояния.
Процесс объединения виджетов вместе называется композицией. Это означает, что мы можем объединить несколько простых виджетов интерфейса, каждый из которых выполняет одну конкретную работу, создавая мощные эффекты.
Жесты
Взаимодействие с виджетами Flutter возможно благодаря уникальному виджету под названием [GestureDetector]
. GestureDetector
— это невидимый виджет, который может регистрировать события пользователя с его дочерними виджетами, такие как касание и перетаскивание.
Используя виджет GestureDetector
, Flutter обеспечивает отличную поддержку всех типов жестов, и мы можем включить интерактивную функцию в существующий виджет. Давайте создадим простое приложение, чтобы посмотреть, как это работает, используя событие ontap()
.
import 'package:flutter/material.dart';
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 Application', theme: ThemeData(
primarySwatch: Colors.blue,),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
MyHomePageState createState() => new MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('gesture'),
centerTitle: true,
),
body: new Center(child: GestureDetector(
onTap: () {
print('Box Clicked');
},
child: Container(
height: 60.0,
width: 120.0,
padding: EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: Colors.blueGrey,
borderRadius: BorderRadius.circular(15.0),
),
child: Center(child: Text('Click Me')),
)
)),
);
}
}
Это выведет следующий экран; когда вы нажмете на кнопку, вывод отобразится на консоли:
Состояния виджета
Состояние — это поведение приложения в любой момент времени. Считайте, что это информация о виджете при его первом создании, определяющая свойства этого виджета. Но эта информация может меняться в течение жизни виджета.
Для построения пользовательского интерфейса во Flutter мы используем два типа виджетов:
- Виджеты без состояния
- Виджеты без состояния
Нестационарные виджеты
Виджеты без состояния статичны, что означает, что после инициализации они не меняются. Виджеты Stateless не хранят никакой информации в реальном времени, не имеют состояния, которым можно управлять, и не имеют прямого взаимодействия с приложением. Icons
, IconButton
и Text
являются примерами виджетов без состояния.
Также обратите внимание, что виджет без статики переопределяет build()
и возвращает виджет. Мы используем виджет stateless, когда пользовательский интерфейс зависит от информации, содержащейся в самом объекте:
import 'package:flutter/material.dart';
void main() => runApp(OurApp());
class OurApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container();
}
}
Здесь имя этого виджета без статики OurApp
. Функция build переопределяется и принимает BuildContext в качестве параметра, который возвращает виджет. Поэтому мы видим, что возвращаемый тип метода build — виджет.
Это место, где вы можете спроектировать пользовательский интерфейс экрана, который не имеет состояния.
Стативные виджеты
Виджеты stateful являются динамическими, то есть они могут меняться в зависимости от ситуации. Они способны хранить данные в реальном времени, и мы можем использовать эту информацию для обновления пользовательского интерфейса. TextField
, Slider
и Form
— все это примеры stateful виджетов.
Метод createState
переопределяется виджетом с состоянием, который возвращает состояние. Когда пользовательский интерфейс может меняться динамически, мы используем виджеты с состоянием.
Вот как устроен виджет с состоянием:
import 'package:flutter/material.dart';
void main() => runApp(OurApp());
class OurApp extends StatefulWidget {
@override
_OurAppState createState() => _OurAppState();
}
class _OurAppState extends State<OurApp> {
@override
Widget build(BuildContext context) {
return Container();
}
}
Имя виджета снова OurApp
, но теперь он переопределяет метод createState
, а не метод build
, который возвращает экземпляр класса _OurAppState
. Этот класс затем расширяется от State<>
, который принимает OurApp
в качестве входного шаблона.
Метод _OurAppState
теперь переопределяет функцию build
и возвращает виджет. Именно здесь мы можем определить пользовательский интерфейс приложения, который зависит от состояния. Поскольку это виджет с состоянием, вы можете вызывать метод build
любое количество раз, что перерисовывает виджеты на экране.
Когда состояние виджета меняется, объект state вызывает setState()
, сообщая фреймворку о необходимости перерисовать виджет.
Управление состоянием
Управление состоянием приложения — один из самых важных и необходимых процессов, поскольку он помогает разработчикам централизовать все состояния различных элементов управления пользовательского интерфейса для управления потоком данных во всем приложении.
Мы также можем увидеть, как состояние управляется и передается в системе и как управление состоянием контролирует состояние одного или нескольких элементов управления пользовательского интерфейса, таких как текстовые поля, радиокнопки, флажки, выпадающие окна, переключатели и формы.
Во Flutter есть два вида состояния:
- Эфемерное/локальное состояние
- состояние приложения
Состояния во Flutter
Эфемерное состояние
Эфемерное состояние представляет собой локальное состояние одного виджета. Это может быть текущая страница в PageView
, текущий прогресс анимации или любое другое текущее локальное состояние пользовательского интерфейса.
Нет необходимости применять какую-либо стратегию управления состоянием с этим состоянием, поскольку оно управляемое и не слишком сложное. Оно действительно простое и базовое; однако из-за этого оно не идеально подходит для больших программ и усложняет обслуживание состояния.
Состояние приложения
Состояние приложения — это состояние, которое разделяется несколькими разделами приложения и используется во время сеанса пользователя. Состояние приложения включает в себя такую информацию, как информация о входе пользователя в систему, уведомления в приложении для социальных сетей или электронной коммерции, а также предпочтения пользователя.
Вы должны выбрать подход к управлению состоянием для обработки состояния приложения. Хотя существует множество подходов к достижению состояния приложения, выбор в значительной степени зависит от сложности и характера приложения.
Одним из подходов является использование inheritedWidget
. Когда приложение становится больше и дерево виджетов усложняется, на помощь может прийти inheritedWidget
.
InheritedWidget
— это базовый класс, который позволяет классам, расширяющим его, эффективно распространять информацию вниз по дереву. По сути, он работает, уведомляя зарегистрированные контексты сборки о любых изменениях. Другие альтернативные подходы включают:
- Провайдер
- BLoC/RxDart
- Riverpod
- Redux
- GetX
- Getit
- MobX
- SetState
Процесс рендеринга во Flutter
Процесс рендеринга во Flutter — это, по сути, превращение виджетов в пиксели. Это работа мультиконвейера рендеринга Flutter. Flutter обрабатывает входные данные, запускает анимацию, строит дерево виджетов, выстраивает объекты рендеринга, раскрашивает объекты рендеринга и компилирует все в единое изображение.
Всякий раз, когда часть дерева виджетов получает пользовательский ввод, анимацию или любые другие изменения, Flutter переходит в фазу сборки. В этой фазе фреймворк реконструирует дерево виджетов, вызывая необходимые методы сборки.
Фаза build()
обновляет дерево элементов, которое обновляет дерево объектов рендеринга. Объекты рендеринга отвечают за компоновку и рисование. Во время фазы компоновки Flutter проходит по дереву объектов рендеринга, передавая ограничения.
После завершения фазы компоновки он переходит к фазе рисования. Здесь Flutter вызывает метод paint
для всех объектов рендеринга:
@overide
void paint(PaintingContext context, Offset offset) {
final paint =ui.paint()..color = const Color(0xff0000ff);
final rect = Rect.fromLTWH(0, 0, size.width, size.height);
context.canvas.drawReact(rect, paint);
}
Объекты рендеринга рисуют под холстом, используя такие методы, как drawRect
и drawLine
. После завершения рисования переходят к фазе композитинга. Это означает объединение изображений в единую картину.
Затем движок получает дерево компоновки, содержащее инструкции рисования, и преобразует их в пиксели. Этот процесс называется Растеризация
.
Рендеринг через компоновку, рисование и композицию
Как Flutter интегрируется с другим кодом?
Внедряете ли вы собственные элементы управления в проект Flutter или встраиваете Flutter в существующее приложение, Flutter предоставляет широкий спектр методов взаимодействия или протоколов.
С помощью канала платформы Flutter позволяет интегрировать пользовательский код в мобильные и настольные приложения и обеспечивает простую технику взаимодействия между платформенным кодом вашего приложения и вашим кодом Dart.
Создав общий канал, вы можете отправлять и получать сообщения между компонентами платформы, построенными на таких языках, как Swift, Kotlin и Dart. Данные сериализуются из типа Dart в стандартный формат, а затем десериализуются в эквивалентное представление Kotlin или Swift.
Преимущества использования Flutter
Более быстрое кодирование
Обычно разработчики iOS и Android должны создавать код и ждать, пока он скомпилируется и загрузится на устройство, прежде чем они смогут увидеть изменения. Однако благодаря горячей перезагрузке Flutter они могут тестировать изменения мгновенно или без задержек.
Это означает, что ваша команда разработчиков может быстро внести изменения и увидеть результаты. Именно поэтому разработка приложений на Flutter занимает меньше времени, чем на других платформах.
Родной движок рендеринга Flutter
Родной движок рендеринга позволяет создавать пользовательские интерфейсы, идентичные на всех платформах. Обычно процесс рендеринга порождает дополнительные проблемы в слое отображения свойств виджета платформы и синхронизации данных виджета фреймворка.
И Flutter делает все, чтобы облегчить этот процесс. Поскольку он использует Skia, ему не требуется конкретный компонент пользовательского интерфейса для рендеринга, достаточно лишь холста для рисования. Какое огромное преимущество использования Flutter!
Одна кодовая база для всех платформ
Благодаря возможности повторного использования кода Flutter, вы можете разработать единую кодовую базу и использовать ее не только для мобильных Android и iOS, но и для веб-приложений, настольных и других приложений.
Стоит также отметить, что Flutter только что выпустил большое обновление поддержки Windows, которое значительно сокращает время разработки, устраняет затраты и позволяет быстрее развернуть продукт.
Отличная производительность
Приложения Flutter работают на уровне, сравнимом с нативными приложениями, и выигрывают у других кроссплатформенных технологий.
В основном это связано с тем, что Flutter — единственный мобильный SDK, который не полагается на мост (JavaScript или web view) для связи между приложением и платформой. Как следствие, вы получаете более отзывчивое приложение с привлекательной, быстрой анимацией и меньшим количеством проблем с производительностью.
Сокращение времени выхода на рынок
Благодаря всем возможностям Flutter, которые упрощают разработку, вы можете быстрее выпустить свое приложение и одновременно отправлять новые функции и улучшения на iOS и Android.
Сообщество Flutter
Как платформа с открытым исходным кодом, Flutter является бесплатной для использования и имеет растущее сообщество, которое вносит свой вклад в его превосходную документацию и помогает разработчикам в решении проблем, с которыми они могут столкнуться.
Также существует множество видеороликов на YouTube для тех, кто заинтересован в изучении Flutter или совершенствовании своих навыков в мобильном UI-фреймворке Google.
Заключение
Структура нашего приложения и то, как организованы наши данные, может сделать разницу между отличным и обычным пользовательским опытом. Понимание всех основных концепций очень помогает при создании мобильных приложений на Flutter. Поделитесь, пожалуйста, было ли это полезно?
LogRocket: Полная видимость ваших веб-приложений
LogRocket — это решение для мониторинга фронтенд-приложений, которое позволяет воспроизводить проблемы так, как будто они происходят в вашем собственном браузере. Вместо того чтобы гадать, почему возникают ошибки, или просить пользователей предоставить скриншоты и дампы журналов, LogRocket позволяет воспроизвести сессию, чтобы быстро понять, что пошло не так. Он отлично работает с любым приложением, независимо от фреймворка, и имеет плагины для регистрации дополнительного контекста из Redux, Vuex и @ngrx/store.
Помимо регистрации действий и состояния Redux, LogRocket записывает журналы консоли, ошибки JavaScript, трассировку стека, сетевые запросы/ответы с заголовками + телами, метаданные браузера и пользовательские журналы. Он также использует DOM для записи HTML и CSS на странице, воссоздавая пиксельно идеальные видео даже самых сложных одностраничных и мобильных приложений.
Попробуйте бесплатно.