Widget là một phần tử giao diện người dùng nhỏ gọn trên màn hình chính iOS hoặc Android, hiển thị thông tin liên quan mà không cần mở ứng dụng. Widget xuất hiện trong iOS 14 (2020) với framework WidgetKit trên SwiftUI và đã tồn tại trên Android từ phiên bản 1.5 thông qua AppWidgetProvider trên Kotlin hoặc Java. Chúng cho phép người dùng xem thời tiết, tỷ giá hối đoái, lịch, ghi chú hoặc trạng thái nhiệm vụ trực tiếp trên màn hình chính, tiết kiệm thời gian và đơn giản hóa tương tác. Theo Apple, người dùng tương tác với widget trung bình 4–6 lần mỗi ngày và việc đặt widget trên màn hình chính làm tăng mức độ tương tác với ứng dụng lên 20–30%. Đối với Android, những con số này có thể so sánh được. Hãy cùng tìm hiểu cách widget hoạt động trên cả hai nền tảng với các ví dụ mã.
Những điểm chính
Widget là một phần tử giao diện người dùng nhỏ gọn được đặt trên màn hình chính của thiết bị di động để hiển thị thông tin liên quan từ ứng dụng. Không giống như ứng dụng đầy đủ, widget không yêu cầu khởi chạy — người dùng thấy dữ liệu ngay sau khi mở khóa màn hình. Khái niệm này bắt nguồn từ hệ điều hành máy tính để bàn (macOS Dashboard, Windows Sidebar), và trong môi trường di động, Android đã giới thiệu hỗ trợ widget ngay từ phiên bản đầu tiên (Android 1.5 Cupcake, 2009), trong khi Apple chỉ thêm chúng trong iOS 14.
Triển khai kỹ thuật khác nhau giữa các nền tảng. Widget iOS là một target riêng biệt trong Xcode, được biên dịch dưới dạng Widget Extension, chạy trong tiến trình riêng của nó. Widget Android là một BroadcastReceiver mở rộng AppWidgetProvider, hiển thị bố cục qua RemoteViews. Cả hai nền tảng đều áp đặt các hạn chế nghiêm ngặt về hiệu suất và bộ nhớ: hệ thống có thể xóa widget nếu vượt quá giới hạn. iOS giới hạn widget ở 30–50 MB và thời gian tạo timeline là 10 giây. Android — khoảng thời gian cập nhật không quá 30 phút qua updatePeriodMillis và buộc kết thúc nếu onUpdate bị treo.
Smart Stack trên iOS và ngăn xếp trên Android cho phép người dùng nhóm nhiều widget vào một khe trên màn hình chính. Thiết bị tự động chuyển đổi widget hiển thị dựa trên thời gian trong ngày, vị trí hoặc hoạt động của người dùng. Ví dụ, buổi sáng hiển thị lịch, ban ngày — thời tiết, buổi tối — ghi chú. Đối với nhà phát triển, điều này có nghĩa là widget phải hoạt động chính xác như một phần của ngăn xếp và không phụ thuộc vào việc hiển thị liên tục.
WidgetKit là framework của Apple để tạo widget trên iOS 14+, iPadOS 14+ và macOS 11+. Widget được viết bằng Swift sử dụng SwiftUI và cơ chế TimelineProvider để cập nhật nội dung. Kiến trúc WidgetKit bao gồm ba thành phần: TimelineProvider — một nguồn dữ liệu tạo thành dòng thời gian với các mục; Widget Entry View — một SwiftUI View hiển thị nội dung dựa trên một mục; Widget Configuration — mô tả về loại, kích thước và họ của widget.
WidgetKit tự động quản lý cập nhật — nhà phát triển không thể buộc widget cập nhật thường xuyên hơn mức hệ thống cho phép. Apple sử dụng ngân sách cập nhật phân phối giữa tất cả các widget trên thiết bị. Khoảng thời gian tối thiểu là 15–30 phút. Đối với dữ liệu quan trọng (ví dụ: trạng thái giao hàng), push-to-refresh được sử dụng — gửi thông báo đẩy với ngày trong tương lai có liên quan, sau đó WidgetKit yêu cầu một dòng thời gian mới. Bắt đầu từ iOS 17, widget hỗ trợ tương tác qua App Intents — người dùng có thể nhấn nút bên trong widget mà không cần mở ứng dụng.
| Kích thước | iOS (pt) | Nội dung |
|---|---|---|
| Small | 170 × 170 | 1–2 chỉ số: nhiệt độ, tỷ giá hối đoái |
| Medium | 360 × 170 | 2–4 chỉ số: dự báo 5 ngày, danh sách |
| Large | 360 × 380 | 5–10 chỉ số: lịch, nguồn cấp tin tức |
AppWidgetProvider là lớp cơ sở để tạo widget trên Android, một phần của Android SDK từ API level 3. Widget Android là một BroadcastReceiver nhận các sự kiện cập nhật, kích hoạt và vô hiệu hóa từ hệ thống. Không giống như iOS, nơi widget được hiển thị bằng SwiftUI trên công cụ riêng của nó, Android sử dụng RemoteViews — một tập hợp giới hạn các thành phần View được hiển thị trong tiến trình hệ thống (trình khởi chạy). Không thể sử dụng View tùy chỉnh, hoạt ảnh phức tạp hoặc thư viện UI.
Quy trình làm việc của widget Android bao gồm bốn giai đoạn. Cấu hình — khi thêm widget vào màn hình, hệ thống có thể khởi chạy một Activity cấu hình nơi người dùng chọn cài đặt. Cập nhật — hệ thống gọi onUpdate() theo khoảng thời gian đã định (tối thiểu 30 phút) hoặc theo tín hiệu từ ứng dụng qua WorkManager. Hiển thị — nhà cung cấp tạo RemoteViews với dữ liệu hiện tại và chuyển chúng qua AppWidgetManager. Xử lý chạm — widget hỗ trợ PendingIntent cho các nút: nhấp chuột mở một Activity hoặc thực hiện một hành động. Bắt đầu từ Android 12, widget đã có hỗ trợ góc bo tròn, màu sắc thích ứng Material You và thay đổi kích thước động.
Hãy tạo một widget thời tiết bằng Swift sử dụng WidgetKit và SwiftUI. Dự án phải bao gồm một target Widget Extension, được thêm trong Xcode qua File → New → Target → Widget Extension.
import WidgetKit
import SwiftUI
// 1. Mô hình dữ liệu cho widget
struct WeatherEntry: TimelineEntry {
let date: Date
let temperature: Int
let condition: String
}
// 2. TimelineProvider — nhà cung cấp dữ liệu
struct Provider: TimelineProvider {
func placeholder(in context: Context) -> WeatherEntry {
WeatherEntry(date: Date(), temperature: 22, condition: "Nắng")
}
func getSnapshot(in context: Context, completion: @escaping (WeatherEntry) -> Void) {
let entry = WeatherEntry(date: Date(), temperature: 22, condition: "Nắng")
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<WeatherEntry>) -> Void) {
let now = Date()
let entry = WeatherEntry(date: now, temperature: 20, condition: "Nhiều mây")
let nextUpdate = Calendar.current.date(byAdding: .hour, value: 3, to: now)!
let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
completion(timeline)
}
}
// 3. SwiftUI View để hiển thị
struct WeatherWidgetEntryView: View {
var entry: WeatherEntry
var body: some View {
VStack(alignment: .leading) {
Text("Thời tiết").font(.caption).foregroundColor(.secondary)
HStack {
Text("\(entry.temperature)°").font(.largeTitle).fontWeight(.bold)
Spacer()
Text(entry.condition).font(.body)
}
}.padding()
}
}
// 4. Cấu hình widget
@main
struct WeatherWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(kind: "WeatherWidget", provider: Provider()) { entry in
WeatherWidgetEntryView(entry: entry)
}
.configurationDisplayName("Thời tiết")
.description("Hiển thị nhiệt độ hiện tại")
.supportedFamilies([.systemSmall, .systemMedium])
}
}
Các điểm chính: TimelineProvider xác định khi nào và dữ liệu nào sẽ hiển thị; getTimeline tạo một dòng thời gian với chính sách cập nhật .after(nextUpdate); StaticConfiguration kết nối nhà cung cấp với View. Đối với dữ liệu động, IntentConfiguration được sử dụng với các tham số tùy chỉnh.
Hãy triển khai một widget thời tiết tương tự bằng Kotlin với AppWidgetProvider và 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 = "Nhiều mây"
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 — đăng ký nhà cung cấp
<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>
Tệp widget_weather_info.xml đặt kích thước tối thiểu, khoảng thời gian và bố cục: minWidth 160dp, minHeight 80dp, updatePeriodMillis 3600000 (1 giờ). RemoteViews chỉ sử dụng các thành phần được hỗ trợ: LinearLayout, TextView, ImageView. View tùy chỉnh và ConstraintLayout có sẵn từ Android 5.0. GlanceLayout — một cách tiếp cận thử nghiệm dựa trên Compose cho RemoteViews — xuất hiện trong Android 15.
Câu hỏi thường gặp
iOS WidgetKit cung cấp ba kích thước cố định (small, medium, large) và cập nhật qua TimelineProvider với ngân sách hệ thống. Android AppWidgetProvider sử dụng kích thước linh hoạt dựa trên ô, RemoteViews để hiển thị và cập nhật qua updatePeriodMillis hoặc WorkManager.
Trên iOS, hệ thống kiểm soát cập nhật — khoảng thời gian tối thiểu là 15–30 phút, đối với dữ liệu quan trọng, push-to-refresh được sử dụng. Trên Android, khoảng thời gian được đặt trong updatePeriodMillis (tối thiểu 30 phút) hoặc qua WorkManager cho các tác vụ nền.
iOS hỗ trợ small (170x170pt), medium (360x170pt) và large (360x380pt). Android sử dụng lưới ô linh hoạt: từ 2x1 đến 5x5, kích thước phụ thuộc vào trình khởi chạy và phiên bản hệ điều hành.
Trên iOS, hoạt ảnh bên trong widget không có sẵn — chúng là các phần tử tĩnh. Android RemoteViews hỗ trợ hoạt ảnh cơ bản qua setFloat() và ViewPropertyAnimator, nhưng hoạt ảnh phức tạp không có sẵn.
Với triển khai đúng cách — không. Hệ thống giới hạn nghiêm ngặt tài nguyên widget: trên iOS — ngân sách cập nhật và bộ nhớ (30–50 MB), trên Android — khoảng thời gian cập nhật tối thiểu. Widget có vấn đề sẽ bị hệ thống buộc kết thúc.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm