Widget — компактный элемент пользовательского интерфейса на домашнем экране iOS или Android, отображающий актуальную информацию без открытия приложения. Виджеты появились в iOS 14 (2020) с фреймворком WidgetKit на SwiftUI и существуют в Android с версии 1.5 через AppWidgetProvider на Kotlin или Java. Они позволяют пользователю видеть погоду, курс валют, календарь, заметки или статус задач прямо на home screen, экономя время и упрощая взаимодействие. По данным Apple, пользователи взаимодействуют с виджетами в среднем 4–6 раз в день, а установка виджета на home screen увеличивает вовлечённость в приложение на 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 позволяют пользователю группировать несколько виджетов в один слот на home screen. Устройство автоматически переключает отображаемый виджет в зависимости от времени суток, местоположения или активности пользователя. Например, утром показывается календарь, днём — погода, вечером — заметки. Для разработчика это означает, что виджет должен корректно работать в составе стопки и не полагаться на постоянное отображение.
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 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также