Widget — компактний елемент інтерфейсу користувача на домашньому екрані iOS або Android, що відображає актуальну інформацію без відкриття застосунку. Віджети з'явилися в iOS 14 (2020) з фреймворком WidgetKit на SwiftUI та існують в Android з версії 1.5 через AppWidgetProvider на Kotlin або Java. Вони дозволяють користувачеві бачити погоду, курс валют, календар, нотатки або статус завдань прямо на домашньому екрані, економлячи час та спрощуючи взаємодію. За даними Apple, користувачі взаємодіють з віджетами в середньому 4–6 разів на день, а встановлення віджета на домашній екран збільшує залученість у застосунок на 20–30%. Для Android ці показники порівнянні. Розберемо, як влаштовані віджети на обох платформах, з прикладами коду.
Головне
Widget — це компактний елемент інтерфейсу користувача, що розміщується на домашньому екрані мобільного пристрою для відображення актуальної інформації із застосунку. На відміну від повноцінного застосунку, віджет не потребує запуску — користувач бачить дані одразу після розблокування екрана. Концепція прийшла з десктопних ОС (macOS Dashboard, Windows Sidebar), а в мобільному середовищі Android ввів підтримку віджетів з першого релізу (Android 1.5 Cupcake, 2009), тоді як Apple додала їх тільки в iOS 14.
Технічна реалізація відрізняється на платформах. Віджет iOS — окремий target у Xcode, що компілюється як Widget Extension, працюючи у власному процесі. Віджет Android — BroadcastReceiver, що розширює AppWidgetProvider, рендерячи макет через RemoteViews. Обидві платформи накладають суворі обмеження на продуктивність та пам'ять: система може видалити віджет при перевищенні лімітів. iOS обмежує віджет 30–50 МБ та часом формування timeline до 10 секунд. Android — інтервал оновлення не частіше 30 хвилин через updatePeriodMillis та примусове завершення при зависанні onUpdate.
Smart Stack на iOS та стопки на Android дозволяють користувачеві групувати кілька віджетів в один слот на домашньому екрані. Пристрій автоматично перемикає відображуваний віджет залежно від часу доби, місцезнаходження або активності користувача. Наприклад, вранці показується календар, вдень — погода, ввечері — нотатки. Для розробника це означає, що віджет повинен коректно працювати в складі стопки та не покладатися на постійне відображення.
WidgetKit — фреймворк Apple для створення віджетів на iOS 14+, iPadOS 14+ та macOS 11+. Віджети пишуться на Swift з використанням SwiftUI та механізму TimelineProvider для оновлення контенту. Архітектура WidgetKit включає три компоненти: TimelineProvider — джерело даних, що формує часову шкалу з записами; Widget Entry View — SwiftUI View, що відображає вміст на основі запису; Widget Configuration — опис типу, розмірів та сімейств віджета.
WidgetKit сам керує оновленням — розробник не може примусово оновити віджет частіше, ніж дозволяє система. Apple використовує бюджет оновлень, що розподіляється між усіма віджетами на пристрої. Мінімальний інтервал — 15–30 хвилин. Для критичних даних (наприклад, статус доставки) використовується push-to-refresh — надсилання push-повідомлення з релевантною датою в майбутньому, після чого WidgetKit запитує новий timeline. Починаючи з iOS 17, віджети підтримують інтерактивність через App Intents — користувач може натискати кнопки всередині віджета без відкриття застосунку.
| Розмір | iOS (pt) | Вміст |
|---|---|---|
| Small | 170 × 170 | 1–2 показники: температура, курс валюти |
| Medium | 360 × 170 | 2–4 показники: прогноз на 5 днів, список |
| Large | 360 × 380 | 5–10 показників: календар, стрічка новин |
AppWidgetProvider — базовий клас для створення віджетів на Android, що входить до складу Android SDK з API level 3. Віджет Android — це BroadcastReceiver, що отримує події оновлення, включення та вимкнення від системи. На відміну від iOS, де віджет малюється SwiftUI на власному рушієві, Android використовує RemoteViews — обмежений набір компонентів View, що рендеряться в процесі системи (launcher). Не можна використовувати кастомні View, складні анімації або UI-бібліотеки.
Схема роботи Android-віджета включає чотири етапи. Конфігурація — при додаванні віджета на екран система може запустити Activity конфігурації, де користувач вибирає налаштування. Оновлення — система викликає onUpdate() через заданий інтервал (мінімум 30 хвилин) або за сигналом із застосунку через WorkManager. Рендеринг — провайдер створює RemoteViews з актуальними даними та передає їх через AppWidgetManager. Обробка натискань — віджет підтримує PendingIntent для кнопок: при кліку відкривається Activity або виконується дія. Починаючи з Android 12, віджети отримали підтримку закруглених кутів, адаптивних кольорів Material You та динамічної зміни розміру.
Створимо віджет погоди на Swift з використанням WidgetKit та SwiftUI. Проект повинен містити Widget Extension target, що додається в Xcode через File → New → Target → Widget Extension.
import WidgetKit
import SwiftUI
// 1. Модель даних для віджета
struct WeatherEntry: TimelineEntry {
let date: Date
let temperature: Int
let condition: String
}
// 2. TimelineProvider — постачальник даних
struct Provider: TimelineProvider {
func placeholder(in context: Context) -> WeatherEntry {
WeatherEntry(date: Date(), temperature: 22, condition: "Сонячно")
}
func getSnapshot(in context: Context, completion: @escaping (WeatherEntry) -> Void) {
let entry = WeatherEntry(date: Date(), temperature: 22, condition: "Сонячно")
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<WeatherEntry>) -> Void) {
let now = Date()
let entry = WeatherEntry(date: now, temperature: 20, condition: "Хмарно")
let nextUpdate = Calendar.current.date(byAdding: .hour, value: 3, to: now)!
let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
completion(timeline)
}
}
// 3. SwiftUI View для відображення
struct WeatherWidgetEntryView: View {
var entry: WeatherEntry
var body: some View {
VStack(alignment: .leading) {
Text("Погода").font(.caption).foregroundColor(.secondary)
HStack {
Text("\(entry.temperature)°").font(.largeTitle).fontWeight(.bold)
Spacer()
Text(entry.condition).font(.body)
}
}.padding()
}
}
// 4. Конфігурація віджета
@main
struct WeatherWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(kind: "WeatherWidget", provider: Provider()) { entry in
WeatherWidgetEntryView(entry: entry)
}
.configurationDisplayName("Погода")
.description("Показує поточну температуру")
.supportedFamilies([.systemSmall, .systemMedium])
}
}
Ключові моменти: TimelineProvider визначає, коли та які дані показувати; getTimeline створює часову шкалу з політикою оновлення .after(nextUpdate); StaticConfiguration пов'язує провайдера з View. Для динамічних даних використовується IntentConfiguration з користувацькими параметрами.
Реалізуємо аналогічний віджет погоди на Kotlin з AppWidgetProvider та RemoteViews.
// WeatherWidgetProvider.kt
class WeatherWidgetProvider : AppWidgetProvider() {
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray
) {
for (appWidgetId in appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId)
}
}
override fun onAppWidgetOptionsChanged(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetId: Int,
newOptions: Bundle
) {
updateAppWidget(context, appWidgetManager, appWidgetId)
}
companion object {
fun updateAppWidget(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetId: Int
) {
val temperature = 20
val condition = "Хмарно"
val views = RemoteViews(
context.packageName, R.layout.widget_weather
).apply {
setTextViewText(R.id.tv_temperature, "$temperature°")
setTextViewText(R.id.tv_condition, condition)
setOnClickPendingIntent(
R.id.widget_root, PendingIntent.getActivity(
context, 0,
Intent(context, MainActivity::class.java),
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
)
}
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
}
// AndroidManifest — реєстрація провайдера
<receiver android:name=".WeatherWidgetProvider" android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_weather_info" />
</receiver>
Файл widget_weather_info.xml задає мінімальні розміри, інтервал та layout: minWidth 160dp, minHeight 80dp, updatePeriodMillis 3600000 (1 година). RemoteViews використовує тільки підтримувані компоненти: LinearLayout, TextView, ImageView. Кастомні View та ConstraintLayout доступні з Android 5.0. GlanceLayout — експериментальний Compose-підхід для RemoteViews — з'явився в Android 15.
Поширені запитання
iOS WidgetKit пропонує три фіксованих розміри (small, medium, large) та оновлення через TimelineProvider з бюджетом системи. Android AppWidgetProvider використовує гнучкі розміри в комірках, RemoteViews для рендерингу та оновлення через updatePeriodMillis або WorkManager.
На iOS оновлення контролює система — мінімальний інтервал 15–30 хвилин, для критичних даних використовується push-to-refresh. На Android інтервал задається в updatePeriodMillis (мінімум 30 хвилин) або через WorkManager для фонових завдань.
iOS підтримує small (170x170pt), medium (360x170pt) та large (360x380pt). Android використовує гнучку сітку комірок: від 2x1 до 5x5, розмір залежить від launcher та версії ОС.
На iOS анімації всередині віджета відсутні — це статичні елементи. Android RemoteViews підтримує базові анімації через setFloat() та ViewPropertyAnimator, але складні анімації недоступні.
При правильній реалізації — ні. Система суворо обмежує ресурси віджета: на iOS — бюджет оновлень та пам'яті (30–50 МБ), на Android — мінімальний інтервал оновлення. Проблемні віджети система примусово завершує.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також