Widget is a compact user interface element on the iOS or Android home screen that displays relevant information without opening the app. Widgets appeared in iOS 14 (2020) with the WidgetKit framework on SwiftUI and have existed on Android since version 1.5 via AppWidgetProvider on Kotlin or Java. They allow users to see weather, exchange rates, calendar, notes or task status directly on the home screen, saving time and simplifying interaction. According to Apple, users interact with widgets on average 4–6 times a day, and placing a widget on the home screen increases app engagement by 20–30%. For Android, these metrics are comparable. Let's explore how widgets work on both platforms with code examples.
Key Takeaways
Widget is a compact user interface element placed on the home screen of a mobile device to display relevant information from an app. Unlike a full application, a widget does not require launching — the user sees data immediately after unlocking the screen. The concept originated from desktop OS (macOS Dashboard, Windows Sidebar), and in the mobile environment, Android introduced widget support with its first release (Android 1.5 Cupcake, 2009), while Apple added them only in iOS 14.
Technical implementation differs across platforms. An iOS widget is a separate target in Xcode, compiled as a Widget Extension, running in its own process. An Android widget is a BroadcastReceiver extending AppWidgetProvider, rendering the layout via RemoteViews. Both platforms impose strict performance and memory restrictions: the system may remove the widget if limits are exceeded. iOS limits widgets to 30–50 MB and timeline generation time to 10 seconds. Android — update interval no more than 30 minutes via updatePeriodMillis and forced termination if onUpdate hangs.
Smart Stack on iOS and stacks on Android allow users to group multiple widgets into one home screen slot. The device automatically switches the displayed widget based on time of day, location, or user activity. For example, in the morning the calendar is shown, during the day — weather, in the evening — notes. For developers, this means the widget must work correctly as part of a stack and not rely on constant display.
WidgetKit is Apple's framework for creating widgets on iOS 14+, iPadOS 14+, and macOS 11+. Widgets are written in Swift using SwiftUI and the TimelineProvider mechanism for content updates. The WidgetKit architecture includes three components: TimelineProvider — a data source that forms a timeline with entries; Widget Entry View — a SwiftUI View that displays content based on an entry; Widget Configuration — a description of the widget type, sizes, and families.
WidgetKit manages updates automatically — the developer cannot force a widget update more often than the system allows. Apple uses an update budget distributed among all widgets on the device. The minimum interval is 15–30 minutes. For critical data (e.g., delivery status), push-to-refresh is used — sending a push notification with a relevant future date, after which WidgetKit requests a new timeline. Starting from iOS 17, widgets support interactivity via App Intents — users can press buttons inside a widget without opening the app.
| Size | iOS (pt) | Content |
|---|---|---|
| Small | 170 × 170 | 1–2 metrics: temperature, exchange rate |
| Medium | 360 × 170 | 2–4 metrics: 5-day forecast, list |
| Large | 360 × 380 | 5–10 metrics: calendar, news feed |
AppWidgetProvider is the base class for creating widgets on Android, part of the Android SDK since API level 3. An Android widget is a BroadcastReceiver that receives update, enable, and disable events from the system. Unlike iOS, where the widget is rendered with SwiftUI on its own engine, Android uses RemoteViews — a limited set of View components rendered in the system process (launcher). Custom Views, complex animations, or UI libraries cannot be used.
The Android widget workflow includes four stages. Configuration — when adding a widget to the screen, the system may launch a configuration Activity where the user selects settings. Update — the system calls onUpdate() at a specified interval (minimum 30 minutes) or upon a signal from the app via WorkManager. Rendering — the provider creates RemoteViews with current data and passes them through AppWidgetManager. Touch handling — the widget supports PendingIntent for buttons: clicking opens an Activity or performs an action. Starting from Android 12, widgets gained support for rounded corners, adaptive Material You colors, and dynamic size changes.
Let's create a weather widget in Swift using WidgetKit and SwiftUI. The project must include a Widget Extension target, added in Xcode via File → New → Target → Widget Extension.
import WidgetKit
import SwiftUI
// 1. Data model for the widget
struct WeatherEntry: TimelineEntry {
let date: Date
let temperature: Int
let condition: String
}
// 2. TimelineProvider — data provider
struct Provider: TimelineProvider {
func placeholder(in context: Context) -> WeatherEntry {
WeatherEntry(date: Date(), temperature: 22, condition: "Sunny")
}
func getSnapshot(in context: Context, completion: @escaping (WeatherEntry) -> Void) {
let entry = WeatherEntry(date: Date(), temperature: 22, condition: "Sunny")
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<WeatherEntry>) -> Void) {
let now = Date()
let entry = WeatherEntry(date: now, temperature: 20, condition: "Cloudy")
let nextUpdate = Calendar.current.date(byAdding: .hour, value: 3, to: now)!
let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
completion(timeline)
}
}
// 3. SwiftUI View for display
struct WeatherWidgetEntryView: View {
var entry: WeatherEntry
var body: some View {
VStack(alignment: .leading) {
Text("Weather").font(.caption).foregroundColor(.secondary)
HStack {
Text("\(entry.temperature)°").font(.largeTitle).fontWeight(.bold)
Spacer()
Text(entry.condition).font(.body)
}
}.padding()
}
}
// 4. Widget configuration
@main
struct WeatherWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(kind: "WeatherWidget", provider: Provider()) { entry in
WeatherWidgetEntryView(entry: entry)
}
.configurationDisplayName("Weather")
.description("Shows the current temperature")
.supportedFamilies([.systemSmall, .systemMedium])
}
}
Key points: TimelineProvider determines when and what data to show; getTimeline creates a timeline with the .after(nextUpdate) update policy; StaticConfiguration connects the provider to the View. For dynamic data, IntentConfiguration is used with custom parameters.
Let's implement a similar weather widget in Kotlin with AppWidgetProvider and 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 = "Cloudy"
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 — provider registration
<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>
The widget_weather_info.xml file sets minimum sizes, interval, and layout: minWidth 160dp, minHeight 80dp, updatePeriodMillis 3600000 (1 hour). RemoteViews uses only supported components: LinearLayout, TextView, ImageView. Custom Views and ConstraintLayout are available since Android 5.0. GlanceLayout — an experimental Compose-based approach for RemoteViews — appeared in Android 15.
Frequently Asked Questions
iOS WidgetKit offers three fixed sizes (small, medium, large) and updates via TimelineProvider with a system budget. Android AppWidgetProvider uses flexible cell-based sizes, RemoteViews for rendering, and updates via updatePeriodMillis or WorkManager.
On iOS, the system controls updates — the minimum interval is 15–30 minutes, for critical data push-to-refresh is used. On Android, the interval is set in updatePeriodMillis (minimum 30 minutes) or via WorkManager for background tasks.
iOS supports small (170x170pt), medium (360x170pt), and large (360x380pt). Android uses a flexible cell grid: from 2x1 to 5x5, size depends on the launcher and OS version.
On iOS, animations inside widgets are not available — they are static elements. Android RemoteViews supports basic animations via setFloat() and ViewPropertyAnimator, but complex animations are not available.
With proper implementation — no. The system strictly limits widget resources: on iOS — update and memory budget (30–50 MB), on Android — minimum update interval. Problematic widgets are forcibly terminated by the system.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also