-
-
Notifications
You must be signed in to change notification settings - Fork 285
feat: Add support for HomeWidget.saveFile and HomeWidget.saveImage
#409
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
ec23c42
feat: add support for saving files and images
ABausG 5495e6b
fix: ios integration test
ABausG cc17382
feat: also delete files when saving data with `null`
ABausG 148dafd
feat: add example for image
ABausG 635bb13
feat: add example for image and file handling
ABausG 5529d1e
docs: mention the example in the docs
ABausG 826bf56
style: formatting
ABausG 21efec8
style: ignore kts files from spellcheck
ABausG 31962a2
docs: flesh out documentation of example
ABausG a25da48
chore: ensure null directories throw
ABausG a5e6fe0
fix: fix integration tests
ABausG 517d384
chore: update cspell
ABausG 269b635
chore: address ai review
ABausG File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| --- | ||
| title: Save files and images | ||
| description: Use saveFile and saveImage to store files for Home Screen widgets | ||
| --- | ||
|
|
||
| # Save files and images | ||
|
|
||
| Besides primitive values via [`saveWidgetData`](/usage/sync-data), you can write **files** into the same shared area your app and widgets use. The absolute path is stored under your chosen key so native widget code can load the file (for example with `UIImage(contentsOfFile:)` or `BitmapFactory.decodeFile`). | ||
|
|
||
| ## Flutter | ||
|
|
||
| ### `saveFile` | ||
|
|
||
| Write arbitrary bytes and register the path: | ||
|
|
||
| ```dart | ||
| import 'dart:convert'; | ||
| import 'dart:typed_data'; | ||
|
|
||
| final json = jsonEncode({'count': 3}); | ||
| final path = await HomeWidget.saveFile( | ||
| 'stats', | ||
| Uint8List.fromList(utf8.encode(json)), | ||
| extension: 'json', | ||
| ); | ||
| ``` | ||
|
|
||
| - The file is written as `{container}/home_widget/{key}.{extension}`. | ||
| - On **iOS** the container is the [app group](/setup/ios) (call `setAppGroupId` first). | ||
| - On **Android** it is under the application support directory, in a `home_widget` subdirectory. | ||
|
|
||
| `key` must be non-empty and must not contain `/`, `\`, `..`, or spaces. `extension` is normalized (e.g. `json` or `.json`); it must not contain path separators. These rules are enforced with **assertions** in debug builds. | ||
|
|
||
| ### `saveImage` | ||
|
|
||
| Decode an [`ImageProvider`](https://api.flutter.dev/flutter/painting/ImageProvider-class.html) and save the **first frame** as PNG via `saveFile`: | ||
|
|
||
| ```dart | ||
| await HomeWidget.saveImage( | ||
| 'avatar', | ||
| NetworkImage('https://example.com/photo.png'), | ||
| ); | ||
| ``` | ||
|
|
||
| Animated images use the first frame only. | ||
|
|
||
| ### Relationship to `renderFlutterWidget` | ||
|
|
||
| [`renderFlutterWidget`](/features/render-flutter-widgets) renders a widget to a PNG and uses the same storage and `saveWidgetData` path behavior as `saveFile` with extension `png`. | ||
|
|
||
| ## Native widgets | ||
|
|
||
| Use the same approach as for rendered Flutter widgets: read the key with your platform’s widget APIs to get the path string, then load that path as a file or image. See the [iOS](/setup/ios) and [Android](/setup/android) setup guides for accessing shared data from the widget extension or `AppWidgetProvider`. | ||
|
|
||
| ## Example | ||
|
|
||
| See the **[file_and_images](https://github.com/ABausG/home_widget/tree/main/examples/file_and_images)** example app on GitHub for how to use `saveFile`, `saveImage`, and load the stored files from Android and iOS home screen widgets. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| # Miscellaneous | ||
| *.class | ||
| *.log | ||
| *.pyc | ||
| *.swp | ||
| .DS_Store | ||
| .atom/ | ||
| .build/ | ||
| .buildlog/ | ||
| .history | ||
| .svn/ | ||
| .swiftpm/ | ||
| migrate_working_dir/ | ||
|
|
||
| # IntelliJ related | ||
| *.iml | ||
| *.ipr | ||
| *.iws | ||
| .idea/ | ||
|
|
||
| # The .vscode folder contains launch configuration and tasks you configure in | ||
| # VS Code which you may wish to be included in version control, so this line | ||
| # is commented out by default. | ||
| #.vscode/ | ||
|
|
||
| # Flutter/Dart/Pub related | ||
| **/doc/api/ | ||
| **/ios/Flutter/.last_build_id | ||
| .dart_tool/ | ||
| .flutter-plugins-dependencies | ||
| .pub-cache/ | ||
| .pub/ | ||
| /build/ | ||
| /coverage/ | ||
|
|
||
| # Symbolication related | ||
| app.*.symbols | ||
|
|
||
| # Obfuscation related | ||
| app.*.map.json | ||
|
|
||
| # Android Studio will place build artifacts here | ||
| /android/app/debug | ||
| /android/app/profile | ||
| /android/app/release |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # This file tracks properties of this Flutter project. | ||
| # Used by Flutter tool to assess capabilities and perform upgrades etc. | ||
| # | ||
| # This file should be version controlled and should not be manually edited. | ||
|
|
||
| version: | ||
| revision: "f6ff1529fd6d8af5f706051d9251ac9231c83407" | ||
| channel: "stable" | ||
|
|
||
| project_type: app | ||
|
|
||
| # Tracks metadata for the flutter migrate command | ||
| migration: | ||
| platforms: | ||
| - platform: root | ||
| create_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407 | ||
| base_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407 | ||
| - platform: android | ||
| create_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407 | ||
| base_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407 | ||
| - platform: ios | ||
| create_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407 | ||
| base_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407 | ||
|
|
||
| # User provided section | ||
|
|
||
| # List of Local paths (relative to this file) that should be | ||
| # ignored by the migrate tool. | ||
| # | ||
| # Files that are not part of the templates will be ignored by default. | ||
| unmanaged_files: | ||
| - 'lib/main.dart' | ||
| - 'ios/Runner.xcodeproj/project.pbxproj' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| # File and images | ||
|
|
||
| A simple demo app showing how to save images and files from Flutter and access the data from native widgets. | ||
|
|
||
| **Flutter (save)** — [`lib/main.dart`](lib/main.dart): | ||
|
|
||
| ```dart | ||
| await HomeWidget.saveImage(_imageKey, imageProvider); | ||
| await HomeWidget.saveWidgetData(_imageTypeKey, imageType.name); | ||
| ``` | ||
|
|
||
| ```dart | ||
| await HomeWidget.saveFile( | ||
| _fileJsonKey, | ||
| Uint8List.fromList(utf8.encode(json)), | ||
| extension: 'json', | ||
| ); | ||
| ``` | ||
|
|
||
| **Android (path → file)** — [`ImageWidgetHomeWidget.kt`](android/app/src/main/kotlin/es/antonborri/file_and_images/ImageWidgetHomeWidget.kt), [`FileWidgetHomeWidget.kt`](android/app/src/main/kotlin/es/antonborri/file_and_images/FileWidgetHomeWidget.kt): | ||
|
|
||
| ```kotlin | ||
| val imagePath = prefs.getString(IMAGE_KEY, null) | ||
| BitmapFactory.decodeFile(imagePath) // after `File(path).isFile` check | ||
| ``` | ||
|
|
||
| ```kotlin | ||
| val jsonPath = prefs.getString(FILE_JSON_KEY, null) | ||
| File(jsonPath).readText(Charsets.UTF_8) // when path exists and is a file | ||
| ``` | ||
|
|
||
| **iOS (path → file)** — [`ImageWidgetHomeWidget/Widget.swift`](ios/ImageWidgetHomeWidget/Widget.swift), [`FileWidgetHomeWidget/Widget.swift`](ios/FileWidgetHomeWidget/Widget.swift): | ||
|
|
||
| ```swift | ||
| UserDefaults(suiteName: appGroupId)?.string(forKey: imageKey) // then UIImage(contentsOfFile:) | ||
| ``` | ||
|
|
||
| ```swift | ||
| UserDefaults(suiteName: appGroupId)?.string(forKey: fileJsonKey) // then Data(contentsOf: URL(fileURLWithPath:)) | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| include: package:flutter_lints/flutter.yaml |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| gradle-wrapper.jar | ||
| /.gradle | ||
| /captures/ | ||
| /gradlew | ||
| /gradlew.bat | ||
| /local.properties | ||
| GeneratedPluginRegistrant.java | ||
| .cxx/ | ||
|
|
||
| # Remember to never publicly share your keystore. | ||
| # See https://flutter.dev/to/reference-keystore | ||
| key.properties | ||
| **/*.keystore | ||
| **/*.jks |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| plugins { | ||
| id("com.android.application") | ||
| id("kotlin-android") | ||
| id("org.jetbrains.kotlin.plugin.compose") version "2.2.20" | ||
| // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. | ||
| id("dev.flutter.flutter-gradle-plugin") | ||
| } | ||
|
|
||
| android { | ||
| namespace = "es.antonborri.file_and_images" | ||
| compileSdk = flutter.compileSdkVersion | ||
| ndkVersion = flutter.ndkVersion | ||
|
|
||
| compileOptions { | ||
| sourceCompatibility = JavaVersion.VERSION_17 | ||
| targetCompatibility = JavaVersion.VERSION_17 | ||
| } | ||
|
|
||
| kotlinOptions { jvmTarget = JavaVersion.VERSION_17.toString() } | ||
|
|
||
| defaultConfig { | ||
| // TODO: Specify your own unique Application ID | ||
| // (https://developer.android.com/studio/build/application-id.html). | ||
| applicationId = "es.antonborri.file_and_images" | ||
| // You can update the following values to match your application needs. | ||
| // For more information, see: https://flutter.dev/to/review-gradle-config. | ||
| minSdk = flutter.minSdkVersion | ||
| targetSdk = flutter.targetSdkVersion | ||
| versionCode = flutter.versionCode | ||
| versionName = flutter.versionName | ||
| } | ||
|
|
||
| buildTypes { | ||
| release { | ||
| // TODO: Add your own signing config for the release build. | ||
| // Signing with the debug keys for now, so `flutter run --release` works. | ||
| signingConfig = signingConfigs.getByName("debug") | ||
| } | ||
| } | ||
| buildFeatures { compose = true } | ||
| } | ||
|
|
||
| flutter { source = "../.." } | ||
|
|
||
| dependencies { implementation("androidx.glance:glance-appwidget:1.1.1") } |
7 changes: 7 additions & 0 deletions
7
examples/file_and_images/android/app/src/debug/AndroidManifest.xml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||
| <!-- The INTERNET permission is required for development. Specifically, | ||
| the Flutter tool needs it to communicate with the running application | ||
| to allow setting breakpoints, to provide hot reload, etc. | ||
| --> | ||
| <uses-permission android:name="android.permission.INTERNET"/> | ||
| </manifest> |
66 changes: 66 additions & 0 deletions
66
examples/file_and_images/android/app/src/main/AndroidManifest.xml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||
| <application | ||
| android:label="file_and_images" | ||
| android:name="${applicationName}" | ||
| android:icon="@mipmap/ic_launcher"> | ||
| <activity | ||
| android:name=".MainActivity" | ||
| android:exported="true" | ||
| android:launchMode="singleTop" | ||
| android:taskAffinity="" | ||
| android:theme="@style/LaunchTheme" | ||
| android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" | ||
| android:hardwareAccelerated="true" | ||
| android:windowSoftInputMode="adjustResize"> | ||
| <!-- Specifies an Android theme to apply to this Activity as soon as | ||
| the Android process has started. This theme is visible to the user | ||
| while the Flutter UI initializes. After that, this theme continues | ||
| to determine the Window background behind the Flutter UI. --> | ||
| <meta-data | ||
| android:name="io.flutter.embedding.android.NormalTheme" | ||
| android:resource="@style/NormalTheme" /> | ||
| <intent-filter> | ||
| <action android:name="android.intent.action.MAIN" /> | ||
| <category android:name="android.intent.category.LAUNCHER" /> | ||
| </intent-filter> | ||
| </activity> | ||
| <!-- Don't delete the meta-data below. | ||
| This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> | ||
| <meta-data | ||
| android:name="flutterEmbedding" | ||
| android:value="2" /> | ||
| <receiver | ||
| android:name="es.antonborri.file_and_images.ImageWidgetHomeWidgetReceiver" | ||
| android:label="ImageWidgetHomeWidget" | ||
| 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/image_widget_home_widget" /> | ||
| </receiver> | ||
| <receiver | ||
| android:name="es.antonborri.file_and_images.FileWidgetHomeWidgetReceiver" | ||
| android:label="FileWidgetHomeWidget" | ||
| 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/file_widget_home_widget" /> | ||
| </receiver> | ||
| </application> | ||
| <!-- Required to query activities that can process text, see: | ||
| https://developer.android.com/training/package-visibility and | ||
| https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT. | ||
|
|
||
| In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. --> | ||
| <queries> | ||
| <intent> | ||
| <action android:name="android.intent.action.PROCESS_TEXT" /> | ||
| <data android:mimeType="text/plain" /> | ||
| </intent> | ||
| </queries> | ||
| </manifest> |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.