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 MB и време формирања 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 поставља минималне величине, интервал и распоред: 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 MB), на Android — минимални интервал ажурирања. Проблематичне виџете систем принудно зауставља.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође