glance_widget_ios
iOS implementation of the glance_widget plugin using WidgetKit.
Features
- Three widget templates: Simple, Progress, and List
- Instant updates when app is in foreground (no budget limit!)
- Theme support with dark/light modes
- Widget tap actions sent back to Flutter
- Widget Push Updates support (iOS 26+)
Requirements
- iOS 16.0 or higher
- Xcode 15.0 or higher
- Swift 5.0 or higher
Installation
This package is automatically included when you add glance_widget to your Flutter project and run on iOS.
dependencies:
glance_widget: ^0.1.0
Setup
1. Create Widget Extension
In Xcode:
- Open your iOS project (
ios/Runner.xcworkspace) - File → New → Target → Widget Extension
- Name it
GlanceWidgets - Uncheck "Include Configuration App Intent" (optional)
2. Configure App Groups
Both the main app and widget extension need the same App Group:
- Select your main app target → Signing & Capabilities → + Capability → App Groups
- Add:
group.com.yourcompany.yourapp - Select widget extension target → repeat steps 1-2 with the same group ID
3. Configure URL Scheme
In ios/Runner/Info.plist, add:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>glancewidget</string>
</array>
<key>CFBundleURLName</key>
<string>com.yourcompany.yourapp.widget.action</string>
</dict>
</array>
4. Implement Widget Extension
Create your widget views in the extension. Example SimpleWidget.swift:
import WidgetKit
import SwiftUI
struct SimpleWidgetProvider: TimelineProvider {
func placeholder(in context: Context) -> SimpleWidgetEntry {
SimpleWidgetEntry(date: Date(), title: "Title", value: "--")
}
func getSnapshot(in context: Context, completion: @escaping (SimpleWidgetEntry) -> Void) {
let entry = loadFromStorage()
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<SimpleWidgetEntry>) -> Void) {
let entry = loadFromStorage()
// Use .never policy - updates only when app calls reloadTimelines
let timeline = Timeline(entries: [entry], policy: .never)
completion(timeline)
}
private func loadFromStorage() -> SimpleWidgetEntry {
let defaults = UserDefaults(suiteName: "group.com.yourcompany.yourapp")
// Load data from shared storage...
}
}
Update Strategy
App in Foreground (Instant, No Budget!)
When your Flutter app is in the foreground, widget updates are instant and have no budget limit:
// This triggers instant update when app is visible
await GlanceWidget.simple(
id: 'price',
title: 'Bitcoin',
value: '\$45,000',
);
App in Background (Timeline)
When the app is in background, iOS uses timeline-based updates with a daily budget.
Server-Triggered (iOS 26+)
For server-triggered updates without opening the app:
// Get the push token
final token = await GlanceWidget.getWidgetPushToken();
if (token != null) {
// Send to your server for APNs push
}
Server sends APNs request:
POST https://api.push.apple.com/3/device/{token}
Headers:
apns-push-type: widgets
apns-topic: com.yourcompany.yourapp.push-type.widgets
Body:
{"aps": {"content-changed": true}}
Troubleshooting
Widget not updating?
- Ensure App Groups are configured correctly in both targets
- Check that the App Group ID matches in your code
- Verify the widget extension is properly linked
Widget shows placeholder?
- The widget extension can't find data in shared storage
- Verify UserDefaults suite name matches App Group ID
- Try force refreshing:
GlanceWidget.forceRefreshAll()
More Information
Libraries
- glance_widget_ios
- iOS implementation of the glance_widget plugin.