Как перейти на Flutter? Что нужно знать разработчикам на iOS и Android

Логотип компании
Как перейти на Flutter? Что нужно знать разработчикам на iOS и Android
Мир программирования все больше захватывают кросс-платформы. Такие технологии, как Flutter, позволяют создавать приложения одновременно и для iOS, и для Android. Происходит это не без своих ограничений, конечно же, однако для многих компаний использование кросс-платформ становится отличным решением. Возникает закономерный вопрос: как специалистам в области создания приложения под iOS или Android быстро освоиться в новом пространстве?

Понимание Основ Flutter

Для начала важно понять основные принципы Flutter, который использует язык программирования Dart, сочетающий элементы OOP и функционального программирования. Flutter основан на декларативном UI, где вы описываете, как должен выглядеть ваш интерфейс, а не шаги его создания.

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

Dart: язык Flutter

Dart — это оптимизированный под UI язык для создания приложений на любой платформе. Синтаксис Dart будет вам знаком, если вы использовали язык в стиле C. Он поддерживает объектно ориентированные концепции и является строго статически типизированным.

Dart компилируется в машинный код ARM и x64 для мобильных устройств, desktops и бэкенда. Он также компилируется в JavaScript для веба. Эта гибкость позволяет запускать код Dart различными способами, что очень важно для кросс-платформенности Flutter.

Декларативный UI во Flutter

При переходе на Flutter одним из самых значительных изменений для разработчиков Android и iOS станет адаптация к декларативному фреймворку UI. Мы рассмотрим эту концепцию, сравним ее с императивным стилем и покажем, как она может улучшить ваш процесс разработки.

Декларативный подход означает, что вы описываете, как должен выглядеть UI в том или ином состоянии приложения, а фреймворк берет на себя заботу о рендеринге UI и переходе между состояниями. Это отличается от императивного стиля, используемого в Android (где вы напрямую управляете элементами пользовательского интерфейса) и iOS (особенно до появления SwiftUI).

Во Flutter вы создаете UI с помощью виджетов. Каждый виджет — это неизменяемое объявление части UI. Виджеты можно рассматривать как кирпичики, из которых собирается UI вашего приложения — по аналогии с Views в Android и ViewControllers в iOS.

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

Во Flutter вы создаете UI путем вложения виджетов друг в друга. Это может напомнить вам XML-макеты Android, но предлагает больше возможностей и гибкости благодаря широкому спектру настраиваемых виджетов и возможности создавать свои собственные.

Декларативный UI Flutter на практике

Давайте рассмотрим практический пример. Создадим экран, на котором отображается список элементов. Для этого возьмем виджет ListView для создания списка, а для каждого элемента определим виджет, который описывает, как должен выглядеть элемент в зависимости от его текущего состояния.

ListView.builder(
   itemCount: items.length,
   itemBuilder: (context, index) {
           return ListTile(
            title: Text(items[index].name),
            subtitle: Text(items[index].description),
            onTap: () => setState(() {
            items[index].favorite = !items[index].favorite;
            }),
          );
   },
)

В этом фрагменте ListView.builder автоматически перестраивает каждый виджет ListTile при изменении состояния элемента — например, когда элемент помечается как избранный.

Сравнение с Android и iOS

Android: традиционно разработчики Android использовали XML для определения макета и код на Java/Kotlin для работы с элементами пользовательского интерфейса. Во Flutter и макет, и логика объединены в рамках одного языка (Dart), что упрощает процесс разработки.

iOS: до появления SwiftUI разработчики iOS в значительной степени полагались на Storyboards и императивный код во ViewControllers. В SwiftUI появился более декларативный подход, который в большей степени соответствует Flutter.

Создание простого виджета в Flutter и его аналог в Android/iOS:

Flutter:

class MyWidget extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
           return Center(
                child: Text('Привет, Flutter!'),
          );
   }
}

 

Android (Kotlin):

val textView = TextView(this)
textView.text = "Hello Custom Views"
textView.gravity = Gravity.CENTER
setContentView(textView)

 

iOS (Swift):

let label = UILabel()
label.text = "Привет, iOS!"
label.textAlignment = .center
view.addSubview(label)

Архитектура Flutter и виджеты

Переход на Flutter требует понимания его архитектуры и центральной роли виджетов. Мы подробно рассмотрим эти понятия и поможем разработчикам Android и iOS разобраться, чем дизайн Flutter отличается от привычного и как использовать его для эффективной разработки приложений.

Обзор архитектуры Flutter

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

