Hola, Amigos! Меня зовут Сергей Климович, я Mobile TeamLead агентства заказной разработки Amiga и соавтор телеграм-канала Flutter. Много. На канале мы уже рассказывали про Home Widget для Android, теперь пришло время поговорить про iOS. Я нашел отличную статью по этой теме и решил поделиться с вами переводом.
С выпуском iOS17 Apple вдохнула новую жизнь в виджеты, представив интерактивные виджеты. Посмотрим, как WidgetKit можно интегрировать в существующий проект Flutter.
Инструкции для этой демонстрации будут максимально общими и простыми для выполнения. Будем использовать Movie Picker, вы можете включить его в любой проект Flutter, который пожелаете. В конце будет краткий обзор того, как может выглядеть стилизованная и полностью реализованная версия этой функции с помощью Movie Picker.
В этом уроке будем использовать пакет flutter_widgetkit. Этот пакет невероятно прост в использовании, но его возможности ограничены созданием только виджетов домашнего экрана iOS. Однако в вашем распоряжении есть несколько других пакетов, таких как home_widget, который позволяет создавать виджеты как для iOS, так и для Android, но по сути все они функционируют одинаково для iOS.
Предисловие
Прежде чем начать, убедитесь, что у вас выполнены следующие условия:
Flutter и Dart установлены и настроены для разработки на вашем компьютере.
Xcode установлен и настроен для разработки iOS, включая инструменты командной строки.
iOS-эмулятор или реальное устройство на iOS.
Настройки Flutter
Шаг 1. Настройте проект Flutter
Если вы еще этого не сделали, создайте новый проект Flutter или используйте существующий. Откройте проект в предпочитаемом редакторе кода. Автор использует Android Studio.
Шаг 2. Добавьте зависимости
В свой pubspec.yaml
добавьте пакет flutter_widgetkit:
dependencies:
flutter:
sdk: flutter
flutter_widgetkit: ^1.0.3 # Use the latest version
Шаг 3. Создание домашней страницы.
Начнем с создания простого домашнего экрана Flutter. Он будет содержать ElevatedButton
и простой файл TextField
. Все, что нужно от этой страницы, — это принимать текст в качестве входных данных из TextField, и при нажатии кнопки нужно, чтобы наш виджет iOS обновлялся.
var textController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 250,
child: TextField(
controller: textController,
decoration: InputDecoration(
hintText: "Enter widget text",
),
),
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: () {},
child: const Text("Send to Widget"),
),
],
),
),
);
}
Шаг 4. Создание WidgetData
Чтобы упростить отправку данных в iOS-виджет, создадим простой WidgetData
. Нужно отправить экземпляр класса данных в виде строки JSON, которая может быть проанализирована кодом SwiftUI. На данный момент этот класс содержит только одну переменную — строку с именем text
, но она может содержать любые произвольные данные, которые вы хотите отправить в свой виджет.
class WidgetData {
final String text;
WidgetData(this.text);
WidgetData.fromJson(Map<String, dynamic> json) : text = json['text'];
Map<String, dynamic> toJson() => {'text': text};
}
Шаг 5: Отправка информации
Используем WidgetKit.setItem()
для отправки данных в виджет. Для этого метода потребуется key
, который будет на стороне Xcode, и значение value
будет данными, которые нужно отправить. Важно предоставить группу приложений, которую настроим в XCode всего за секунду, используем group.flutterioswidget
.
ElevatedButton(
onPressed: () {
WidgetKit.setItem(
'widgetData',
jsonEncode(WidgetData(textController.text)),
'group.flutterioswidget');
WidgetKit.reloadAllTimelines();
},
child: const Text("Push to Widget"),
),
Вызываем WidgetKit.reloadAllTimelines()
, он перезагрузит временную шкалу и произойдет перерисовка нашего виджета.
Настройки XCode
Шаг 6. Откройте проект в Xcode.
В AndroidStudio или VSCode щелкните правой кнопкой мыши папку ios > нажмите «Открыть в Xcode».
Шаг 7: Создайте новую цель
Нажмите Runner, а затем в левом нижнем углу нажмите кнопку «плюс» (+), чтобы создать новую цель.
Найдите «Расширение виджета» и щелкните результат.
Введите любое имя и нажмите «Готово».
Шаг 8. Назначьте группы приложений
Прежде чем перейти к следующему шагу, нужно назначить AppGroups этим целям. В разделе «Targets» выберите цель виджета, которая была недавно создана, и нажмите плюс (+) рядом с надписью «Capability», чтобы добавить новую. Найдите «AppGroup» и выберите первый результат.
Создайте новую группу приложений, нажав кнопку «плюс» (+). Группа приложений должна начинаться с .group
, и очень важно, чтобы это соответствовало тому, что вы указали в коде Flutter.
Теперь нужно добавить такую же возможность Runner. Нажмите на Runner в Targets и повторите шаги, описанные выше.
Теперь всё готово, чтобы принимать данные из нашего приложения Flutter!
SwiftUI
Шаг 9. Перейдите к коду виджета.
Перейдите к FlutterIOSWidget
(или к тому, в котором есть Provider
).
Шаг 10. Создание виджета SwiftUi
В нашем примере будет только одна запись данных, а именно строка с именем text
.
struct WidgetData: Decodable, Hashable {
let text: String
}
Далее нужно создать TimelineEntry
, назовем его FlutterEntry
:
struct FlutterEntry: TimelineEntry {
let date: Date
let widgetData: WidgetData?
}
Теперь нужно создать файл Provider
. Если вы знакомы со SwiftUI, в этом нет ничего нового. Обратите внимание, что AppGroup, указанная в sharedDefaults
, должна совпадать везде, где она была указана.
В getTimeline
также создаём новый экземпляр flutterData.
struct Provider: TimelineProvider {
func placeholder(in context: Context) -> FlutterEntry {
FlutterEntry(date: Date(), widgetData: WidgetData(text: "Flutter iOS widget!"))
}
func getSnapshot(in context: Context, completion: @escaping (FlutterEntry) -> ()) {
let entry = FlutterEntry(date: Date(), widgetData: WidgetData(text: "Flutter iOS widget!"))
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
let sharedDefaults = UserDefaults.init(suiteName: "group.flutterioswidget")
let flutterData = try? JSONDecoder().decode(WidgetData.self, from: (sharedDefaults?
.string(forKey: "widgetData")?.data(using: .utf8)) ?? Data())
let entryDate = Calendar.current.date(byAdding: .hour, value: 24, to: Date())!
let entry = FlutterEntry(date: entryDate, widgetData: flutterData)
let timeline = Timeline(entries: [entry], policy: .atEnd)
completion(timeline)
}
}
Ниже показано, что виджет просто принимает текст, который был установлен из виджета Flutter, и отображает его.
struct FlutterIOSWidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
Text(entry.widgetData?.text ?? "Tap to set message.")
}
}
struct FlutterIOSWidget: Widget {
let kind: String = "FlutterIOSWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
FlutterIOSWidgetEntryView(entry: entry)
}
.configurationDisplayName("Flutter iOS Widget")
.description("This is an example Flutter iOS widget.")
}
}
Вот тут и начинается самое интересное, потому что, обладая небольшими знаниями SwiftUI, можно создать абсолютно любой виджет на основе данных, полученных от приложения Flutter.
На этом всё! Полный пример можно посмотреть на github.
Надеюсь, вам было интересно! Подписывайтесь на наш телеграм-канал Flutter. Много, который мы ведем командой мобильных разработчиков. Рассказываем про свой личный опыт и делимся советами от софт-скиллов до технических знаний.