iOS Setup
On iOS Widgets are an App Extension. The following steps explain how to add the correct App Extension, how to do the basic configuration to read/display data you send from your App in the Widget and how to setup GroupIds to ensure the correct communication between your App and the Widget.
Add a Widget to your App in Xcode
Add a widget extension by going File > New > Target > Widget Extension
The generated Widget code includes the following classes:
TimelineProvider
- Provides a Timeline of entries at which the System will update the Widget automaticallyTimelineEntry
- Represents the Data Object used to build the Widget. Thedata
field is necessary and defines the point in time at which the Timeline would updateView
- The Widget itself, which is built with SwiftUIWidget
- Configuration: Make note of thekind
you set in the Configuration as this is what's needed to update the Widget from Flutter
Widget
In the Widget Configuration it is important to set the kind
to the same value as the name
/iOSName
in the updateWidget
function in Flutter
TimelineEntry / TimelineProvider
Adjust the TimelineEntry
to match your desired Data Structure you need to build your Widget. This entry is what is passed to the actual View
to build the Widget.TimelineEntry
struct CounterEntry: TimelineEntry {
let date: Date
let counter: Int
}
Adjust the TimelineProvider
to build a timeline (can be single entry if all updating is always handled from Flutter) where you create a TimelineEntry
based on the Data stored with HomeWidget.saveWidgetData
struct Provider: TimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), counter: 0)
}
func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let prefs = UserDefaults(suiteName: "group.YOUR_GROUP_ID")
let counter = prefs?.integer(forKey: "counter")
let entry = CounterEntry(date: Date(), counter: counter ?? 0)
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
getSnapshot(in: context) { (entry) in
let timeline = Timeline(entries: [entry], policy: .atEnd)
completion(timeline)
}
}
}
View
In the View you build your Layout of the Widget. Using the TimelineEntry
you can access the Data you stored in UserDefaults
and build your Widget accordingly
struct WidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
VStack {
Text(entry.counter.description)
}
}
}
For more Information on how to build Widgets in iOS, check out the Apple Documentation
GroupId
home_widget syncs data between your App and the Widget using App Groups.
Note: in order to add groupIds you need a paid Apple Developer Account
Go to your Apple Developer Account and add a new group. Add this group to your Runner and the Widget Extension inside XCode: Signing & Capabilities > App Groups > +.
To swap between your App, and the Extension change the Target)
Setup in Flutter
For iOS, you need to call HomeWidget.setAppGroupId('YOUR_GROUP_ID');
Without this you won't be able to share data between your App and the Widget and calls to saveWidgetData
and getWidgetData
will return an error
Sync CFBundleVersion (optional)
This step is optional, this will sync the widget extension build version with your app version, so you don't get warnings of mismatch version from App Store Connect when uploading your app.
In your Runner (app) target go to Build Phases > + > New Run Script Phase and add the following script:
generatedPath="$SRCROOT/Flutter/Generated.xcconfig"
# Read and trim versionNumber and buildNumber
versionNumber=$(grep FLUTTER_BUILD_NAME "$generatedPath" | cut -d '=' -f2 | xargs)
buildNumber=$(grep FLUTTER_BUILD_NUMBER "$generatedPath" | cut -d '=' -f2 | xargs)
infoPlistPath="$SRCROOT/HomeExampleWidget/Info.plist"
# Check and add CFBundleVersion if it does not exist
/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "$infoPlistPath" 2>/dev/null
if [ $? != 0 ]; then
/usr/libexec/PlistBuddy -c "Add :CFBundleVersion string $buildNumber" "$infoPlistPath"
else
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$infoPlistPath"
fi
# Check and add CFBundleShortVersionString if it does not exist
/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$infoPlistPath" 2>/dev/null
if [ $? != 0 ]; then
/usr/libexec/PlistBuddy -c "Add :CFBundleShortVersionString string $versionNumber" "$infoPlistPath"
else
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $versionNumber" "$infoPlistPath"
fi
Replace HomeExampleWidget
with the name of the widget extension folder that you have created.