Во Flutter виджеты — это базовые строительные блоки UI вашего приложения. В отличие от Views в Android и ViewControllers в iOS, виджеты во Flutter могут представлять как элемент пользовательского интерфейса (например, кнопку или текстовое поле), так и параметры макета (например, отступы или выравнивание). Приложение Flutter — это дерево виджетов. Корневой виджет содержит различные дочерние виджеты, и каждый из них может иметь свои собственные дочерние виджеты, создавая иерархию.

Жизненный цикл виджетов

Во Flutter есть два основных типа виджетов — Stateless и Stateful. Виджеты без состояния неизменяемы, то есть их свойства не могут меняться — все значения конечны. Stateful-виджеты хранят состояние, которое может меняться в течение жизни виджета.

Stateful-виджеты имеют жизненный цикл. Основные этапы жизненного цикла — создание (initState), обновление (setState), построение (build) и уничтожение (dispose). Управление этими этапами жизненного цикла очень важно для эффективного использования ресурсов.

Создание UI во Flutter

Для построения UI Flutter опирается на композицию. Вы создаете сложные виджеты, комбинируя более простые. Например, чтобы создать пользовательскую кнопку, вы можете скомпоновать виджет Material с виджетом GestureDetector, а не расширять класс Button.

Давайте рассмотрим пример создания пользовательского виджета карточки:

Card(
   child: Column(
      children: [
            Image.network('path/to/image'),
            Text('Title'),
            Text('Description'),
      ],
  ),
)

Здесь Card, Column, Image и Text — виджеты, каждый из которых выполняет свою функцию в создании пользовательского интерфейса.

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

Во Flutter управление состоянием имеет решающее значение для обеспечения эффективности вашего приложения. Рассмотрим ключевые концепции и стратегии управления состояниями во Flutter и сравним их с привычными практиками в Android и iOS.

Что такое состояние? Во Flutter под состоянием понимается информация, которая может считываться синхронно при создании виджета и может меняться в течение его жизни. UI реагирует на изменение состояния.

Flutter классифицирует состояние на два основных типа: локальное состояние и состояние приложения. Локальное состояние относится к отдельному виджету (например, текущая страница в PageView), в то время как состояние приложения является глобальным и разделяется между несколькими частями вашего приложения (например, аутентификация пользователя).

Подходы к управлению состояниями во Flutter

Для локального управления состоянием часто используют StatefulWidget и управляют состоянием внутри самого виджета с помощью setState. Этот подход прост и хорошо пригоден для небольших сценариев применения.

Для более сложных приложений вам могут понадобиться более масштабируемые решения. Flutter предлагает несколько вариантов для этого:

Provider: популярный и простой способ управления состоянием приложения, он позволяет виджетам отслеживать изменения и перестраиваться соответствующим образом.

Паттерн Bloc (или Business Logic Component): Bloc отделяет бизнес-логику от слоя UI и отлично подходит для более сложного управления состоянием.

Сравнение с Android и iOS

Если вы пришли из Android, вы, возможно, знакомы с ViewModel и LiveData для управления состоянием.

В iOS, особенно с появлением SwiftUI, управление состоянием имеет некоторые параллели с Flutter. Использование состояния и bindings в SwiftUI предлагает похожий декларативный подход.

Рассмотрим простое приложение-счетчик. Мы можем управлять состоянием в StatefulWidget:

class CounterWidget extends StatefulWidget {
   @override
   _CounterWidgetState createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State {
   int _counter = 0;

   void _incrementCounter() {
      setState(() {
         _counter++;
      });
   }

   @override
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title: Text('Counter')),
         body: Center(
           child: Text('You have pushed the button $_counter times.'),
        ),
         floatingActionButton: FloatingActionButton(
            onPressed: _incrementCounter,
            tooltip: 'Increment',
           child: Icon(Icons.add),
        ),
     );
   }
}

В этом примере _counter — это состояние, и оно управляется локально внутри виджета CounterWidget. Метод setState предназначен для обновления состояния и запуска перестройки виджета.

State management — это, наверное, наиболее поляризующая тема в комьюнити Flutter, разработчики чаще всего спорят именно по поводу правильного подхода к управлению состоянием и выбору вспомогательных библиотек.

Управление состоянием в Flutter отличается от традиционных подходов в Android и iOS. В Android и iOS используются разновидности ViewBinding, во Flutter самым популярным подходом является BLoC (business logic component) и одноименная библиотека bloc. Из всех архитектурных паттернов (MVVM, MVI, MVC, MVP) BLoC больше всего напоминает MVVM. BLoC состоит из событий, состояния и блока. Бизнес-логика выделяется в блок, в UI выбрасываются события и в качестве реакции на эти события, блок меняет свое состояние. UI в свою очередь подписан на это состояние и меняется при его изменении.

