Widget — kompaktowy element interfejsu użytkownika na ekranie głównym iOS lub Android, wyświetlający aktualne informacje bez otwierania aplikacji. Widżety pojawiły się w iOS 14 (2020) z frameworkiem WidgetKit w SwiftUI i istnieją w Android od wersji 1.5 poprzez AppWidgetProvider w Kotlin lub Java. Pozwalają użytkownikowi zobaczyć pogodę, kursy walut, kalendarz, notatki lub status zadań bezpośrednio na home screen, oszczędzając czas i upraszczając interakcję. Według danych Apple, użytkownicy wchodzą w interakcję z widżetami średnio 4–6 razy dziennie, a umieszczenie widżetu na home screen zwiększa zaangażowanie w aplikację o 20–30%. W Android te wskaźniki są porównywalne. Omówimy, jak działają widżety na obu platformach, z przykładami kodu.
Najważniejsze
Widget — to kompaktowy element interfejsu użytkownika umieszczany na ekranie głównym urządzenia mobilnego w celu wyświetlania aktualnych informacji z aplikacji. W przeciwieństwie do pełnej aplikacji, widżet nie wymaga uruchamiania — użytkownik widzi dane natychmiast po odblokowaniu ekranu. Koncepcja pochodzi z systemów desktopowych (macOS Dashboard, Windows Sidebar), a w środowisku mobilnym Android wprowadził obsługę widżetów od pierwszej wersji (Android 1.5 Cupcake, 2009), podczas gdy Apple dodało je dopiero w iOS 14.
Implementacja techniczna różni się na platformach. Widżet iOS — osobny target w Xcode, kompilowany jako Widget Extension, działający we własnym procesie. Widżet Android — BroadcastReceiver rozszerzający AppWidgetProvider, renderujący układ przez RemoteViews. Obie platformy nakładają ścisłe ograniczenia na wydajność i pamięć: system może usunąć widżet przy przekroczeniu limitów. iOS ogranicza widżet do 30–50 MB i czasu tworzenia timeline do 10 sekund. Android — interwał aktualizacji nie częściej niż 30 minut przez updatePeriodMillis i wymuszone zakończenie przy zawieszeniu onUpdate.
Smart Stack w iOS i stosy w Android pozwalają użytkownikowi grupować wiele widżetów w jeden slot na home screen. Urządzenie automatycznie przełącza wyświetlany widżet w zależności od pory dnia, lokalizacji lub aktywności użytkownika. Na przykład rano pokazywany jest kalendarz, w ciągu dnia — pogoda, wieczorem — notatki. Dla dewelopera oznacza to, że widżet musi poprawnie działać w ramach stosu i nie polegać na stałym wyświetlaniu.
WidgetKit — framework Apple do tworzenia widżetów na iOS 14+, iPadOS 14+ i macOS 11+. Widżety pisze się w Swift z użyciem SwiftUI i mechanizmu TimelineProvider do aktualizacji treści. Architektura WidgetKit obejmuje trzy komponenty: TimelineProvider — źródło danych tworzące oś czasu z wpisami; Widget Entry View — SwiftUI View wyświetlająca zawartość na podstawie wpisu; Widget Configuration — opis typu, rozmiarów i rodzin widżetu.
WidgetKit sam zarządza aktualizacją — deweloper nie może wymusić aktualizacji widżetu częściej niż pozwala na to system. Apple stosuje budżet aktualizacji rozdzielany pomiędzy wszystkie widżety na urządzeniu. Minimalny interwał — 15–30 minut. W przypadku krytycznych danych (np. status dostawy) stosuje się push-to-refresh — wysłanie powiadomienia push z odpowiednią datą w przyszłości, po którym WidgetKit żąda nowego timeline. Od iOS 17 widżety obsługują interaktywność przez App Intents — użytkownik może naciskać przyciski wewnątrz widżetu bez otwierania aplikacji.
| Rozmiar | iOS (pt) | Zawartość |
|---|---|---|
| Small | 170 × 170 | 1–2 wskaźniki: temperatura, kurs waluty |
| Medium | 360 × 170 | 2–4 wskaźniki: prognoza na 5 dni, lista |
| Large | 360 × 380 | 5–10 wskaźników: kalendarz, kanał wiadomości |
AppWidgetProvider — klasa bazowa do tworzenia widżetów w Android, wchodząca w skład Android SDK od API level 3. Widżet Android — to BroadcastReceiver otrzymujący zdarzenia aktualizacji, włączenia i wyłączenia od systemu. W przeciwieństwie do iOS, gdzie widżet jest rysowany przez SwiftUI na własnym silniku, Android używa RemoteViews — ograniczonego zestawu komponentów View renderowanych w procesie systemu (launchera). Nie można używać niestandardowych View, złożonych animacji ani bibliotek UI.
Schemat działania widżetu Android obejmuje cztery etapy. Konfiguracja — podczas dodawania widżetu do ekranu system może uruchomić Activity konfiguracji, gdzie użytkownik wybiera ustawienia. Aktualizacja — system wywołuje onUpdate() w określonym interwale (minimum 30 minut) lub na sygnał z aplikacji przez WorkManager. Renderowanie — dostawca tworzy RemoteViews z aktualnymi danymi i przekazuje je przez AppWidgetManager. Obsługa kliknięć — widżet obsługuje PendingIntent dla przycisków: po kliknięciu otwierane jest Activity lub wykonywana jest akcja. Od Android 12 widżety otrzymały obsługę zaokrąglonych rogów, adaptacyjnych kolorów Material You i dynamicznej zmiany rozmiaru.
Stwórzmy widżet pogody w Swift z użyciem WidgetKit i SwiftUI. Projekt powinien zawierać Widget Extension target, dodawany w Xcode przez File → New → Target → Widget Extension.
import WidgetKit
import SwiftUI
// 1. Model danych dla widżetu
struct WeatherEntry: TimelineEntry {
let date: Date
let temperature: Int
let condition: String
}
// 2. TimelineProvider — dostawca danych
struct Provider: TimelineProvider {
func placeholder(in context: Context) -> WeatherEntry {
WeatherEntry(date: Date(), temperature: 22, condition: "Słonecznie")
}
func getSnapshot(in context: Context, completion: @escaping (WeatherEntry) -> Void) {
let entry = WeatherEntry(date: Date(), temperature: 22, condition: "Słonecznie")
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<WeatherEntry>) -> Void) {
let now = Date()
let entry = WeatherEntry(date: now, temperature: 20, condition: "Pochmurnie")
let nextUpdate = Calendar.current.date(byAdding: .hour, value: 3, to: now)!
let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
completion(timeline)
}
}
// 3. SwiftUI View do wyświetlania
struct WeatherWidgetEntryView: View {
var entry: WeatherEntry
var body: some View {
VStack(alignment: .leading) {
Text("Pogoda").font(.caption).foregroundColor(.secondary)
HStack {
Text("\(entry.temperature)°").font(.largeTitle).fontWeight(.bold)
Spacer()
Text(entry.condition).font(.body)
}
}.padding()
}
}
// 4. Konfiguracja widżetu
@main
struct WeatherWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(kind: "WeatherWidget", provider: Provider()) { entry in
WeatherWidgetEntryView(entry: entry)
}
.configurationDisplayName("Pogoda")
.description("Pokazuje aktualną temperaturę")
.supportedFamilies([.systemSmall, .systemMedium])
}
}
Kluczowe punkty: TimelineProvider określa, kiedy i jakie dane pokazywać; getTimeline tworzy oś czasu z polityką aktualizacji .after(nextUpdate); StaticConfiguration łączy dostawcę z View. Do danych dynamicznych używa się IntentConfiguration z parametrami użytkownika.
Zaimplementujemy analogiczny widżet pogody w Kotlin z AppWidgetProvider i 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 = "Pochmurnie"
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 — rejestracja dostawcy
<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>
Plik widget_weather_info.xml określa minimalne rozmiary, interwał i layout: minWidth 160dp, minHeight 80dp, updatePeriodMillis 3600000 (1 godzina). RemoteViews używa tylko obsługiwanych komponentów: LinearLayout, TextView, ImageView. Niestandardowe View i ConstraintLayout są dostępne od Android 5.0. GlanceLayout — eksperymentalne podejście Compose dla RemoteViews — pojawiło się w Android 15.
Często zadawane pytania
iOS WidgetKit oferuje trzy stałe rozmiary (small, medium, large) i aktualizację przez TimelineProvider z budżetem systemu. Android AppWidgetProvider używa elastycznych rozmiarów w komórkach, RemoteViews do renderowania i aktualizacji przez updatePeriodMillis lub WorkManager.
W iOS aktualizacją steruje system — minimalny interwał 15–30 minut, dla danych krytycznych stosuje się push-to-refresh. W Android interwał jest ustawiany w updatePeriodMillis (minimum 30 minut) lub przez WorkManager dla zadań w tle.
iOS obsługuje small (170x170pt), medium (360x170pt) i large (360x380pt). Android używa elastycznej siatki komórek: od 2x1 do 5x5, rozmiar zależy od launchera i wersji systemu.
W iOS animacje wewnątrz widżetu nie występują — są to statyczne elementy. Android RemoteViews obsługuje podstawowe animacje przez setFloat() i ViewPropertyAnimator, ale złożone animacje nie są dostępne.
Przy prawidłowej implementacji — nie. System ściśle ogranicza zasoby widżetu: w iOS budżet aktualizacji i pamięci (30–50 MB), w Android — minimalny interwał aktualizacji. Problematyczne widżety system wymusza zakończyć.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również