Привет, Хабр! Представляю вашему вниманию перевод статьи «Do Flutter apps dream of platform aware widgets?»
Разрабатывая приложения на Flutter с нативным дизайном под iOS и Android, я столкнулся с тем, что мне пришлось писать кучу условий для проверки платформы на которой исполняется код, при этом делая две схожие реализации UI. Мне это не понравилось и я рад, что мне попалась статья, которая помогла мне решить мою проблему.
Об авторе: Swav Kulinski — Lead Android Developer в The App Business, Flutter GDE.
Далее речь пойдет от лица автора.
Flutter — это решение для кроссплатформенной разработки мобильных приложений, которое обещает абсолютную свободу в создании пользовательского интерфейса независимо от платформы. Это достигается тем, что фреймворк использует собственный механизм рендеринга для рисования виджетов.
Проблема многих кроссплатформенных решений в том, что они выглядят одинаково на iPhone и Android. Но что делать компаниям, которым нужно поддерживать внешний вид платформы? Которым нужно использовать Material Design для Android и Human Interface для iOS? Для таких компаний подходит Flutter, оснащенный пакетами, которые содержат набор собственных виджетов для iOS и Android, под названием Cupertino и Material.
Flutter является кроссплатформенным по своей сути, но когда дело доходит до верстки UI, который должен выглядеть нативно для каждой из платформ, это не совсем так. Приходится делать две схожих реализации верстки. Это происходит из-за того что, например, для IOS, CupertinoNavigationBar должен находиться в CupertinoPageScaffold, а в Android, AppBar внутри Scaffold. Данная особенность уменьшает преимущество кроссплатформенности во Flutter, так как для каждой платформы приходится писать свой код для верстки.
Я хотел бы предложить подход, который позволяет создавать абстрактные интерфейсы и корректировать внешний вид и поведение приложения в зависимости от того, на какой платформе оно работает.
Рассмотрим следующие конструкторы для двух виджетов, которые предоставляют верхнюю панель приложений:
и
Оба вышеупомянутых виджета играют одну и ту же роль, являясь верхней панелью в приложении в стиле Cupertino и Material. Но все же они требуют входные данные в разном виде. Необходимо решение, которое будет абстрагироваться от того, как мы создаем конкретные виджеты, и при этом обеспечивать реализацию в зависимости от платформы. Воспользуемся старым добрым фабричным методом.
По сути, вышеуказанный класс — это фабрика платформозависимых виджетов, которая при реализации может предоставить пользовательский конструктор (или несколько именованных конструкторов), поддерживающих потребности обоих конкретных классов.
Я выбрал generics для возврата конкретных классов, потому что иногда родительскому виджету нужен определенный тип, который должен быть возвращен из дочернего виджета.
Теперь мы можем реализовать наш первый виджет.
Довольно просто, правда? Обратите внимание, что мы полностью контролируем содержимое виджетов панели приложений.
Предположим, что мы реализовали Scaffold и Button.
Теперь мы готовы использовать и переиспользовать наши виджеты, ориентированные на платформу.
Готово! Вышеприведенный код отобразит на обеих платформах панель приложений, ориентированную на платформу, и наш PlatformScaffoldWidget готов к повторному использованию в остальной части приложения без каких-либо проблем.
Код можно посмотреть на гитхабе.
Разрабатывая приложения на Flutter с нативным дизайном под iOS и Android, я столкнулся с тем, что мне пришлось писать кучу условий для проверки платформы на которой исполняется код, при этом делая две схожие реализации UI. Мне это не понравилось и я рад, что мне попалась статья, которая помогла мне решить мою проблему.
Об авторе: Swav Kulinski — Lead Android Developer в The App Business, Flutter GDE.
Далее речь пойдет от лица автора.
Flutter — это решение для кроссплатформенной разработки мобильных приложений, которое обещает абсолютную свободу в создании пользовательского интерфейса независимо от платформы. Это достигается тем, что фреймворк использует собственный механизм рендеринга для рисования виджетов.
Проблема многих кроссплатформенных решений в том, что они выглядят одинаково на iPhone и Android. Но что делать компаниям, которым нужно поддерживать внешний вид платформы? Которым нужно использовать Material Design для Android и Human Interface для iOS? Для таких компаний подходит Flutter, оснащенный пакетами, которые содержат набор собственных виджетов для iOS и Android, под названием Cupertino и Material.
Flutter является кроссплатформенным по своей сути, но когда дело доходит до верстки UI, который должен выглядеть нативно для каждой из платформ, это не совсем так. Приходится делать две схожих реализации верстки. Это происходит из-за того что, например, для IOS, CupertinoNavigationBar должен находиться в CupertinoPageScaffold, а в Android, AppBar внутри Scaffold. Данная особенность уменьшает преимущество кроссплатформенности во Flutter, так как для каждой платформы приходится писать свой код для верстки.
Я хотел бы предложить подход, который позволяет создавать абстрактные интерфейсы и корректировать внешний вид и поведение приложения в зависимости от того, на какой платформе оно работает.
Рассмотрим следующие конструкторы для двух виджетов, которые предоставляют верхнюю панель приложений:
CupertinoNavigationBar ({
this.leading,
this.middle,
})
и
AppBar ({
this.leading,
this.title
})
Оба вышеупомянутых виджета играют одну и ту же роль, являясь верхней панелью в приложении в стиле Cupertino и Material. Но все же они требуют входные данные в разном виде. Необходимо решение, которое будет абстрагироваться от того, как мы создаем конкретные виджеты, и при этом обеспечивать реализацию в зависимости от платформы. Воспользуемся старым добрым фабричным методом.
import 'package:flutter/material.dart';
import 'dart:io' show Platform;
abstract class PlatformWidget<I extends Widget, A extends Widget>
extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (Platform.isAndroid) {
return createAndroidWidget(context);
} else if (Platform.isIOS) {
return createIosWidget(context);
}
// platform not supported returns an empty widget
return Container();
}
I createIosWidget(BuildContext context);
A createAndroidWidget(BuildContext context);
}
По сути, вышеуказанный класс — это фабрика платформозависимых виджетов, которая при реализации может предоставить пользовательский конструктор (или несколько именованных конструкторов), поддерживающих потребности обоих конкретных классов.
Я выбрал generics для возврата конкретных классов, потому что иногда родительскому виджету нужен определенный тип, который должен быть возвращен из дочернего виджета.
Теперь мы можем реализовать наш первый виджет.
class PlatformAppBar extends PlatformWidget<CupertinoNavigationBar, AppBar> {
final Widget leading;
final Widget title;
PlatformAppBar({
this.leading,
this.title,
});
@override
AppBar createAndroidWidget(BuildContext context) {
return AppBar(
leading: leading,
title: title,
);
}
@override
CupertinoNavigationBar createIosWidget(BuildContext context) {
return CupertinoNavigationBar(
leading: leading,
middle: title,
);
}
}
Довольно просто, правда? Обратите внимание, что мы полностью контролируем содержимое виджетов панели приложений.
Предположим, что мы реализовали Scaffold и Button.
class PlatformScaffoldWidget extends PlatformWidget<CupertinoPageScaffold,Scaffold> {
...
}
class PlatformButton extends PlatformWidget<CupertinoButton,FlatButton> {
...
}
Теперь мы готовы использовать и переиспользовать наши виджеты, ориентированные на платформу.
Widget build(BuildContext context) {
return PlatformScaffoldWidget(
appBar: PlatformAppBarWidget(
leading: PlatformButton(
child: Icon(Icons.ic_arrow_back),
onClick: () => _handleBack()
),
title: Text("I love my Platform"),
),
content: ...
);
}
Готово! Вышеприведенный код отобразит на обеих платформах панель приложений, ориентированную на платформу, и наш PlatformScaffoldWidget готов к повторному использованию в остальной части приложения без каких-либо проблем.
Код можно посмотреть на гитхабе.