Основы BLoC

BLoC — это паттерн, разработанный для облегчения управления состоянием и бизнес-логикой в приложениях Flutter. Он основан на принципах потоковой обработки событий (stream) и реактивного программирования. BLoC помогает разделить бизнес-логику от пользовательского интерфейса.

Рассмотрим простой пример в виде счетчика и создадим BLoC для управления его состоянием. Определим события (events), состояния (states) и bloc, который будет обрабатывать состояния.

// События (counter_event.dart):
abstract class CounterEvent {}

class Increment extends CounterEvent {}

class Decrement extends CounterEvent {}

 

// Состояние (counter_state.dart)
class CounterState {
   final int counterValue;
   CounterState({required this.counterValue});
}

// counter_bloc.dart:
import 'package:flutter_bloc/flutter_bloc.dart';

class CounterBloc extends Bloc {
   CounterBloc() : super(CounterState(counterValue: 0)) {
      on((event, emit) {
         emit(CounterState(counterValue: state.counterValue + 1));
      });
      on((event, emit) {
         emit(CounterState(counterValue: state.counterValue - 1));
      });
   }
}

Наконец, интегрируем BLoC в наш пользовательский интерфейс.

class CounterWidget extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title: Text('Counter')),
         body: BlocBuilder(
            builder: (context, state) {
                return Center(
                   child: Text('Counter Value: ${state.counterValue}'),
                );
            },
        ),
         floatingActionButton: Column(
            mainAxisAlignment: MainAxisAlignment.end,
            children: [
                FloatingActionButton(
                   onPressed: () => context.read().add(Increment()),
                   tooltip: 'Increment',
                   child: Icon(Icons.add),
                ),
              SizedBox(height: 10),
                FloatingActionButton(
                   onPressed: () => context.read().add(Decrement()),
                   tooltip: 'Decrement',
                   child: Icon(Icons.remove),
                ),
              ],
        ),
     );
   }
}

BLoC обрабатывает события увеличения и уменьшения счетчика, обновляя состояние и отображая его в UI. Этот подход обеспечивает четкое разделение логики и представления, делает код более понятным и поддерживаемым.

Интеграция в существующее приложение

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

Давайте рассмотрим сценарий, в котором вы хотите интегрировать экран Flutter в существующее приложение для Android.

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

Используйте FlutterActivity или FlutterFragment для запуска экранов Flutter из вашего родного кода Android. Например, вы можете запускать FlutterActivity, когда пользователь нажимает на кнопку в вашем приложении для Android.

// Пример запуска экрана Flutter из Android

startActivity(
     FlutterActivity.createDefaultIntent(this)

);

Если вам нужно отправлять данные в модуль Flutter и из него, можно настроить Platform Channels. Это обеспечит двунаправленную связь между Flutter и нативной частью вашего приложения.

Детальные отличия Flutter глазами Андроид-разработчика

"View vs виджет": в Android все, что находится на экране, является view, а во Flutter — виджетом. Виджеты во Flutter легкие и неизменяемые, что отличается от меняющихся представлений в Android.

Обновление виджетов: в отличие от изменяющихся view в Android, виджеты Flutter неизменяемы. Вы работаете с состоянием виджета, используя виджеты Stateful и Stateless.

Макеты: Flutter не применяет XML для макетов, как Android; вместо этого он использует дерево виджетов.

Анимация и рисование: Flutter предоставляет богатый набор библиотек анимации и API Canvas, знакомый разработчикам Android.

Кастомные виджеты: создание собственных виджетов во Flutter подразумевает компоновку более мелких виджетов, а не создание подклассов.

Навигация: Flutter управляет навигацией с помощью Navigator и Routes, которые не связаны напрямую с Intent в Android.

Детальные отличия Flutter глазами iOS-разработчика

Views и виджеты: SwiftUI использует view, настроенные с помощью модификаторов, в то время как Flutter применяет виджеты как для компонентов UI, так и для их свойств.

Процесс layout: процесс создания layout в SwiftUI основан на коммуникации по размеру представлений между родителями и детьми, в то время как Flutter использует ограничения, передаваемые от родительских виджетов к дочерним.

Система дизайна: Flutter поддерживает различные системы дизайна, включая Material Design и Cupertino.

Запуск приложения: в SwiftUI для запуска приложения используются структуры App и View, а во Flutter — функция runApp с виджетом.

Кнопки и компоненты: Flutter предоставляет различные предварительно стилизованные кнопки, например CupertinoButton, аналогичную структуре Button в SwiftUI.

Выравнивание компонентов: SwiftUI использует HStack и VStack для выравнивания. Во Flutter это достигается с помощью виджетов Row и Column.

Списки и сетки: ListView и GridView во Flutter похожи на List и Grid в SwiftUI, но имеют некоторые различия в реализации.

Реализация скролла: Flutter использует SingleChildScrollView для создания пользовательских компонентов скролла, сравнимых с ScrollView в SwiftUI.

Управление состоянием: Flutter использует StatefulWidget и класс State для управления состоянием, аналогично @State и ObservableObject в SwiftUI.

Анимации: и SwiftUI, и Flutter поддерживают неявные и явные анимации, но их реализация различается. Flutter использует для анимации такие виджеты, как AnimatedFoo и FooTransition.

Рисование на экране: возможности рисования во Flutter основаны на классе Canvas, с CustomPaint и CustomPainter для рисования на экране, аналогично использованию CoreGraphics в SwiftUI.

Навигация: Flutter управляет навигацией с помощью Navigator и Routes, которые не связаны напрямую с механизмами навигации iOS.

Язык Dart и его отличия

Погрузиться в подробности языка можно на официальном сайте. Здесь представлен обзор ключевых различий между Dart и Swift/Kotlin:

Kotlin vs. Dart

1. Система типов

Kotlin статически типизирован, а для совместимости с JavaScript предлагает dynamic-тип в Kotlin/JS.

Dart также статически типизирован и поддерживает полную null-safety. Для большей гибкости Dart позволяет использовать тип `dynamic`, который является статическим, но может содержать любой тип во время выполнения.

2. Null safety

Kotlin использует null safety для разделения нулевых и ненулевых ссылок, устраняя `NullPointerException`.

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

3. Асинхронные операции

В Dart для асинхронных операций используется класс `Future` с ключевыми словами `async` и `await` вместо корутин.

Swift vs. Dart

1. Переменные и type inference

Оба языка поддерживают type inference — компилятор определяет тип переменной по правой части присваивания.

2. Мутабельные/немутабельные переменные

В Dart для неизменяемых переменных применяется `final`, а в Swift — `let`.

3. Функции

В Dart и Swift, как и объекты, функции можно передавать в качестве аргументов, сохранять как свойства или возвращать как результат. В Dart тип возврата предшествует имени метода, а в Swift в качестве суффикса используется нотация `-> T`.

4. Именованные и неименованные параметры

В Swift параметры именуются по умолчанию, а в Dart для определения именованных параметров требуются фигурные скобки (`{}`).

5. Опциональные параметры и параметры по умолчанию

Swift поддерживает опциональные параметры, присваивая им значение по умолчанию. Dart позволяет использовать как позиционные, так и именованные опциональные параметры.

6. Замыкания

Оба языка поддерживают замыкания. Dart применяет стрелочную нотацию (`=>`) для одиночных return, а Swift позволяет использовать `$0` для ссылки на первый аргумент в замыкании.

7. Кортежи

Swift поддерживает кортежи как способ группировки нескольких значений в одно составное значение. До недавнего времени Dart поддерживал кортежи с помощью отдельного пакета, но с последним обновлением появились Records.

8. Nullables и optionals

В Swift встроена поддержка optionals для объявления того, может объект иметь значение или нет. Грубо говоря, в Dart похожая система с nullable objects, только optionals проверяются во время компиляции, а nullables во время runtime.

Продвинутые темы Flutter

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

Кастомные виджеты и их переиспользование

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

Анимация и движение

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

Оптимизация производительности

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

Расширенное управление состояниями

Изучите продвинутые паттерны управления состояниями, такие как Bloc или Riverpod. Поймите их преимущества и примеры использования.

Работа с сетью и хранение данных

Узнайте, как эффективно обрабатывать сетевые запросы и отображать данные в вашем приложении (например, с помощью Dio). Изучите варианты сохранения данных локально на устройстве, скажем, с помощью sqflite, hive, objectbox.

Взаимодействие с кодом конкретной платформы

Стоит посмотреть на platform channels для более сложных взаимодействий с нативным кодом. Создайте функцию, требующую специфической для платформы реализации, например, доступ к датчикам устройства или применение специфических для платформы компонентов пользовательского интерфейса.

Unit, виджет и интеграционное тестирование

Узнайте, как писать и запускать разные типы тестов во Flutter. Добавьте тесты в свое приложение, чтобы обеспечить его стабильность и надежность.

Доступность и интернационализация

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

Ресурсы и взаимодействие с сообществом

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

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

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

Изображение: Kandinsky by Sber AI

Опубликовано 22.01.2024

Похожие статьи