├── app_widget
├── lib
│ ├── app_widget.dart
│ └── src
│ │ └── app_widget_plugin.dart
├── .fvm
│ └── fvm_config.json
├── assets
│ ├── example_app.gif
│ └── screen_shot.webp
├── example
│ ├── android
│ │ ├── gradle.properties
│ │ ├── app
│ │ │ ├── src
│ │ │ │ ├── main
│ │ │ │ │ ├── res
│ │ │ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ │ ├── drawable-v21
│ │ │ │ │ │ │ ├── widget_background.xml
│ │ │ │ │ │ │ └── launch_background.xml
│ │ │ │ │ │ ├── drawable
│ │ │ │ │ │ │ ├── widget_background.xml
│ │ │ │ │ │ │ └── launch_background.xml
│ │ │ │ │ │ ├── drawable-night
│ │ │ │ │ │ │ ├── widget_background.xml
│ │ │ │ │ │ │ └── launch_background.xml
│ │ │ │ │ │ ├── xml
│ │ │ │ │ │ │ └── app_widget_example_info.xml
│ │ │ │ │ │ ├── values
│ │ │ │ │ │ │ └── styles.xml
│ │ │ │ │ │ ├── values-night
│ │ │ │ │ │ │ └── styles.xml
│ │ │ │ │ │ └── layout
│ │ │ │ │ │ │ └── example_layout.xml
│ │ │ │ │ ├── kotlin
│ │ │ │ │ │ └── tech
│ │ │ │ │ │ │ └── noxasch
│ │ │ │ │ │ │ └── app_widget_example
│ │ │ │ │ │ │ ├── MainActivity.kt
│ │ │ │ │ │ │ ├── AppWidgetExampleDiffProvider.kt
│ │ │ │ │ │ │ └── AppWidgetExampleProvider.kt
│ │ │ │ │ └── AndroidManifest.xml
│ │ │ │ ├── debug
│ │ │ │ │ └── AndroidManifest.xml
│ │ │ │ └── profile
│ │ │ │ │ └── AndroidManifest.xml
│ │ │ └── build.gradle
│ │ ├── gradle
│ │ │ └── wrapper
│ │ │ │ └── gradle-wrapper.properties
│ │ ├── .gitignore
│ │ ├── settings.gradle
│ │ └── build.gradle
│ ├── README.md
│ ├── .gitignore
│ ├── pubspec.yaml
│ ├── test
│ │ └── widget_test.dart
│ ├── analysis_options.yaml
│ ├── integration_test
│ │ ├── android_test_2.dart
│ │ ├── android_test.dart
│ │ └── android_diff_package_name_test.dart
│ ├── pubspec.lock
│ └── lib
│ │ └── main.dart
├── .gitignore
├── pubspec.yaml
├── .metadata
├── LICENSE
├── CHANGELOG.md
├── test
│ ├── app_widget_ios_test.dart
│ └── app_widget_test.dart
└── README.md
├── app_widget_android
├── android
│ ├── settings.gradle
│ ├── .gitignore
│ ├── src
│ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── kotlin
│ │ │ └── tech
│ │ │ └── noxasch
│ │ │ └── app_widget
│ │ │ ├── AppWidgetPlugin.kt
│ │ │ └── AppWidgetMethodCallHandler.kt
│ └── build.gradle
├── example
│ ├── android
│ │ ├── gradle.properties
│ │ ├── app
│ │ │ ├── src
│ │ │ │ ├── main
│ │ │ │ │ ├── res
│ │ │ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ │ ├── drawable
│ │ │ │ │ │ │ └── launch_background.xml
│ │ │ │ │ │ ├── drawable-v21
│ │ │ │ │ │ │ └── launch_background.xml
│ │ │ │ │ │ ├── values
│ │ │ │ │ │ │ └── styles.xml
│ │ │ │ │ │ └── values-night
│ │ │ │ │ │ │ └── styles.xml
│ │ │ │ │ ├── kotlin
│ │ │ │ │ │ └── tech
│ │ │ │ │ │ │ └── noxasch
│ │ │ │ │ │ │ └── app_widget_android_example
│ │ │ │ │ │ │ └── MainActivity.kt
│ │ │ │ │ └── AndroidManifest.xml
│ │ │ │ ├── debug
│ │ │ │ │ └── AndroidManifest.xml
│ │ │ │ └── profile
│ │ │ │ │ └── AndroidManifest.xml
│ │ │ └── build.gradle
│ │ ├── gradle
│ │ │ └── wrapper
│ │ │ │ └── gradle-wrapper.properties
│ │ ├── .gitignore
│ │ ├── settings.gradle
│ │ └── build.gradle
│ ├── README.md
│ ├── lib
│ │ └── main.dart
│ ├── .gitignore
│ ├── test
│ │ └── widget_test.dart
│ ├── analysis_options.yaml
│ ├── pubspec.yaml
│ └── pubspec.lock
├── lib
│ ├── app_widget_android.dart
│ └── src
│ │ ├── app_widget_android_platform.dart
│ │ └── app_widget_android_plugin.dart
├── README.md
├── .gitignore
├── pubspec.yaml
├── .metadata
├── LICENSE
├── CHANGELOG.md
└── test
│ └── app_widget_android_test.dart
├── app_widget_platform_interface
├── lib
│ ├── app_widget_platform_interface.dart
│ └── src
│ │ └── app_widget_platform.dart
├── pubspec.yaml
├── .gitignore
├── README.md
├── .metadata
├── CHANGELOG.md
└── LICENSE
├── .github
├── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
└── workflows
│ ├── android.yaml
│ ├── interface.yaml
│ └── main.yaml
├── README.md
├── LICENSE
└── analysis_options.yaml
/app_widget/lib/app_widget.dart:
--------------------------------------------------------------------------------
1 | export 'src/app_widget_plugin.dart';
2 |
--------------------------------------------------------------------------------
/app_widget_android/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'app_widget_android'
2 |
--------------------------------------------------------------------------------
/app_widget/.fvm/fvm_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "flutterSdkVersion": "3.13.8",
3 | "flavors": {}
4 | }
--------------------------------------------------------------------------------
/app_widget_platform_interface/lib/app_widget_platform_interface.dart:
--------------------------------------------------------------------------------
1 | export 'src/app_widget_platform.dart';
2 |
--------------------------------------------------------------------------------
/app_widget/assets/example_app.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noxasch/flutter_app_widget/HEAD/app_widget/assets/example_app.gif
--------------------------------------------------------------------------------
/app_widget/assets/screen_shot.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noxasch/flutter_app_widget/HEAD/app_widget/assets/screen_shot.webp
--------------------------------------------------------------------------------
/app_widget/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/app_widget_android/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/app_widget_android/lib/app_widget_android.dart:
--------------------------------------------------------------------------------
1 | export 'src/app_widget_android_platform.dart';
2 | export 'src/app_widget_android_plugin.dart';
3 |
--------------------------------------------------------------------------------
/app_widget_android/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .cxx
10 |
--------------------------------------------------------------------------------
/app_widget_android/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/app_widget/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noxasch/flutter_app_widget/HEAD/app_widget/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_widget/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noxasch/flutter_app_widget/HEAD/app_widget/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_widget/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noxasch/flutter_app_widget/HEAD/app_widget/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_widget/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noxasch/flutter_app_widget/HEAD/app_widget/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_widget/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noxasch/flutter_app_widget/HEAD/app_widget/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_widget_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noxasch/flutter_app_widget/HEAD/app_widget_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_widget_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noxasch/flutter_app_widget/HEAD/app_widget_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_widget_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noxasch/flutter_app_widget/HEAD/app_widget_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_widget_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noxasch/flutter_app_widget/HEAD/app_widget_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_widget_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/noxasch/flutter_app_widget/HEAD/app_widget_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app_widget_android/example/android/app/src/main/kotlin/tech/noxasch/app_widget_android_example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package tech.noxasch.app_widget_example
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/app_widget/example/android/app/src/main/res/drawable-v21/widget_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app_widget/example/android/app/src/main/res/drawable/widget_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app_widget/example/android/app/src/main/res/drawable-night/widget_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app_widget/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
7 |
--------------------------------------------------------------------------------
/app_widget_android/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
7 |
--------------------------------------------------------------------------------
/app_widget/example/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 | **/*.keystore
13 | **/*.jks
14 |
--------------------------------------------------------------------------------
/app_widget_android/example/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 | **/*.keystore
13 | **/*.jks
14 |
--------------------------------------------------------------------------------
/app_widget_android/lib/src/app_widget_android_platform.dart:
--------------------------------------------------------------------------------
1 | import 'package:app_widget_platform_interface/app_widget_platform_interface.dart';
2 |
3 | class AppWidgetAndroid extends AppWidgetPlatform {
4 | // this is use by flutter pluginRegistrant
5 | // this will register with no callback
6 | // no methodChannel are allowed since this are call before the app
7 | static void registerWith() {
8 | AppWidgetPlatform.instance = AppWidgetAndroid();
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/app_widget/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app_widget/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app_widget_android/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app_widget_android/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app_widget/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/app_widget_android/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/app_widget/example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/app_widget/example/android/app/src/main/res/drawable-night/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/app_widget/example/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/app_widget_android/example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/app_widget_android/example/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/app_widget/example/android/app/src/main/kotlin/tech/noxasch/app_widget_example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package tech.noxasch.app_widget_example
2 |
3 | import android.appwidget.AppWidgetManager
4 | import io.flutter.embedding.android.FlutterActivity
5 | import io.flutter.embedding.engine.FlutterEngine
6 | import tech.noxasch.app_widget.AppWidgetPlugin
7 |
8 | // this need to be implemented manually
9 | class MainActivity: FlutterActivity() {
10 | override fun onFlutterUiDisplayed() {
11 | super.onFlutterUiDisplayed()
12 |
13 | AppWidgetPlugin.Companion.handleWidgetAction(context, intent)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app_widget/example/android/app/src/main/res/xml/app_widget_example_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
--------------------------------------------------------------------------------
/app_widget_platform_interface/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: app_widget_platform_interface
2 | description: Common platform interface for app_widget plugin.
3 | version: 0.4.0
4 | homepage: https://noxasch.tech/
5 | repository: https://github.com/noxasch/flutter_app_widget/tree/master/app_widget_platform_interface
6 | issue_tracker: https://github.com/noxasch/flutter_app_widget/issues
7 |
8 | environment:
9 | sdk: ">=2.17.6 <4.0.0"
10 | flutter: ">=2.5.0"
11 |
12 | dependencies:
13 | flutter:
14 | sdk: flutter
15 | plugin_platform_interface: ^2.1.7
16 |
17 | dev_dependencies:
18 | flutter_test:
19 | sdk: flutter
20 | flutter_lints: ^2.0.0
21 |
--------------------------------------------------------------------------------
/app_widget/example/README.md:
--------------------------------------------------------------------------------
1 | # app_widget_example
2 |
3 | Demonstrates how to use the app_widget plugin.
4 |
5 | ## Getting Started
6 |
7 | This project is a starting point for a Flutter application.
8 |
9 | A few resources to get you started if this is your first Flutter project:
10 |
11 | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
13 |
14 | For help getting started with Flutter development, view the
15 | [online documentation](https://docs.flutter.dev/), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/app_widget_android/README.md:
--------------------------------------------------------------------------------
1 | # app_widget_android
2 |
3 | Android implementation for [app_widget](https://pub.dev/packages/app_widget) plugin. This package are not meant to be used
4 | on it's own.
5 |
6 | ## Getting Started
7 |
8 | This project is a starting point for a Flutter
9 | [plug-in package](https://flutter.dev/developing-packages/),
10 | a specialized package that includes platform-specific implementation code for
11 | Android and/or iOS.
12 |
13 | For help getting started with Flutter development, view the
14 | [online documentation](https://flutter.dev/docs), which offers tutorials,
15 | samples, guidance on mobile development, and a full API reference.
16 |
17 |
--------------------------------------------------------------------------------
/app_widget_android/example/README.md:
--------------------------------------------------------------------------------
1 | # app_widget_android_example
2 |
3 | Demonstrates how to use the app_widget_android plugin.
4 |
5 | ## Getting Started
6 |
7 | This project is a starting point for a Flutter application.
8 |
9 | A few resources to get you started if this is your first Flutter project:
10 |
11 | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
13 |
14 | For help getting started with Flutter development, view the
15 | [online documentation](https://docs.flutter.dev/), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/app_widget_android/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # The .vscode folder contains launch configuration and tasks you configure in
20 | # VS Code which you may wish to be included in version control, so this line
21 | # is commented out by default.
22 | #.vscode/
23 |
24 | # Flutter/Dart/Pub related
25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
26 | /pubspec.lock
27 | **/doc/api/
28 | .dart_tool/
29 | .packages
30 | build/
31 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: feature-request
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/app_widget_platform_interface/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # The .vscode folder contains launch configuration and tasks you configure in
20 | # VS Code which you may wish to be included in version control, so this line
21 | # is commented out by default.
22 | #.vscode/
23 |
24 | # Flutter/Dart/Pub related
25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
26 | /pubspec.lock
27 | **/doc/api/
28 | .dart_tool/
29 | .packages
30 | build/
31 |
32 | .fvm/flutter_sdk
33 |
--------------------------------------------------------------------------------
/app_widget/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.6.10'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.1.2'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | tasks.register("clean", Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/app_widget_android/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.6.10'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.1.2'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/app_widget_android/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | void main() {
4 | runApp(const MyApp());
5 | }
6 |
7 | class MyApp extends StatefulWidget {
8 | const MyApp({Key? key}) : super(key: key);
9 |
10 | @override
11 | State createState() => _MyAppState();
12 | }
13 |
14 | class _MyAppState extends State {
15 | @override
16 | void initState() {
17 | super.initState();
18 | }
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | return MaterialApp(
23 | home: Scaffold(
24 | appBar: AppBar(
25 | title: const Text('Plugin example app'),
26 | ),
27 | body: const Center(
28 | child: Text('Running on AppWidgetAndroid'),
29 | ),
30 | ),
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app_widget/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # The .vscode folder contains launch configuration and tasks you configure in
20 | # VS Code which you may wish to be included in version control, so this line
21 | # is commented out by default.
22 | #.vscode/
23 |
24 | # Flutter/Dart/Pub related
25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
26 | /pubspec.lock
27 | **/doc/api/
28 | .dart_tool/
29 | .packages
30 | build/
31 | .idea
32 |
33 | # fvm
34 | .fvm/flutter_sdk
35 |
36 | coverage
37 |
38 | .flutter-plugins
39 | .flutter-plugins-dependencies
40 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: "[BUG]"
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Platform (please complete the following information):**
27 | - Device: [e.g. iPhone6]
28 | - OS: [e.g. iOS8.1]
29 | - Browser [e.g. stock browser, safari]
30 | - Version [e.g. 22]
31 |
32 | Development details
33 | - flutter version
34 | - plugins version
35 |
36 | **Additional context**
37 | Add any other context about the problem here.
38 |
--------------------------------------------------------------------------------
/app_widget/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # The .vscode folder contains launch configuration and tasks you configure in
20 | # VS Code which you may wish to be included in version control, so this line
21 | # is commented out by default.
22 | #.vscode/
23 |
24 | # Flutter/Dart/Pub related
25 | **/doc/api/
26 | **/ios/Flutter/.last_build_id
27 | .dart_tool/
28 | .flutter-plugins
29 | .flutter-plugins-dependencies
30 | .packages
31 | .pub-cache/
32 | .pub/
33 | /build/
34 |
35 | # Web related
36 | lib/generated_plugin_registrant.dart
37 |
38 | # Symbolication related
39 | app.*.symbols
40 |
41 | # Obfuscation related
42 | app.*.map.json
43 |
44 | # Android Studio will place build artifacts here
45 | /android/app/debug
46 | /android/app/profile
47 | /android/app/release
48 |
--------------------------------------------------------------------------------
/app_widget/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: app_widget_example
2 | description: Demonstrates how to use the app_widget plugin.
3 |
4 | # The following line prevents the package from being accidentally published to
5 | # pub.dev using `flutter pub publish`. This is preferred for private packages.
6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
7 |
8 | environment:
9 | sdk: ">=2.17.6 <3.0.0"
10 | dependencies:
11 | flutter:
12 | sdk: flutter
13 |
14 | app_widget:
15 | path: ../
16 |
17 | # The following adds the Cupertino Icons font to your application.
18 | # Use with the CupertinoIcons class for iOS style icons.
19 | cupertino_icons: ^1.0.2
20 | # url_launcher: ^6.1.5
21 | # workmanager: ^0.5.0
22 |
23 | dev_dependencies:
24 | flutter_test:
25 | sdk: flutter
26 | integration_test:
27 | sdk: flutter
28 | flutter_driver:
29 | sdk: flutter
30 |
31 | flutter_lints: ^2.0.0
32 |
33 | flutter:
34 | uses-material-design: true
35 |
--------------------------------------------------------------------------------
/app_widget_android/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: app_widget_android
2 | description: Android implementation for app_widget plugin
3 | version: 0.4.0
4 | homepage: https://noxasch.tech/
5 | repository: https://github.com/noxasch/flutter_app_widget/tree/master/app_widget_android
6 | issue_tracker: https://github.com/noxasch/flutter_app_widget/issues
7 |
8 | environment:
9 | sdk: ">=2.17.6 <4.0.0"
10 | flutter: ">=2.5.0"
11 |
12 | dependencies:
13 | flutter:
14 | sdk: flutter
15 | plugin_platform_interface: ^2.1.7
16 | app_widget_platform_interface: ^0.4.0
17 | # app_widget_platform_interface: # local dev
18 | # path: ../app_widget_platform_interface
19 |
20 | dev_dependencies:
21 | flutter_test:
22 | sdk: flutter
23 | flutter_lints: ^2.0.0
24 |
25 | flutter:
26 | plugin:
27 | implements: app_widget
28 | platforms:
29 | android:
30 | package: tech.noxasch.app_widget
31 | pluginClass: AppWidgetPlugin
32 | dartPluginClass: AppWidgetAndroid
33 |
--------------------------------------------------------------------------------
/app_widget_android/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # The .vscode folder contains launch configuration and tasks you configure in
20 | # VS Code which you may wish to be included in version control, so this line
21 | # is commented out by default.
22 | #.vscode/
23 |
24 | # Flutter/Dart/Pub related
25 | **/doc/api/
26 | **/ios/Flutter/.last_build_id
27 | .dart_tool/
28 | .flutter-plugins
29 | .flutter-plugins-dependencies
30 | .packages
31 | .pub-cache/
32 | .pub/
33 | /build/
34 |
35 | # Web related
36 | lib/generated_plugin_registrant.dart
37 |
38 | # Symbolication related
39 | app.*.symbols
40 |
41 | # Obfuscation related
42 | app.*.map.json
43 |
44 | # Android Studio will place build artifacts here
45 | /android/app/debug
46 | /android/app/profile
47 | /android/app/release
48 |
--------------------------------------------------------------------------------
/app_widget/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: app_widget
2 | description: Flutter plugin to manage app widget / home screen widget from within flutter app.
3 | version: 0.4.0
4 | homepage: https://noxasch.tech/
5 | repository: https://github.com/noxasch/flutter_app_widget/tree/master/app_widget
6 | issue_tracker: https://github.com/noxasch/flutter_app_widget/issues
7 |
8 | environment:
9 | sdk: ">=2.17.6 <4.0.0"
10 | flutter: ">=2.5.0"
11 |
12 | dependencies:
13 | flutter:
14 | sdk: flutter
15 | plugin_platform_interface: ^2.1.7
16 | app_widget_platform_interface: ^0.4.0
17 | app_widget_android: ^0.4.0
18 | # app_widget_platform_interface: # local dev
19 | # path: ../app_widget_platform_interface
20 | # app_widget_android: # local dev
21 | # path: ../app_widget_android
22 |
23 | dev_dependencies:
24 | flutter_test:
25 | sdk: flutter
26 | flutter_lints: ^2.0.0
27 |
28 | flutter:
29 | plugin:
30 | platforms:
31 | android:
32 | default_package: app_widget_android
33 |
--------------------------------------------------------------------------------
/app_widget_platform_interface/README.md:
--------------------------------------------------------------------------------
1 | # app_widget_platform_interface
2 |
3 | Common platform interface for [app_widget](https://pub.dev/packages/app_widget) plugin.
4 |
5 | ## Usage
6 |
7 | To implement a new platform-specific, extend `AppWidgetPlatform` with a `registerWith` as static method that set default
8 | `AppWidgetPlatform.instance = AppWidgetMyPlatform();`. And then create another class `AppWidgetMyPlatformlugin` with an implementation that performs the platform-specific behavior. Finally add your platform initialization in AppWidgetPlugin private
9 | constructor to reinstantiate with the platform specific implementation `AppWidgetPlatform.instance = AppWidgetMyPlatformPlugin();` when the app run. This is because the plugin registrar will register the first intance before `FlutterWidgetBindings.ensureInitialized()` and will throw an error if we try to access any `methodChannel`.
10 |
11 | We try to avoid breaking changes at all. Try to reuse the existing interface to keep the api clean whenever possible.
12 |
--------------------------------------------------------------------------------
/app_widget/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled.
5 |
6 | version:
7 | revision: f1875d570e39de09040c8f79aa13cc56baab8db1
8 | channel: stable
9 |
10 | project_type: plugin
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
17 | base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
18 | - platform: android
19 | create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
20 | base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
21 |
22 | # User provided section
23 |
24 | # List of Local paths (relative to this file) that should be
25 | # ignored by the migrate tool.
26 | #
27 | # Files that are not part of the templates will be ignored by default.
28 | unmanaged_files:
29 | - 'lib/main.dart'
30 | - 'ios/Runner.xcodeproj/project.pbxproj'
31 |
--------------------------------------------------------------------------------
/app_widget_android/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled.
5 |
6 | version:
7 | revision: f1875d570e39de09040c8f79aa13cc56baab8db1
8 | channel: stable
9 |
10 | project_type: plugin
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
17 | base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
18 | - platform: android
19 | create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
20 | base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
21 |
22 | # User provided section
23 |
24 | # List of Local paths (relative to this file) that should be
25 | # ignored by the migrate tool.
26 | #
27 | # Files that are not part of the templates will be ignored by default.
28 | unmanaged_files:
29 | - 'lib/main.dart'
30 | - 'ios/Runner.xcodeproj/project.pbxproj'
31 |
--------------------------------------------------------------------------------
/app_widget_android/example/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility in the flutter_test package. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:app_widget_android_example/main.dart';
12 |
13 | void main() {
14 | testWidgets('Verify Platform version', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(const MyApp());
17 |
18 | // Verify that platform version is retrieved.
19 | expect(
20 | find.byWidgetPredicate(
21 | (Widget widget) => widget is Text &&
22 | widget.data!.startsWith('Running on:'),
23 | ),
24 | findsOneWidget,
25 | );
26 | });
27 | }
28 |
--------------------------------------------------------------------------------
/app_widget_platform_interface/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled.
5 |
6 | version:
7 | revision: f1875d570e39de09040c8f79aa13cc56baab8db1
8 | channel: stable
9 |
10 | project_type: plugin
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
17 | base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
18 | - platform: android
19 | create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
20 | base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
21 |
22 | # User provided section
23 |
24 | # List of Local paths (relative to this file) that should be
25 | # ignored by the migrate tool.
26 | #
27 | # Files that are not part of the templates will be ignored by default.
28 | unmanaged_files:
29 | - 'lib/main.dart'
30 | - 'ios/Runner.xcodeproj/project.pbxproj'
31 |
--------------------------------------------------------------------------------
/app_widget_platform_interface/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.4.0
2 | * breaking changes: `configureWidget` and `updateWidget` accept `layoutId` instead of `layoutName`
3 |
4 | ## 0.3.1
5 |
6 | * update platform interface version
7 |
8 | ## 0.3.0
9 |
10 | * support dart sdk 4.0
11 |
12 | ## 0.2.0
13 |
14 | * breaking changes: `androidAppName` renamed to `androidPackageName`
15 | * breaking changes: parameter `textViewsIdMap` renamed to `textViews` for `configureWidget` and `updateWidget`
16 | * feat: androidPackageName are now accepted for reloadWidget and getWidgetIds
17 |
18 |
19 | ## 0.1.0
20 |
21 | * finalize interface based on android usage
22 |
23 | ## 0.0.3
24 |
25 | * implement getWidgetIds api interface
26 |
27 | ## 0.0.2
28 |
29 | * implement updateWidget api interface
30 |
31 | ## 0.0.1
32 |
33 | * implement configureWidget api interface
34 | * implement cancelConfifureWidget api interface
35 | * implement reloadWidgets api interface
36 | * implement widgetExist api interface
37 | * implement onConfigureWidget callback api interface
38 | * implement onClickWidget callback api interface
39 |
--------------------------------------------------------------------------------
/app_widget/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/app_widget/example/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/app_widget_android/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/app_widget_android/example/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/.github/workflows/android.yaml:
--------------------------------------------------------------------------------
1 | name: android
2 | on:
3 | workflow_dispatch:
4 | branches:
5 | - master
6 | pull_request:
7 | branches: ['master']
8 | paths: ['app_widget_android/lib/**', 'app_widget_android/test/**']
9 | push:
10 | branches: ['master']
11 | paths: ['app_widget_android/lib/**', 'app_widget_android/test/**']
12 | jobs:
13 | analysis:
14 | runs-on: ubuntu-latest
15 | defaults:
16 | run:
17 | working-directory: ./app_widget_android
18 | strategy:
19 | matrix:
20 | os: [ubuntu-latest]
21 | steps:
22 | - uses: actions/checkout@v3 # checkout to current dir
23 | - uses: subosito/flutter-action@v2
24 | with:
25 | channel: 'stable'
26 | cache: true
27 | cache-key: ${{ runner.os }}-flutter-install-cache
28 | cache-path: ${{ runner.tool_cache }}/flutter
29 | - run: flutter pub get
30 | - name: Static Analysis
31 | run: flutter analyze
32 | - name: unit test
33 | run: flutter test
34 | - name: pub publishable
35 | run: flutter pub publish --dry-run
36 |
--------------------------------------------------------------------------------
/app_widget/example/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // // This is a basic Flutter widget test.
2 | // //
3 | // // To perform an interaction with a widget in your test, use the WidgetTester
4 | // // utility in the flutter_test package. For example, you can send tap and scroll
5 | // // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | // import 'package:flutter/material.dart';
9 | // import 'package:flutter_test/flutter_test.dart';
10 |
11 | // import 'package:app_widget_example/main.dart';
12 |
13 | // void main() {
14 | // testWidgets('Verify Platform version', (WidgetTester tester) async {
15 | // // Build our app and trigger a frame.
16 | // await tester.pumpWidget(const MyApp());
17 |
18 | // // Verify that platform version is retrieved.
19 | // expect(
20 | // find.byWidgetPredicate(
21 | // (Widget widget) => widget is Text &&
22 | // widget.data!.startsWith('Running on:'),
23 | // ),
24 | // findsOneWidget,
25 | // );
26 | // });
27 | // }
28 |
--------------------------------------------------------------------------------
/.github/workflows/interface.yaml:
--------------------------------------------------------------------------------
1 | name: platform-interface
2 | on:
3 | workflow_dispatch:
4 | branches:
5 | - master
6 | pull_request:
7 | branches: ['master']
8 | paths: ['app_widget_platform_interface/lib/**', 'app_widget_platform_interface/test/**']
9 | push:
10 | branches: ['master']
11 | paths: ['app_widget_platform_interface/lib/**', 'app_widget_platform_interface/test/**']
12 | jobs:
13 | analysis:
14 | runs-on: ubuntu-latest
15 | defaults:
16 | run:
17 | working-directory: ./app_widget_platform_interface
18 | strategy:
19 | matrix:
20 | os: [ubuntu-latest]
21 | steps:
22 | - uses: actions/checkout@v3 # checkout to current dir
23 | - uses: subosito/flutter-action@v2
24 | with:
25 | channel: 'stable'
26 | cache: true
27 | cache-key: ${{ runner.os }}-flutter-install-cache
28 | cache-path: ${{ runner.tool_cache }}/flutter
29 | - run: flutter pub get
30 | - name: Static Analysis
31 | run: flutter analyze
32 | - name: pub publishable
33 | run: flutter pub publish --dry-run
34 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Flutter App Widget
2 | App Widget / Home Screen widget plugin for flutter app
3 |
4 | ## Usage
5 |
6 | Please see [app_widget](./app_widget) subdirectory for the usage documentation.
7 |
8 | ## Plaform Support
9 |
10 | | Android | iOS |
11 | | :-----: | :-: |
12 | | ✔️ | |
13 |
14 | ## Project Structure
15 |
16 | This project follow the standard for flutter plugin development where each
17 | pieces are independent of each other. Other platform interface can be easily added
18 | by inherit the base platform interface and expose the require api to the main `app_wiget`
19 | plugin.
20 |
21 | ## Contributing
22 | 0. Fork
23 | 1. Checkout to feature branch
24 | 2. Add feature and unit test
25 | 3. Make sure all unit test pass
26 | 4. Add platform integration test and run integration test
27 | 5. Create a PR
28 |
29 | ## Android Integration Test
30 | - Make sure to run on the latest and minSdk supported
31 |
32 | in `app_widget/example/integration_test/app_widget_test.dart`
33 |
34 | ```sh
35 | cd app_widget/example
36 | # this will require a connected device for android
37 | flutter test integration_test/app_widget_test.dart
38 | ```
39 |
--------------------------------------------------------------------------------
/app_widget_android/android/build.gradle:
--------------------------------------------------------------------------------
1 | group 'tech.noxasch.app_widget'
2 | version '1.0-SNAPSHOT'
3 |
4 | buildscript {
5 | ext.kotlin_version = '1.6.10'
6 | repositories {
7 | google()
8 | mavenCentral()
9 | }
10 |
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:7.1.2'
13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14 | }
15 | }
16 |
17 | rootProject.allprojects {
18 | repositories {
19 | google()
20 | mavenCentral()
21 | }
22 | }
23 |
24 | apply plugin: 'com.android.library'
25 | apply plugin: 'kotlin-android'
26 |
27 | android {
28 | compileSdkVersion 33
29 |
30 | compileOptions {
31 | sourceCompatibility JavaVersion.VERSION_1_8
32 | targetCompatibility JavaVersion.VERSION_1_8
33 | }
34 |
35 | kotlinOptions {
36 | jvmTarget = '1.8'
37 | }
38 |
39 | sourceSets {
40 | main.java.srcDirs += 'src/main/kotlin'
41 | }
42 |
43 | defaultConfig {
44 | minSdkVersion 16
45 | }
46 | }
47 |
48 | dependencies {
49 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
50 | }
51 |
--------------------------------------------------------------------------------
/app_widget/example/android/app/src/main/res/layout/example_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
22 |
23 |
31 |
32 |
--------------------------------------------------------------------------------
/app_widget/example/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at
17 | # https://dart-lang.github.io/linter/lints/index.html.
18 | #
19 | # Instead of disabling a lint rule for the entire project in the
20 | # section below, it can also be suppressed for a single line of code
21 | # or a specific dart file by using the `// ignore: name_of_lint` and
22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
23 | # producing the lint.
24 | rules:
25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
27 |
28 | # Additional information about this file can be found at
29 | # https://dart.dev/guides/language/analysis-options
30 |
--------------------------------------------------------------------------------
/app_widget_android/example/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at
17 | # https://dart-lang.github.io/linter/lints/index.html.
18 | #
19 | # Instead of disabling a lint rule for the entire project in the
20 | # section below, it can also be suppressed for a single line of code
21 | # or a specific dart file by using the `// ignore: name_of_lint` and
22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
23 | # producing the lint.
24 | rules:
25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
27 |
28 | # Additional information about this file can be found at
29 | # https://dart.dev/guides/language/analysis-options
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2022, Alexander Dischberg
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | 3. Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/app_widget/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2022, Alexander Dischberg
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | 3. Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/app_widget_android/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2022, Alexander Dischberg
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | 3. Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/app_widget_platform_interface/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2022, Alexander Dischberg
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | 3. Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/app_widget/example/android/app/src/main/kotlin/tech/noxasch/app_widget_example/AppWidgetExampleDiffProvider.kt:
--------------------------------------------------------------------------------
1 | package tech.noxasch.diff_name
2 |
3 | import android.appwidget.AppWidgetManager
4 | import android.appwidget.AppWidgetProvider
5 | import android.content.Context
6 | import android.content.Intent
7 | import android.util.Log
8 | import android.widget.RemoteViews
9 | import androidx.core.content.ContextCompat.startActivity
10 | import androidx.core.view.accessibility.AccessibilityEventCompat.setAction
11 | import io.flutter.embedding.engine.FlutterEngine
12 | import io.flutter.plugin.common.MethodChannel
13 | import tech.noxasch.app_widget.AppWidgetPlugin
14 |
15 | class AppWidgetExampleDiffProvider : AppWidgetProvider() {
16 | override fun onUpdate(
17 | context: Context?,
18 | appWidgetManager: AppWidgetManager?,
19 | appWidgetIds: IntArray?
20 | ) {
21 | super.onUpdate(context, appWidgetManager, appWidgetIds)
22 | Log.d("APP_WIDGET_PLUGIN", "ON_UPDATE")
23 | if (appWidgetIds != null) {
24 | for (widgetId in appWidgetIds) {
25 | Log.d("APP_WIDGET_PLUGIN", "WIDGET_ID: $widgetId")
26 | }
27 | }
28 |
29 | // check if widgetId store sharedPreferences
30 | // fetch data from sharedPreferences
31 | // then update
32 | // for (widgetId in appWidgetIds!!) {
33 | // val remoteViews = RemoteViews(context!!.packageName, R.layout.example_layout).apply() {
34 | // setTextViewText(R.id.widget_title, "Widget Title")
35 | // setTextViewText(R.id.widget_message, "This is my message")
36 | // }
37 | //
38 | // appWidgetManager!!.partiallyUpdateAppWidget(widgetId, remoteViews)
39 | // }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app_widget/example/android/app/src/main/kotlin/tech/noxasch/app_widget_example/AppWidgetExampleProvider.kt:
--------------------------------------------------------------------------------
1 | package tech.noxasch.app_widget_example
2 |
3 | import android.appwidget.AppWidgetManager
4 | import android.appwidget.AppWidgetProvider
5 | import android.content.Context
6 | import android.content.Intent
7 | import android.util.Log
8 | import android.widget.RemoteViews
9 | import androidx.core.content.ContextCompat.startActivity
10 | import androidx.core.view.accessibility.AccessibilityEventCompat.setAction
11 | import io.flutter.embedding.engine.FlutterEngine
12 | import io.flutter.plugin.common.MethodChannel
13 | import tech.noxasch.app_widget.AppWidgetPlugin
14 |
15 | class AppWidgetExampleProvider : AppWidgetProvider() {
16 | override fun onUpdate(
17 | context: Context?,
18 | appWidgetManager: AppWidgetManager?,
19 | appWidgetIds: IntArray?
20 | ) {
21 | super.onUpdate(context, appWidgetManager, appWidgetIds)
22 | Log.d("APP_WIDGET_PLUGIN", "ON_UPDATE")
23 | if (appWidgetIds != null) {
24 | for (widgetId in appWidgetIds) {
25 | Log.d("APP_WIDGET_PLUGIN", "WIDGET_ID: $widgetId")
26 | }
27 | }
28 |
29 | // check if widgetId store sharedPreferences
30 | // fetch data from sharedPreferences
31 | // then update
32 | // for (widgetId in appWidgetIds!!) {
33 | // val remoteViews = RemoteViews(context!!.packageName, R.layout.example_layout).apply() {
34 | // setTextViewText(R.id.widget_title, "Widget Title")
35 | // setTextViewText(R.id.widget_message, "This is my message")
36 | // }
37 | //
38 | // appWidgetManager!!.partiallyUpdateAppWidget(widgetId, remoteViews)
39 | // }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app_widget_android/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
15 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
30 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app_widget_android/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.4.0
2 | * breaking changes: `configureWidget` and `updateWidget` accept `layoutId` instead of `layoutName`
3 | * breaking changes: `onConfigureWidget` now accept 3 params (`widgetId`, `layoutId`, `layoutName`)
4 |
5 | ## 0.3.3
6 | * fix: fix trigger update widget updating the widget multiple time
7 |
8 | ## 0.3.2
9 |
10 | * chore: update latest dependencies
11 |
12 | ## 0.3.1
13 |
14 | * chore: use latest platform interface
15 | ## 0.3.0
16 |
17 | *feat: support widget provider with different package name
18 | *fix: fix MainActivity not using application package name
19 |
20 | ## 0.2.1
21 |
22 | * fix(android): onClickWidget callback should be independent on each widget
23 |
24 | ## 0.2.0
25 |
26 | * breaking changes: `androidAppName` renamed to `androidPackageName`
27 | * breaking changes: parameter `textViewsIdMap` renamed to `textViews` for `configureWidget` and `updateWidget`
28 | * breaking changes: `onClickWidget` now accept String instead of Map
29 | * feat: androidPackageName are now accepted for reloadWidget and getWidgetIds
30 | * feat: accept uri parameters in `configureWidget` and `updatewidget`
31 | * fix: onClickWidget callback
32 |
33 | ## 0.1.1
34 |
35 | * perf: improve configure widget callback response
36 | * fix: fix reload wigdets
37 | * fix: fix getWidgetIds
38 |
39 | ## 0.1.0
40 |
41 | * feat: support flavored app
42 | * fix: update method
43 | * chore: finalized android params name
44 | ## 0.0.4
45 |
46 | * implement `handleConfigureAction` android method
47 |
48 | ## 0.0.3
49 |
50 | * implement getWidgetIds api
51 |
52 | ## 0.0.2
53 |
54 | * implement updateWidget api
55 | * remove debug log - Android and Dart
56 |
57 | ## 0.0.1
58 |
59 | * implement configureWidget api
60 | * implement cancelConfifureWidget api
61 | * implement reloadWidgets api
62 | * implement widgetExist api
63 | * implement onConfigureWidget callback api
64 | * implement onClickWidget callback api
65 |
--------------------------------------------------------------------------------
/app_widget/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.4.0
2 | * breaking changes: `configureWidget` and `updateWidget` accept `layoutId` instead of `layoutName`
3 | * breaking changes: `onConfigureWidget` now accept 3 params (`widgetId`, `layoutId`, `layoutName`)
4 |
5 | ## 0.3.1
6 | * fix(android): fix trigger update widget updating the widget multiple time
7 |
8 | ## 0.3.0
9 | * feat(android): support widget provider with diff androidPackageName
10 | * test: update widget test
11 | * test(android): update integration test
12 |
13 | ## 0.2.2
14 |
15 | * fix(android): `reloadWidgets` to use initialized `androidPackageName`
16 |
17 | ## 0.2.1
18 |
19 | * fix(android): onClickWidget callback should be independent on each widget
20 |
21 | ## 0.2.0
22 |
23 | * rename interface into more generic
24 | * feat(android): support uri payload for onClick intent
25 | * fix: onClickWidget callback are now called properly
26 |
27 | ### Breaking Changes
28 | - `androidPackageName` are no longer accepted in `configureWidget` and `updateWidget` method
29 | - `onClickWidget` callback now accept a string instead of Map
30 |
31 | ## 0.1.1
32 |
33 | * perf: improve configure widget callback response
34 | * fix: fix reload wigdets
35 | * fix: fix getWidgetIds
36 | * docs: update docs
37 | * docs: update example app
38 |
39 | ## 0.1.0
40 |
41 | * Support flavored app
42 | * Fix update method
43 | * finalized android params name
44 |
45 | ## 0.0.4
46 |
47 | * implement `handleConfigureAction` android method
48 |
49 | ## 0.0.3
50 |
51 | * implement getWidgetIds api - Android
52 |
53 | ## 0.0.2
54 |
55 | * implement updateWidget api - Android
56 | * remove debug log - Android and Dart
57 |
58 | ## 0.0.1
59 |
60 | * implement configureWidget api - Android
61 | * implement cancelConfifureWidget api - Android
62 | * implement reloadWidgets api - Android
63 | * implement widgetExist api - Android
64 | * implement onConfigureWidget callback api - Android
65 | * implement onClickWidget callback api - Android
66 |
--------------------------------------------------------------------------------
/app_widget/example/integration_test/android_test_2.dart:
--------------------------------------------------------------------------------
1 | import 'package:app_widget/app_widget.dart';
2 | import 'package:flutter_test/flutter_test.dart';
3 | import 'package:integration_test/integration_test.dart';
4 |
5 | // there is no way to test callback as it need to interact with actual widgets
6 | void main() {
7 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
8 |
9 | group('without androidPackageName', () {
10 | final AppWidgetPlugin appWidgetPlugin = AppWidgetPlugin();
11 |
12 | testWidgets('configureWidget', (tester) async {
13 | final res = await appWidgetPlugin.configureWidget(
14 | widgetId: 1,
15 | layoutId: 1,
16 | payload: '{"itemId": 1, "stringUid": "uid"}',
17 | url: 'https://google.come',
18 | );
19 | expect(res, isTrue);
20 | });
21 |
22 | testWidgets('cancelConfigureWidget', (tester) async {
23 | final res = await appWidgetPlugin.cancelConfigureWidget();
24 |
25 | expect(res, isTrue);
26 | });
27 |
28 | testWidgets('updateWidget', (tester) async {
29 | final res = await appWidgetPlugin.updateWidget(
30 | widgetId: 1,
31 | layoutId: 1,
32 | payload: '{"itemId": 1, "stringUid": "uid"}',
33 | url: 'https://google.come',
34 | textViews: {'widget_title': 'my title'},
35 | );
36 |
37 | expect(res, isTrue);
38 | });
39 |
40 | testWidgets('getWidgetIds', (tester) async {
41 | final res = await appWidgetPlugin.getWidgetIds(
42 | androidProviderName: 'AppWidgetExampleProvider',
43 | );
44 |
45 | expect(res, []);
46 | });
47 |
48 | testWidgets('reloadWidgets', (tester) async {
49 | final res = await appWidgetPlugin.reloadWidgets(
50 | androidProviderName: 'AppWidgetExampleProvider',
51 | );
52 |
53 | expect(res, isTrue);
54 | });
55 |
56 | testWidgets('widgetExist', (tester) async {
57 | final res = await appWidgetPlugin.widgetExist(12);
58 |
59 | expect(res, isFalse);
60 | });
61 | });
62 | }
63 |
--------------------------------------------------------------------------------
/app_widget/example/integration_test/android_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:app_widget/app_widget.dart';
2 | import 'package:flutter_test/flutter_test.dart';
3 | import 'package:integration_test/integration_test.dart';
4 |
5 | // there is no way to test callback as it need to interact with actual widgets
6 | void main() {
7 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
8 |
9 | group('with androidPackageName', () {
10 | final AppWidgetPlugin appWidgetPlugin = AppWidgetPlugin(
11 | androidPackageName: 'tech.noxasch.app_widget_example',
12 | );
13 |
14 | testWidgets('configureWidget', (tester) async {
15 | final res = await appWidgetPlugin.configureWidget(
16 | widgetId: 1,
17 | layoutId: 1,
18 | payload: '{"itemId": 1, "stringUid": "uid"}',
19 | url: 'https://google.come',
20 | );
21 | expect(res, isTrue);
22 | });
23 |
24 | testWidgets('cancelConfigureWidget', (tester) async {
25 | final res = await appWidgetPlugin.cancelConfigureWidget();
26 |
27 | expect(res, isTrue);
28 | });
29 |
30 | testWidgets('updateWidget', (tester) async {
31 | final res = await appWidgetPlugin.updateWidget(
32 | widgetId: 1,
33 | layoutId: 1,
34 | payload: '{"itemId": 1, "stringUid": "uid"}',
35 | url: 'https://google.come',
36 | textViews: {'widget_title': 'my title'},
37 | );
38 |
39 | expect(res, isTrue);
40 | });
41 |
42 | testWidgets('getWidgetIds', (tester) async {
43 | final res = await appWidgetPlugin.getWidgetIds(
44 | androidProviderName: 'AppWidgetExampleProvider',
45 | );
46 |
47 | expect(res, []);
48 | });
49 |
50 | testWidgets('reloadWidgets', (tester) async {
51 | final res = await appWidgetPlugin.reloadWidgets(
52 | androidProviderName: 'AppWidgetExampleProvider',
53 | );
54 |
55 | expect(res, isTrue);
56 | });
57 |
58 | testWidgets('widgetExist', (tester) async {
59 | final res = await appWidgetPlugin.widgetExist(12);
60 |
61 | expect(res, isFalse);
62 | });
63 | });
64 | }
65 |
--------------------------------------------------------------------------------
/app_widget/example/integration_test/android_diff_package_name_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:app_widget/app_widget.dart';
2 | import 'package:flutter_test/flutter_test.dart';
3 | import 'package:integration_test/integration_test.dart';
4 |
5 | // there is no way to test callback as it need to interact with actual widgets
6 | void main() {
7 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
8 |
9 | group('with androidPackageName', () {
10 | final AppWidgetPlugin appWidgetPlugin = AppWidgetPlugin(
11 | androidPackageName: 'tech.noxasch.app_widget_example',
12 | );
13 |
14 | testWidgets('configureWidget', (tester) async {
15 | final res = await appWidgetPlugin.configureWidget(
16 | androidPackageName: 'tech.noxasch.diff_name',
17 | widgetId: 1,
18 | layoutId: 1,
19 | payload: '{"itemId": 1, "stringUid": "uid"}',
20 | url: 'https://google.come',
21 | );
22 | expect(res, isTrue);
23 | });
24 |
25 | testWidgets('cancelConfigureWidget', (tester) async {
26 | final res = await appWidgetPlugin.cancelConfigureWidget();
27 |
28 | expect(res, isTrue);
29 | });
30 |
31 | testWidgets('updateWidget', (tester) async {
32 | final res = await appWidgetPlugin.updateWidget(
33 | androidPackageName: 'tech.noxasch.diff_name',
34 | widgetId: 1,
35 | layoutId: 1,
36 | payload: '{"itemId": 1, "stringUid": "uid"}',
37 | url: 'https://google.come',
38 | textViews: {'widget_title': 'my title'},
39 | );
40 |
41 | expect(res, isTrue);
42 | });
43 |
44 | testWidgets('getWidgetIds', (tester) async {
45 | final res = await appWidgetPlugin.getWidgetIds(
46 | androidPackageName: 'tech.noxasch.diff_name',
47 | androidProviderName: 'AppWidgetExampleDiffProvider',
48 | );
49 |
50 | expect(res, []);
51 | });
52 |
53 | testWidgets('reloadWidgets', (tester) async {
54 | final res = await appWidgetPlugin.reloadWidgets(
55 | androidPackageName: 'tech.noxasch.diff_name',
56 | androidProviderName: 'AppWidgetExampleDiffProvider',
57 | );
58 |
59 | expect(res, isTrue);
60 | });
61 |
62 | testWidgets('widgetExist', (tester) async {
63 | final res = await appWidgetPlugin.widgetExist(12);
64 |
65 | expect(res, isFalse);
66 | });
67 | });
68 | }
69 |
--------------------------------------------------------------------------------
/app_widget_android/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion flutter.compileSdkVersion
30 | ndkVersion flutter.ndkVersion
31 |
32 | compileOptions {
33 | sourceCompatibility JavaVersion.VERSION_1_8
34 | targetCompatibility JavaVersion.VERSION_1_8
35 | }
36 |
37 | kotlinOptions {
38 | jvmTarget = '1.8'
39 | }
40 |
41 | sourceSets {
42 | main.java.srcDirs += 'src/main/kotlin'
43 | }
44 |
45 | defaultConfig {
46 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
47 | applicationId "tech.noxasch.app_widget_example"
48 | // You can update the following values to match your application needs.
49 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
50 | minSdkVersion flutter.minSdkVersion
51 | targetSdkVersion flutter.targetSdkVersion
52 | versionCode flutterVersionCode.toInteger()
53 | versionName flutterVersionName
54 | }
55 |
56 | buildTypes {
57 | release {
58 | // TODO: Add your own signing config for the release build.
59 | // Signing with the debug keys for now, so `flutter run --release` works.
60 | signingConfig signingConfigs.debug
61 | }
62 | }
63 | }
64 |
65 | flutter {
66 | source '../..'
67 | }
68 |
69 | dependencies {
70 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
71 | }
72 |
--------------------------------------------------------------------------------
/app_widget_platform_interface/lib/src/app_widget_platform.dart:
--------------------------------------------------------------------------------
1 | import 'package:plugin_platform_interface/plugin_platform_interface.dart';
2 |
3 | abstract class AppWidgetPlatform extends PlatformInterface {
4 | /// Constructs a AppWidgetPlatform.
5 | AppWidgetPlatform() : super(token: _token);
6 |
7 | static final Object _token = Object();
8 |
9 | // static late AppWidgetPlatform _instance = MethodChannelAppWidget();
10 | // static AppWidgetPlatform _instance = MethodChannelAppWidget();
11 | static late AppWidgetPlatform _instance;
12 |
13 | /// The default instance of [AppWidgetPlatform] to use.
14 | ///
15 | /// Defaults to [MethodChannelAppWidget].
16 | static AppWidgetPlatform get instance => _instance;
17 |
18 | /// Platform-specific implementations should set this with their own
19 | /// platform-specific class that extends [AppWidgetPlatform] when
20 | /// they register themselves.
21 | static set instance(AppWidgetPlatform instance) {
22 | PlatformInterface.verifyToken(instance, _token);
23 | _instance = instance;
24 | }
25 |
26 | static const channel = 'tech.noxasch.flutter/app_widget_foreground';
27 |
28 | Future cancelConfigureWidget() {
29 | throw UnimplementedError();
30 | }
31 |
32 | // the params are not require in base interface as different platform will require differet parameters
33 | // we handle this in platform specific and plugin interface
34 | Future configureWidget({
35 | String? androidPackageName,
36 | int? widgetId,
37 | int? layoutId,
38 | Map? textViews,
39 | String? payload,
40 | String? url,
41 | }) async {
42 | throw UnimplementedError();
43 | }
44 |
45 | Future reloadWidgets({
46 | String? androidPackageName,
47 | String? androidProviderName,
48 | }) async {
49 | throw UnimplementedError();
50 | }
51 |
52 | Future updateWidget({
53 | String? androidPackageName,
54 | int? widgetId,
55 | int? layoutId,
56 | Map? textViews,
57 | String? payload,
58 | String? url,
59 | }) async {
60 | throw UnimplementedError();
61 | }
62 |
63 | /// get all widgets ids associated with the given provider
64 | /// will throw an error if the provider doesn't exist
65 | Future?> getWidgetIds({
66 | String? androidPackageName,
67 | String? androidProviderName,
68 | }) async {
69 | throw UnimplementedError();
70 | }
71 |
72 | Future widgetExist(int widgetId) async {
73 | throw UnimplementedError();
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/app_widget/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion flutter.compileSdkVersion
30 | ndkVersion flutter.ndkVersion
31 |
32 | compileOptions {
33 | sourceCompatibility JavaVersion.VERSION_1_8
34 | targetCompatibility JavaVersion.VERSION_1_8
35 | }
36 |
37 | kotlinOptions {
38 | jvmTarget = '1.8'
39 | }
40 |
41 | sourceSets {
42 | main.java.srcDirs += 'src/main/kotlin'
43 | }
44 |
45 | defaultConfig {
46 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
47 | applicationId "tech.noxasch.app_widget_example"
48 | // You can update the following values to match your application needs.
49 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
50 | minSdkVersion flutter.minSdkVersion
51 | targetSdkVersion flutter.targetSdkVersion
52 | versionCode flutterVersionCode.toInteger()
53 | versionName flutterVersionName
54 | }
55 |
56 | buildTypes {
57 | release {
58 | // TODO: Add your own signing config for the release build.
59 | // Signing with the debug keys for now, so `flutter run --release` works.
60 | signingConfig signingConfigs.debug
61 | }
62 | }
63 | }
64 |
65 | flutter {
66 | source '../..'
67 | }
68 |
69 | dependencies {
70 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
71 | implementation 'com.google.code.gson:gson:2.9.1'
72 | }
73 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | linter:
2 | # The lint rules applied to this project can be customized in the
3 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
4 | # included above or to enable additional rules. A list of all available lints
5 | # and their documentation is published at
6 | # https://dart-lang.github.io/linter/lints/index.html.
7 | #
8 | # Instead of disabling a lint rule for the entire project in the
9 | # section below, it can also be suppressed for a single line of code
10 | # or a specific dart file by using the `// ignore: name_of_lint` and
11 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
12 | # producing the lint.
13 | rules:
14 | prefer_single_quotes: true
15 | eol_at_end_of_file: true
16 | directives_ordering: true
17 | require_trailing_commas: true
18 | unnecessary_await_in_return: true
19 |
20 | # Additional information about this file can be found at
21 | # https://dart.dev/guides/language/analysis-options
22 | analyzer:
23 | exclude:
24 | - lib/**/*.g.dart # freezed and drift
25 | - lib/firebase_options_*.dart
26 | - lib/**/*.freezed.dart # freezed
27 | errors:
28 | invalid_annotation_target: ignore
29 | missing_required_param: warning
30 | missing_return: warning
31 | todo: ignore
32 | language:
33 | strict-casts: true
34 | strict-inference: true
35 | strict-raw-types: true
36 | # high CPU usage issue
37 | # https://github.com/dart-code-checker/dart-code-metrics/issues/568
38 | # plugins:
39 | # - dart_code_metrics
40 |
41 | dart_code_metrics:
42 | anti-patterns:
43 | - long-method:
44 | severity: warning
45 | - long-parameter-list:
46 | severity: warning
47 | metrics:
48 | cyclomatic-complexity: 20
49 | maximum-nesting-level: 5
50 | number-of-parameters: 4
51 | source-lines-of-code: 50
52 | # maintainability-index: 50
53 | metrics-exclude:
54 | - test/**
55 | - lib/**/*.g.dart
56 | - lib/firebase_options_*.dart
57 | rules:
58 | - newline-before-return:
59 | severity: style
60 | - no-boolean-literal-compare:
61 | severity: warning
62 | - prefer-async-await
63 | - no-empty-block
64 | - prefer-enums-by-name
65 | - prefer-trailing-comma:
66 | severity: style
67 | - prefer-conditional-expressions
68 | - no-equal-then-else
69 | - avoid-non-ascii-symbols
70 | - double-literal-format
71 | - no-magic-number
72 | - avoid-returning-widgets
73 | - avoid-unnecessary-setstate
74 | - avoid-use-expanded-as-spacer
75 | - avoid-wrapping-in-padding
76 | - prefer-correct-edge-insets-constructor
77 | - prefer-extracting-callbacks
78 | - avoid-nested-conditional-expressions:
79 | acceptable-level: 2
80 | - member-ordering:
81 | # alphabetize: true
82 | order:
83 | - constructors
84 | - public-fields
85 | - private-fields
86 |
--------------------------------------------------------------------------------
/app_widget/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
15 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
47 |
48 |
50 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/.github/workflows/main.yaml:
--------------------------------------------------------------------------------
1 | name: build
2 | on:
3 | workflow_dispatch:
4 | branches:
5 | - master
6 | pull_request:
7 | branches: ['master']
8 | paths: ['lib/**', 'android/**', 'app_widget/test/**', 'app_widget/example/**']
9 | push:
10 | branches: ['master']
11 | paths: ['lib/**', 'android/**', 'app_widget/test/**', 'app_widget/example/**']
12 | jobs:
13 | analysis:
14 | runs-on: ubuntu-latest
15 | defaults:
16 | run:
17 | working-directory: ./app_widget
18 | strategy:
19 | matrix:
20 | os: [ubuntu-latest]
21 | steps:
22 | - uses: actions/checkout@v3 # checkout to current dir
23 | - uses: subosito/flutter-action@v2
24 | with:
25 | channel: 'stable'
26 | cache: true
27 | cache-key: ${{ runner.os }}-flutter-install-cache
28 | cache-path: ${{ runner.tool_cache }}/flutter
29 | - run: flutter pub get
30 | - name: Static Analysis
31 | run: flutter analyze
32 | - name: pub publishable
33 | run: flutter pub publish --dry-run
34 | - name: unit test
35 | run: flutter test
36 | # never finish on CI, but run fine on local
37 | # build_android:
38 | # needs: analysis
39 | # strategy:
40 | # matrix:
41 | # api-level: [29, 30, 31, 32, 33]
42 | # runs-on: macos-latest
43 | # defaults:
44 | # run:
45 | # working-directory: ./app_widget/example
46 | # steps:
47 | # - uses: actions/checkout@v3
48 | # - uses: subosito/flutter-action@v2
49 | # with:
50 | # channel: 'stable'
51 | # cache: true
52 | # cache-key: ${{ runner.os }}-flutter-install-cache
53 | # cache-path: ${{ runner.tool_cache }}/flutter
54 | # - run: flutter pub get
55 | # - name: AVD cache
56 | # uses: actions/cache@v3
57 | # id: avd-cache
58 | # with:
59 | # path: |
60 | # ~/.android/avd/*
61 | # ~/.android/adb*
62 | # key: avd-${{ matrix.api-level }}
63 | # - name: create AVD and generate snapshot for caching
64 | # if: steps.avd-cache.outputs.cache-hit != 'true'
65 | # uses: reactivecircus/android-emulator-runner@v2
66 | # with:
67 | # api-level: ${{ matrix.api-level }}
68 | # arch: x86_64
69 | # profile: Nexus 6
70 | # target: playstore
71 | # force-avd-creation: false
72 | # emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
73 | # disable-animations: false
74 | # script: echo "Generated AVD snapshot for caching."
75 | # - uses: actions/setup-java@v2
76 | # with:
77 | # distribution: 'adopt'
78 | # java-version: '11'
79 | # cache: 'gradle'
80 | # - name: Integration Test
81 | # uses: reactivecircus/android-emulator-runner@v2
82 | # with:
83 | # target: playstore
84 | # api-level: ${{ matrix.api-level }}
85 | # arch: x86_64
86 | # profile: Nexus 6
87 | # force-avd-creation: false
88 | # emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
89 | # disable-animations: true
90 | # working-directory: ./app_widget/example
91 | # script: flutter test integration_test/android_test.dart && sleep 2 && flutter test integration_test/android_test_2.dart
92 |
--------------------------------------------------------------------------------
/app_widget_android/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: app_widget_android_example
2 | description: Demonstrates how to use the app_widget_android plugin.
3 |
4 | # The following line prevents the package from being accidentally published to
5 | # pub.dev using `flutter pub publish`. This is preferred for private packages.
6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
7 |
8 | environment:
9 | sdk: ">=2.17.6 <3.0.0"
10 |
11 | # Dependencies specify other packages that your package needs in order to work.
12 | # To automatically upgrade your package dependencies to the latest versions
13 | # consider running `flutter pub upgrade --major-versions`. Alternatively,
14 | # dependencies can be manually updated by changing the version numbers below to
15 | # the latest version available on pub.dev. To see which dependencies have newer
16 | # versions available, run `flutter pub outdated`.
17 | dependencies:
18 | flutter:
19 | sdk: flutter
20 |
21 | app_widget_android:
22 | # When depending on this package from a real application you should use:
23 | # app_widget_android: ^x.y.z
24 | # See https://dart.dev/tools/pub/dependencies#version-constraints
25 | # The example app is bundled with the plugin so we use a path dependency on
26 | # the parent directory to use the current plugin's version.
27 | path: ../
28 |
29 | # The following adds the Cupertino Icons font to your application.
30 | # Use with the CupertinoIcons class for iOS style icons.
31 | cupertino_icons: ^1.0.2
32 |
33 | dev_dependencies:
34 | flutter_test:
35 | sdk: flutter
36 |
37 | # The "flutter_lints" package below contains a set of recommended lints to
38 | # encourage good coding practices. The lint set provided by the package is
39 | # activated in the `analysis_options.yaml` file located at the root of your
40 | # package. See that file for information about deactivating specific lint
41 | # rules and activating additional ones.
42 | flutter_lints: ^2.0.0
43 |
44 | # For information on the generic Dart part of this file, see the
45 | # following page: https://dart.dev/tools/pub/pubspec
46 |
47 | # The following section is specific to Flutter packages.
48 | flutter:
49 |
50 | # The following line ensures that the Material Icons font is
51 | # included with your application, so that you can use the icons in
52 | # the material Icons class.
53 | uses-material-design: true
54 |
55 | # To add assets to your application, add an assets section, like this:
56 | # assets:
57 | # - images/a_dot_burr.jpeg
58 | # - images/a_dot_ham.jpeg
59 |
60 | # An image asset can refer to one or more resolution-specific "variants", see
61 | # https://flutter.dev/assets-and-images/#resolution-aware
62 |
63 | # For details regarding adding assets from package dependencies, see
64 | # https://flutter.dev/assets-and-images/#from-packages
65 |
66 | # To add custom fonts to your application, add a fonts section here,
67 | # in this "flutter" section. Each entry in this list should have a
68 | # "family" key with the font family name, and a "fonts" key with a
69 | # list giving the asset and other descriptors for the font. For
70 | # example:
71 | # fonts:
72 | # - family: Schyler
73 | # fonts:
74 | # - asset: fonts/Schyler-Regular.ttf
75 | # - asset: fonts/Schyler-Italic.ttf
76 | # style: italic
77 | # - family: Trajan Pro
78 | # fonts:
79 | # - asset: fonts/TrajanPro.ttf
80 | # - asset: fonts/TrajanPro_Bold.ttf
81 | # weight: 700
82 | #
83 | # For details regarding fonts from package dependencies,
84 | # see https://flutter.dev/custom-fonts/#from-packages
85 |
--------------------------------------------------------------------------------
/app_widget/test/app_widget_ios_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:app_widget/app_widget.dart';
2 | import 'package:app_widget_platform_interface/app_widget_platform_interface.dart';
3 | import 'package:flutter/foundation.dart';
4 | import 'package:flutter/services.dart';
5 | import 'package:flutter_test/flutter_test.dart';
6 |
7 | void main() {
8 | TestWidgetsFlutterBinding.ensureInitialized();
9 |
10 | const MethodChannel channel = MethodChannel(AppWidgetPlatform.channel);
11 | final List log = [];
12 |
13 | setUpAll(() {
14 | TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
15 | .setMockMethodCallHandler(channel, (methodCall) async {
16 | log.add(methodCall);
17 | switch (methodCall.method) {
18 | case 'getPlatformVersion':
19 | return '42';
20 | case 'configureWidget':
21 | return true;
22 | case 'cancelConfigureWidget':
23 | return true;
24 | case 'getWidgetIds':
25 | return [];
26 | case 'reloadWidgets':
27 | return true;
28 | case 'widgetExist':
29 | return true;
30 | default:
31 | return null;
32 | }
33 | });
34 | });
35 |
36 | group('iOS', () {
37 | setUp(() {
38 | debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
39 |
40 | // AppWidgetPlatform.instance
41 | });
42 |
43 | tearDownAll(() {
44 | log.clear();
45 | });
46 |
47 | test('cancelConfigureWidget', () async {
48 | final appWidgetPlugin = AppWidgetPlugin();
49 |
50 | expect(
51 | appWidgetPlugin.cancelConfigureWidget(),
52 | throwsA(
53 | isA().having(
54 | (e) => e.toString().contains('LateInitializationError'),
55 | 'LateInitializationError',
56 | isTrue,
57 | ),
58 | ),
59 | );
60 | });
61 |
62 | test('configureWidget', () async {
63 | final appWidgetPlugin = AppWidgetPlugin();
64 |
65 | expect(
66 | appWidgetPlugin.configureWidget(),
67 | throwsA(
68 | isA().having(
69 | (e) => e.toString().contains('LateInitializationError'),
70 | 'LateInitializationError',
71 | isTrue,
72 | ),
73 | ),
74 | );
75 | });
76 |
77 | test('getWidgetIds', () async {
78 | final appWidgetPlugin = AppWidgetPlugin();
79 |
80 | expect(
81 | () => appWidgetPlugin.getWidgetIds(
82 | androidProviderName: 'TestProvider',
83 | ),
84 | throwsA(
85 | isA().having(
86 | (e) => e.toString().contains('LateInitializationError'),
87 | 'LateInitializationError',
88 | isTrue,
89 | ),
90 | ),
91 | );
92 | });
93 |
94 | test('reloadWidgets', () async {
95 | final appWidgetPlugin = AppWidgetPlugin();
96 |
97 | expect(
98 | appWidgetPlugin.reloadWidgets(),
99 | throwsA(
100 | isA().having(
101 | (e) => e.toString().contains('LateInitializationError'),
102 | 'LateInitializationError',
103 | isTrue,
104 | ),
105 | ),
106 | );
107 | });
108 |
109 | test('updateWidget', () async {
110 | final appWidgetPlugin = AppWidgetPlugin();
111 |
112 | expect(
113 | appWidgetPlugin.updateWidget(),
114 | throwsA(
115 | isA().having(
116 | (e) => e.toString().contains('LateInitializationError'),
117 | 'LateInitializationError',
118 | isTrue,
119 | ),
120 | ),
121 | );
122 | });
123 |
124 | test('widgetExist', () async {
125 | final appWidgetPlugin = AppWidgetPlugin();
126 |
127 | expect(
128 | appWidgetPlugin.widgetExist(12),
129 | throwsA(
130 | isA().having(
131 | (e) => e.toString().contains('LateInitializationError'),
132 | 'LateInitializationError',
133 | isTrue,
134 | ),
135 | ),
136 | );
137 | });
138 | });
139 | }
140 |
--------------------------------------------------------------------------------
/app_widget_android/test/app_widget_android_test.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: inference_failure_on_collection_literal
2 |
3 | import 'package:app_widget_android/app_widget_android.dart';
4 | import 'package:app_widget_platform_interface/app_widget_platform_interface.dart';
5 | import 'package:flutter_test/flutter_test.dart';
6 | import 'package:plugin_platform_interface/plugin_platform_interface.dart';
7 |
8 | void main() {
9 | final AppWidgetPlatform initialPlatform = MockAppWidgetAndroidPlatform();
10 |
11 | test('$AppWidgetAndroid is the default instance', () {
12 | expect(initialPlatform, isInstanceOf());
13 | });
14 |
15 | test('cancelConfigureWidget', () async {
16 | AppWidgetPlatform appWidgetAndroidPlugin = MockAppWidgetAndroidPlatform();
17 | MockAppWidgetAndroidPlatform fakePlatform = MockAppWidgetAndroidPlatform();
18 | AppWidgetPlatform.instance = fakePlatform;
19 |
20 | expect(await appWidgetAndroidPlugin.cancelConfigureWidget(), isTrue);
21 | });
22 |
23 | test('configureWidget', () async {
24 | AppWidgetPlatform appWidgetAndroidPlugin = MockAppWidgetAndroidPlatform();
25 | MockAppWidgetAndroidPlatform fakePlatform = MockAppWidgetAndroidPlatform();
26 | AppWidgetPlatform.instance = fakePlatform;
27 |
28 | expect(await appWidgetAndroidPlugin.configureWidget(), isTrue);
29 | });
30 |
31 | test('getWidgetIds', () async {
32 | AppWidgetPlatform appWidgetAndroidPlugin = MockAppWidgetAndroidPlatform();
33 | MockAppWidgetAndroidPlatform fakePlatform = MockAppWidgetAndroidPlatform();
34 | AppWidgetPlatform.instance = fakePlatform;
35 |
36 | expect(
37 | await appWidgetAndroidPlugin.getWidgetIds(androidProviderName: 'name'),
38 | [42],
39 | );
40 | });
41 |
42 | test('reloadWidgets', () async {
43 | AppWidgetPlatform appWidgetAndroidPlugin = MockAppWidgetAndroidPlatform();
44 | MockAppWidgetAndroidPlatform fakePlatform = MockAppWidgetAndroidPlatform();
45 | AppWidgetPlatform.instance = fakePlatform;
46 |
47 | expect(await appWidgetAndroidPlugin.reloadWidgets(), isTrue);
48 | });
49 |
50 | test('updateWidget', () async {
51 | AppWidgetPlatform appWidgetAndroidPlugin = MockAppWidgetAndroidPlatform();
52 | MockAppWidgetAndroidPlatform fakePlatform = MockAppWidgetAndroidPlatform();
53 | AppWidgetPlatform.instance = fakePlatform;
54 |
55 | expect(await appWidgetAndroidPlugin.cancelConfigureWidget(), isTrue);
56 | });
57 |
58 | test('widgetExist', () async {
59 | AppWidgetPlatform appWidgetAndroidPlugin = MockAppWidgetAndroidPlatform();
60 | MockAppWidgetAndroidPlatform fakePlatform = MockAppWidgetAndroidPlatform();
61 | AppWidgetPlatform.instance = fakePlatform;
62 |
63 | expect(await appWidgetAndroidPlugin.widgetExist(1), isTrue);
64 | });
65 | }
66 |
67 | class MockAppWidgetAndroidPlatform
68 | with MockPlatformInterfaceMixin
69 | implements AppWidgetPlatform {
70 | @override
71 | Future cancelConfigureWidget() async {
72 | return true;
73 | }
74 |
75 | @override
76 | Future configureWidget({
77 | String? androidPackageName,
78 | int? widgetId,
79 | int? layoutId,
80 | String? widgetLayout,
81 | String? widgetContainerName,
82 | Map? textViews,
83 | String? payload,
84 | String? url,
85 | }) async {
86 | return true;
87 | }
88 |
89 | @override
90 | Future reloadWidgets({
91 | String? androidPackageName,
92 | String? androidProviderName,
93 | }) async {
94 | return true;
95 | }
96 |
97 | @override
98 | Future updateWidget({
99 | String? androidPackageName,
100 | int? widgetId,
101 | int? layoutId,
102 | String? widgetLayout,
103 | Map? textViews,
104 | String? payload,
105 | String? url,
106 | }) async {
107 | return true;
108 | }
109 |
110 | @override
111 | Future widgetExist(int widgetId) async {
112 | return true;
113 | }
114 |
115 | @override
116 | Future?> getWidgetIds({
117 | String? androidPackageName,
118 | String? androidProviderName,
119 | }) async {
120 | return [42];
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/app_widget_android/lib/src/app_widget_android_plugin.dart:
--------------------------------------------------------------------------------
1 | import 'package:app_widget_android/src/app_widget_android_platform.dart';
2 | import 'package:app_widget_platform_interface/app_widget_platform_interface.dart';
3 | import 'package:flutter/services.dart';
4 |
5 | const onConfigureWidgetCallback = 'onConfigureWidget';
6 | const onClickWidgetCallback = 'onClickWidget';
7 | const onUpdateWidgetsCallback = 'onUpdateWidgets';
8 | const onDeletedWidgetsCallback = 'onDeletedWidgets';
9 |
10 | const MethodChannel _methodChannel = MethodChannel(AppWidgetPlatform.channel);
11 |
12 | class AppWidgetAndroidPlugin extends AppWidgetAndroid {
13 | AppWidgetAndroidPlugin({
14 | void Function(int widgetId, int layoutId, String layoutName)?
15 | onConfigureWidget,
16 | void Function(String? payload)? onClickWidget,
17 | }) : _onConfigureWidget = onConfigureWidget,
18 | _onClickWidget = onClickWidget {
19 | _methodChannel.setMethodCallHandler(handleMethod);
20 | }
21 |
22 | final void Function(int widgetId, int layoutId, String layoutName)?
23 | _onConfigureWidget;
24 | final void Function(String? payload)? _onClickWidget;
25 |
26 | Future handleMethod(MethodCall call) async {
27 | switch (call.method) {
28 | case onConfigureWidgetCallback:
29 | final widgetId = call.arguments['widgetId'] as int;
30 | final layoutId = call.arguments['layoutId'] as int;
31 | final layoutName = call.arguments['layoutName'] as String;
32 | return _onConfigureWidget?.call(widgetId, layoutId, layoutName);
33 | case onClickWidgetCallback:
34 | return _onClickWidget?.call(call.arguments['payload'] as String);
35 | default:
36 | throw UnimplementedError('Method ${call.method} is not implemented!');
37 | }
38 | }
39 |
40 | @override
41 | Future cancelConfigureWidget() {
42 | return _methodChannel.invokeMethod('cancelConfigureWidget');
43 | }
44 |
45 | @override
46 | Future configureWidget({
47 | String? androidPackageName,
48 | int? widgetId,
49 | int? layoutId,
50 | Map? textViews = const {},
51 | String? payload,
52 | String? url,
53 | }) {
54 | assert(widgetId != null, 'widgetId is required for android!');
55 | assert(layoutId != null, 'layoutId is required for android!');
56 |
57 | return _methodChannel.invokeMethod('configureWidget', {
58 | 'androidPackageName': androidPackageName,
59 | 'widgetId': widgetId,
60 | 'layoutId': layoutId,
61 | 'textViews': textViews,
62 | 'payload': payload,
63 | 'url': url,
64 | });
65 | }
66 |
67 | @override
68 | Future?> getWidgetIds({
69 | String? androidPackageName,
70 | String? androidProviderName,
71 | }) async {
72 | assert(
73 | androidProviderName != null,
74 | 'androidProviderName is required for android!',
75 | );
76 |
77 | final widgetIds =
78 | await _methodChannel.invokeMethod?>('getWidgetIds', {
79 | if (androidPackageName != null) ...{
80 | 'androidPackageName': androidPackageName,
81 | },
82 | 'androidProviderName': androidProviderName,
83 | });
84 |
85 | return widgetIds?.map((id) => id as int).toList();
86 | }
87 |
88 | @override
89 | Future reloadWidgets({
90 | String? androidPackageName,
91 | String? androidProviderName,
92 | }) {
93 | assert(
94 | androidProviderName != null,
95 | 'androidProviderName is required for android!',
96 | );
97 |
98 | return _methodChannel.invokeMethod(
99 | 'reloadWidgets',
100 | {
101 | if (androidPackageName != null) ...{
102 | 'androidPackageName': androidPackageName,
103 | },
104 | 'androidProviderName': androidProviderName,
105 | },
106 | );
107 | }
108 |
109 | @override
110 | Future updateWidget({
111 | String? androidPackageName,
112 | int? widgetId,
113 | int? layoutId,
114 | String? widgetLayout,
115 | Map? textViews = const {},
116 | String? payload,
117 | String? url,
118 | }) {
119 | assert(widgetId != null, 'widgetId is required for android!');
120 | assert(layoutId != null, 'layoutId is required for android!');
121 |
122 | return _methodChannel.invokeMethod('updateWidget', {
123 | if (androidPackageName != null) ...{
124 | 'androidPackageName': androidPackageName,
125 | },
126 | 'widgetId': widgetId,
127 | 'layoutId': layoutId,
128 | 'textViews': textViews,
129 | 'payload': payload,
130 | 'url': url,
131 | });
132 | }
133 |
134 | @override
135 | Future widgetExist(int widgetId) {
136 | return _methodChannel
137 | .invokeMethod('widgetExist', {'widgetId': widgetId});
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/app_widget_android/android/src/main/kotlin/tech/noxasch/app_widget/AppWidgetPlugin.kt:
--------------------------------------------------------------------------------
1 | package tech.noxasch.app_widget
2 |
3 | import android.app.Activity
4 | import android.appwidget.AppWidgetManager
5 | import android.content.Context
6 | import android.content.Intent
7 | import androidx.annotation.Keep
8 | import androidx.annotation.NonNull
9 | import io.flutter.embedding.engine.plugins.FlutterPlugin
10 | import io.flutter.embedding.engine.plugins.activity.ActivityAware
11 | import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
12 | import io.flutter.plugin.common.PluginRegistry
13 |
14 |
15 | @Keep
16 | class AppWidgetPlugin: FlutterPlugin, ActivityAware,
17 | PluginRegistry.NewIntentListener {
18 |
19 | private var activity: Activity? = null
20 | private var methodCallHandler: AppWidgetMethodCallHandler? = null
21 |
22 | /// namespacing constant that need to be access from outside.
23 | /// similar to android string constant pattern
24 | companion object {
25 | @JvmStatic
26 | val TAG = "APP_WIDGET_PLUGIN"
27 | @JvmStatic
28 | val CHANNEL = "tech.noxasch.flutter/app_widget_foreground"
29 | @JvmStatic
30 | val CONFIGURE_WIDGET_ACTION_CALLBACK = "tech.noxasch.flutter.CONFIGURE_CALLBACK"
31 | @JvmStatic
32 | val CLICK_WIDGET_ACTION = "tech.noxasch.flutter.CLICK_WIDGET"
33 | @JvmStatic
34 | val CLICK_WIDGET_ACTION_CALLBACK = "tech.noxasch.flutter.CLICK_CALLBACK"
35 |
36 | @JvmStatic
37 | val EXTRA_PAYLOAD = "dataPayload"
38 | @JvmStatic
39 | val EXTRA_APP_ITEM_ID = "appItemId"
40 | @JvmStatic
41 | val EXTRA_APP_STRING_UID = "appStringUid"
42 |
43 | @JvmStatic
44 | val ON_CONFIGURE_WIDGET_CALLBACK = "onConfigureWidget"
45 | @JvmStatic
46 | val ON_ClICK_WIDGET_CALLBACK = "onClickWidget"
47 |
48 | // this method rethrow intent with a different name
49 | // to pass intent to onNewIntent
50 | // calling methodChannel in other method is too early
51 | // since they are called before flutter Ui is displayed
52 | @JvmStatic
53 | fun handleWidgetAction(context: Context, intent: Intent) {
54 | when(intent.action) {
55 | AppWidgetManager.ACTION_APPWIDGET_CONFIGURE -> handleConfigureAction(context, intent)
56 | CLICK_WIDGET_ACTION -> handleOnClickAction(context, intent)
57 | }
58 | }
59 |
60 | @JvmStatic
61 | private fun handleOnClickAction(context : Context, intent: Intent) {
62 | val clickIntent = Intent(context, context.javaClass)
63 | clickIntent.action = CLICK_WIDGET_ACTION_CALLBACK
64 |
65 | clickIntent.putExtras(intent)
66 | context.startActivity(clickIntent)
67 | }
68 |
69 | @JvmStatic
70 | private fun handleConfigureAction(context : Context, intent: Intent) {
71 | val extras = intent.extras
72 | val widgetId: Int = extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID) ?: return
73 | if (widgetId == 0) return
74 |
75 | val widgetManager = AppWidgetManager.getInstance(context)
76 | val appWidgetInfo = widgetManager.getAppWidgetInfo(widgetId)
77 | val layoutId = appWidgetInfo.initialLayout
78 | val layoutName = context.resources.getResourceName(layoutId)
79 |
80 | val configIntent = Intent(context, context.javaClass)
81 | configIntent.action = CONFIGURE_WIDGET_ACTION_CALLBACK
82 | configIntent.putExtra("widgetId", widgetId)
83 | configIntent.putExtra("layoutId", layoutId)
84 | configIntent.putExtra("layoutName", layoutName)
85 | context.startActivity(configIntent)
86 | }
87 | }
88 |
89 | override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
90 | methodCallHandler = AppWidgetMethodCallHandler(flutterPluginBinding.applicationContext)
91 | methodCallHandler!!.open(flutterPluginBinding.binaryMessenger)
92 | }
93 |
94 | override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
95 | methodCallHandler!!.close()
96 | }
97 |
98 | override fun onAttachedToActivity(binding: ActivityPluginBinding) {
99 | activity = binding.activity
100 | binding.addOnNewIntentListener(this)
101 | methodCallHandler!!.setActivity(activity)
102 | }
103 |
104 | override fun onDetachedFromActivityForConfigChanges() {
105 | activity = null
106 | }
107 |
108 | override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
109 | activity = binding.activity
110 | binding.addOnNewIntentListener(this)
111 | }
112 |
113 | override fun onDetachedFromActivity() {
114 | activity = null
115 | }
116 |
117 | // this only called when the activity already started
118 | // we need to rethrow the intent here since we don't have access to onUiDisplayed
119 | override fun onNewIntent(intent: Intent): Boolean {
120 | if (intent.action != null) {
121 | when (intent.action) {
122 | CONFIGURE_WIDGET_ACTION_CALLBACK -> methodCallHandler!!.handleConfigureIntent(intent)
123 | CLICK_WIDGET_ACTION_CALLBACK -> methodCallHandler!!.handleClickIntent(intent)
124 | }
125 | }
126 |
127 | return false
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/app_widget_android/example/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | app_widget_android:
5 | dependency: "direct main"
6 | description:
7 | path: ".."
8 | relative: true
9 | source: path
10 | version: "0.3.3"
11 | app_widget_platform_interface:
12 | dependency: transitive
13 | description:
14 | name: app_widget_platform_interface
15 | sha256: a288112ec826c25e7638ddc30c33a5e7279cfc2eadba89ccb33bc8d3ddbb589c
16 | url: "https://pub.dev"
17 | source: hosted
18 | version: "0.4.0"
19 | async:
20 | dependency: transitive
21 | description:
22 | name: async
23 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
24 | url: "https://pub.dev"
25 | source: hosted
26 | version: "2.11.0"
27 | boolean_selector:
28 | dependency: transitive
29 | description:
30 | name: boolean_selector
31 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
32 | url: "https://pub.dev"
33 | source: hosted
34 | version: "2.1.1"
35 | characters:
36 | dependency: transitive
37 | description:
38 | name: characters
39 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
40 | url: "https://pub.dev"
41 | source: hosted
42 | version: "1.3.0"
43 | clock:
44 | dependency: transitive
45 | description:
46 | name: clock
47 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
48 | url: "https://pub.dev"
49 | source: hosted
50 | version: "1.1.1"
51 | collection:
52 | dependency: transitive
53 | description:
54 | name: collection
55 | sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
56 | url: "https://pub.dev"
57 | source: hosted
58 | version: "1.17.2"
59 | cupertino_icons:
60 | dependency: "direct main"
61 | description:
62 | name: cupertino_icons
63 | sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
64 | url: "https://pub.dev"
65 | source: hosted
66 | version: "1.0.5"
67 | fake_async:
68 | dependency: transitive
69 | description:
70 | name: fake_async
71 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
72 | url: "https://pub.dev"
73 | source: hosted
74 | version: "1.3.1"
75 | flutter:
76 | dependency: "direct main"
77 | description: flutter
78 | source: sdk
79 | version: "0.0.0"
80 | flutter_lints:
81 | dependency: "direct dev"
82 | description:
83 | name: flutter_lints
84 | sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
85 | url: "https://pub.dev"
86 | source: hosted
87 | version: "2.0.1"
88 | flutter_test:
89 | dependency: "direct dev"
90 | description: flutter
91 | source: sdk
92 | version: "0.0.0"
93 | lints:
94 | dependency: transitive
95 | description:
96 | name: lints
97 | sha256: "5cfd6509652ff5e7fe149b6df4859e687fca9048437857cb2e65c8d780f396e3"
98 | url: "https://pub.dev"
99 | source: hosted
100 | version: "2.0.0"
101 | matcher:
102 | dependency: transitive
103 | description:
104 | name: matcher
105 | sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
106 | url: "https://pub.dev"
107 | source: hosted
108 | version: "0.12.16"
109 | material_color_utilities:
110 | dependency: transitive
111 | description:
112 | name: material_color_utilities
113 | sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
114 | url: "https://pub.dev"
115 | source: hosted
116 | version: "0.5.0"
117 | meta:
118 | dependency: transitive
119 | description:
120 | name: meta
121 | sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
122 | url: "https://pub.dev"
123 | source: hosted
124 | version: "1.9.1"
125 | path:
126 | dependency: transitive
127 | description:
128 | name: path
129 | sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
130 | url: "https://pub.dev"
131 | source: hosted
132 | version: "1.8.3"
133 | plugin_platform_interface:
134 | dependency: transitive
135 | description:
136 | name: plugin_platform_interface
137 | sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
138 | url: "https://pub.dev"
139 | source: hosted
140 | version: "2.1.7"
141 | sky_engine:
142 | dependency: transitive
143 | description: flutter
144 | source: sdk
145 | version: "0.0.99"
146 | source_span:
147 | dependency: transitive
148 | description:
149 | name: source_span
150 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
151 | url: "https://pub.dev"
152 | source: hosted
153 | version: "1.10.0"
154 | stack_trace:
155 | dependency: transitive
156 | description:
157 | name: stack_trace
158 | sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
159 | url: "https://pub.dev"
160 | source: hosted
161 | version: "1.11.0"
162 | stream_channel:
163 | dependency: transitive
164 | description:
165 | name: stream_channel
166 | sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
167 | url: "https://pub.dev"
168 | source: hosted
169 | version: "2.1.1"
170 | string_scanner:
171 | dependency: transitive
172 | description:
173 | name: string_scanner
174 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
175 | url: "https://pub.dev"
176 | source: hosted
177 | version: "1.2.0"
178 | term_glyph:
179 | dependency: transitive
180 | description:
181 | name: term_glyph
182 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
183 | url: "https://pub.dev"
184 | source: hosted
185 | version: "1.2.1"
186 | test_api:
187 | dependency: transitive
188 | description:
189 | name: test_api
190 | sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
191 | url: "https://pub.dev"
192 | source: hosted
193 | version: "0.6.0"
194 | vector_math:
195 | dependency: transitive
196 | description:
197 | name: vector_math
198 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
199 | url: "https://pub.dev"
200 | source: hosted
201 | version: "2.1.4"
202 | web:
203 | dependency: transitive
204 | description:
205 | name: web
206 | sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
207 | url: "https://pub.dev"
208 | source: hosted
209 | version: "0.1.4-beta"
210 | sdks:
211 | dart: ">=3.1.0-185.0.dev <4.0.0"
212 | flutter: ">=2.5.0"
213 |
--------------------------------------------------------------------------------
/app_widget/lib/src/app_widget_plugin.dart:
--------------------------------------------------------------------------------
1 | import 'package:app_widget_android/app_widget_android.dart';
2 | import 'package:app_widget_platform_interface/app_widget_platform_interface.dart';
3 | import 'package:flutter/foundation.dart';
4 |
5 | /// Instantiate plugin instance and register callback optional callback
6 | ///
7 | /// Accept an optional [androidPackageName] string which use to properly works for flavor that have different package name
8 | /// than the packageName for MainActivity
9 | /// Accept [onConfigureWidget] callback method - optional
10 | /// Accept [onClickWidget] callback method - optional
11 | ///
12 | /// onClickWidget payload:
13 | ///
14 | /// ```dart
15 | /// {
16 | /// "widgetId" : 23,
17 | /// "itemId": 232, // (if set during update/configure otherwise it is null)
18 | /// "stringUid": "dadas", // (if set during update/configure otherwise it is null)
19 | /// }
20 | /// ```
21 | ///
22 | class AppWidgetPlugin {
23 | factory AppWidgetPlugin({
24 | String? androidPackageName,
25 |
26 | /// callback function when the widget is first created
27 | void Function(int widgetId, int layoutId, String layoutName)?
28 | onConfigureWidget,
29 | void Function(String? payload)? onClickWidget,
30 | }) {
31 | if (instance != null) return instance!;
32 | instance = AppWidgetPlugin._(
33 | androidPackageName: androidPackageName,
34 | onConfigureWidget: onConfigureWidget,
35 | onClickWidget: onClickWidget,
36 | );
37 |
38 | return instance!;
39 | }
40 |
41 | AppWidgetPlugin._({
42 | required String? androidPackageName,
43 | required void Function(int widgetId, int layoutId, String layoutName)?
44 | onConfigureWidget,
45 | required void Function(String? payload)? onClickWidget,
46 | }) : _onConfigureWidget = onConfigureWidget,
47 | _onClickWidget = onClickWidget,
48 | _androidPackageName = androidPackageName {
49 | if (kIsWeb) {
50 | return;
51 | }
52 | if (defaultTargetPlatform == TargetPlatform.android) {
53 | AppWidgetPlatform.instance = AppWidgetAndroidPlugin(
54 | onConfigureWidget: _onConfigureWidget,
55 | onClickWidget: _onClickWidget,
56 | );
57 | } else if (defaultTargetPlatform == TargetPlatform.iOS) {
58 | return;
59 | }
60 | }
61 |
62 | static AppWidgetPlugin? instance;
63 |
64 | final String? _androidPackageName;
65 |
66 | /// callback function when the widget is first created
67 | final void Function(int widgetId, int layoutId, String layoutName)?
68 | _onConfigureWidget;
69 |
70 | /// payload keys:
71 | /// - itemId
72 | /// - stringUid
73 | /// - widgetId
74 | final void Function(String? payload)? _onClickWidget;
75 |
76 | /// Cancel widget configuration
77 | ///
78 | /// this will send Activity.RESULT_CANCELLED on android
79 | Future cancelConfigureWidget() async {
80 | return AppWidgetPlatform.instance.cancelConfigureWidget();
81 | }
82 |
83 | /// Configure Widget for the first time
84 | ///
85 | /// [androidPackageName] should be the app package name.
86 | /// eg: com.example.myapp
87 | ///
88 | /// [widgetLayout] is the layout filename without extension
89 | ///
90 | /// Get the [WidgetId] from [onConfigureWidget] callback.
91 | ///
92 | /// [textViews] is the id defined in layout ` configureWidget({
105 | int? widgetId,
106 | int? layoutId,
107 | Map? textViews = const {},
108 | String? payload,
109 | String? url,
110 | String? androidPackageName,
111 | }) async {
112 | return AppWidgetPlatform.instance.configureWidget(
113 | androidPackageName: androidPackageName ?? _androidPackageName,
114 | widgetId: widgetId,
115 | layoutId: layoutId,
116 | textViews: textViews,
117 | payload: payload,
118 | url: url,
119 | );
120 | }
121 |
122 | ///
123 | /// Get all widgetId associated with a AppWidgetProvider
124 | ///
125 | /// [androidProviderName] is the provider class name which also it's filename
126 | /// eg: `AppWidgetExampleProvider`
127 | ///
128 | Future?> getWidgetIds({
129 | required String androidProviderName,
130 | String? androidPackageName,
131 | }) {
132 | return AppWidgetPlatform.instance.getWidgetIds(
133 | androidPackageName: androidPackageName ?? _androidPackageName,
134 | androidProviderName: androidProviderName,
135 | );
136 | }
137 |
138 | /// Force reload all widgets
139 | ///
140 | /// This is a convenient method to force reload all widgets from dart side.
141 | ///
142 | /// This will trigger onUpdate method on android side.
143 | /// Use this if you handle widget update from `AppWidgetProvider` directly
144 | /// otherwise this method is useless.
145 | ///
146 | /// [androidProviderName] is the provider class name which also it's filename
147 | /// eg: `AppWidgetExampleProvider`
148 | ///
149 | Future reloadWidgets({
150 | String? androidProviderName,
151 | String? androidPackageName,
152 | }) async {
153 | return AppWidgetPlatform.instance.reloadWidgets(
154 | androidPackageName: androidPackageName ?? _androidPackageName,
155 | androidProviderName: androidProviderName,
156 | );
157 | }
158 |
159 | /// Update widget view manually
160 | ///
161 | /// [androidPackageName] should be the app package name.
162 | /// eg: com.example.myapp
163 | ///
164 | /// [widgetLayout] is the layout filename without extension
165 | ///
166 | /// Get the [WidgetId] from [onConfigureWidget] callback when the widget
167 | /// is created for the first time.
168 | ///
169 | /// [textViews] is the id defined in layout ` updateWidget({
182 | int? widgetId,
183 | int? layoutId,
184 | String? widgetLayout,
185 | Map? textViews = const {},
186 | String? payload,
187 | String? url,
188 | String? androidPackageName,
189 | }) async {
190 | return AppWidgetPlatform.instance.updateWidget(
191 | androidPackageName: androidPackageName ?? _androidPackageName,
192 | widgetId: widgetId,
193 | layoutId: layoutId,
194 | textViews: textViews,
195 | payload: payload,
196 | url: url,
197 | );
198 | }
199 |
200 | /// return [true] if a widget with given [widgetId] exist
201 | Future widgetExist(int widgetId) async {
202 | return AppWidgetPlatform.instance.widgetExist(widgetId);
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/app_widget/test/app_widget_test.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: inference_failure_on_collection_literal
2 |
3 | import 'package:app_widget/app_widget.dart';
4 | import 'package:app_widget_platform_interface/app_widget_platform_interface.dart';
5 | import 'package:flutter/foundation.dart';
6 | import 'package:flutter/services.dart';
7 | import 'package:flutter_test/flutter_test.dart';
8 |
9 | // NOTE: this merely test platform specific (android) interface
10 | void main() {
11 | TestWidgetsFlutterBinding.ensureInitialized();
12 |
13 | const MethodChannel channel = MethodChannel(AppWidgetPlatform.channel);
14 | final List log = [];
15 |
16 | setUpAll(() {
17 | TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
18 | .setMockMethodCallHandler(channel, (methodCall) async {
19 | log.add(methodCall);
20 | switch (methodCall.method) {
21 | case 'getPlatformVersion':
22 | return '42';
23 | case 'configureWidget':
24 | return true;
25 | case 'cancelConfigureWidget':
26 | return true;
27 | case 'getWidgetIds':
28 | return [42];
29 | case 'reloadWidgets':
30 | return true;
31 | case 'updateWidget':
32 | return true;
33 | case 'widgetExist':
34 | return true;
35 | default:
36 | return false;
37 | }
38 | });
39 | });
40 |
41 | group('Android', () {
42 | setUp(() {
43 | debugDefaultTargetPlatformOverride = TargetPlatform.android;
44 | });
45 |
46 | tearDown(() {
47 | log.clear();
48 | });
49 |
50 | test('configureWidget', () async {
51 | final appWidgetPlugin = AppWidgetPlugin(
52 | androidPackageName: 'appname',
53 | );
54 |
55 | expect(
56 | appWidgetPlugin.configureWidget(
57 | widgetId: 1,
58 | layoutId: 1,
59 | payload: '{"itemId": 1, "stringUid": "uid"}',
60 | url: 'https://google.come',
61 | ),
62 | completion(true),
63 | );
64 |
65 | expect(log, [
66 | isMethodCall(
67 | 'configureWidget',
68 | arguments: {
69 | 'androidPackageName': 'appname',
70 | 'widgetId': 1,
71 | 'layoutId': 1,
72 | 'textViews': {},
73 | 'payload': '{"itemId": 1, "stringUid": "uid"}',
74 | 'url': 'https://google.come',
75 | },
76 | ),
77 | ]);
78 | });
79 |
80 | test('configureWidget diff package name', () async {
81 | final appWidgetPlugin = AppWidgetPlugin(
82 | androidPackageName: 'appname',
83 | );
84 |
85 | expect(
86 | appWidgetPlugin.configureWidget(
87 | widgetId: 1,
88 | layoutId: 1,
89 | payload: '{"itemId": 1, "stringUid": "uid"}',
90 | url: 'https://google.come',
91 | androidPackageName: 'appname2',
92 | ),
93 | completion(true),
94 | );
95 |
96 | expect(log, [
97 | isMethodCall(
98 | 'configureWidget',
99 | arguments: {
100 | 'androidPackageName': 'appname2',
101 | 'widgetId': 1,
102 | 'layoutId': 1,
103 | 'textViews': {},
104 | 'payload': '{"itemId": 1, "stringUid": "uid"}',
105 | 'url': 'https://google.come',
106 | },
107 | ),
108 | ]);
109 | });
110 |
111 | test('updateWidget', () async {
112 | final appWidgetPlugin = AppWidgetPlugin(
113 | androidPackageName: 'appname',
114 | );
115 |
116 | expect(
117 | appWidgetPlugin.updateWidget(
118 | widgetId: 1,
119 | layoutId: 1,
120 | payload: '{"itemId": 1, "stringUid": "uid"}',
121 | url: 'https://google.come',
122 | ),
123 | completion(true),
124 | );
125 |
126 | expect(log, [
127 | isMethodCall(
128 | 'updateWidget',
129 | arguments: {
130 | 'androidPackageName': 'appname',
131 | 'widgetId': 1,
132 | 'layoutId': 1,
133 | 'textViews': {},
134 | 'payload': '{"itemId": 1, "stringUid": "uid"}',
135 | 'url': 'https://google.come',
136 | },
137 | ),
138 | ]);
139 | });
140 |
141 | test('updateWidget different widget package name', () async {
142 | final appWidgetPlugin = AppWidgetPlugin(
143 | androidPackageName: 'appname',
144 | );
145 |
146 | expect(
147 | appWidgetPlugin.updateWidget(
148 | androidPackageName: 'appname2',
149 | widgetId: 1,
150 | layoutId: 1,
151 | payload: '{"itemId": 1, "stringUid": "uid"}',
152 | url: 'https://google.come',
153 | ),
154 | completion(true),
155 | );
156 |
157 | expect(log, [
158 | isMethodCall(
159 | 'updateWidget',
160 | arguments: {
161 | 'androidPackageName': 'appname2',
162 | 'widgetId': 1,
163 | 'layoutId': 1,
164 | 'textViews': {},
165 | 'payload': '{"itemId": 1, "stringUid": "uid"}',
166 | 'url': 'https://google.come',
167 | },
168 | ),
169 | ]);
170 | });
171 |
172 | test('cancelConfigureWidget', () async {
173 | final appWidgetPlugin = AppWidgetPlugin();
174 |
175 | expect(
176 | appWidgetPlugin.cancelConfigureWidget(),
177 | completion(true),
178 | );
179 |
180 | expect(log, [
181 | isMethodCall(
182 | 'cancelConfigureWidget',
183 | arguments: null,
184 | ),
185 | ]);
186 | });
187 |
188 | test('getWidgetIds', () async {
189 | final appWidgetPlugin = AppWidgetPlugin();
190 |
191 | expect(
192 | appWidgetPlugin.getWidgetIds(
193 | androidProviderName: 'TestProvider',
194 | ),
195 | completion([42]),
196 | );
197 |
198 | expect(log, [
199 | isMethodCall(
200 | 'getWidgetIds',
201 | arguments: {
202 | 'androidProviderName': 'TestProvider',
203 | 'androidPackageName': 'appname',
204 | },
205 | ),
206 | ]);
207 | });
208 |
209 | test('getWidgetIds diff package name', () async {
210 | final appWidgetPlugin = AppWidgetPlugin();
211 |
212 | expect(
213 | appWidgetPlugin.getWidgetIds(
214 | androidProviderName: 'TestProvider',
215 | androidPackageName: 'appname2',
216 | ),
217 | completion([42]),
218 | );
219 |
220 | expect(log, [
221 | isMethodCall(
222 | 'getWidgetIds',
223 | arguments: {
224 | 'androidProviderName': 'TestProvider',
225 | 'androidPackageName': 'appname2',
226 | },
227 | ),
228 | ]);
229 | });
230 |
231 | test('reloadWidgets', () async {
232 | final appWidgetPlugin = AppWidgetPlugin();
233 |
234 | expect(
235 | appWidgetPlugin.reloadWidgets(
236 | androidProviderName: 'TestProvider',
237 | ),
238 | completion(true),
239 | );
240 |
241 | expect(log, [
242 | isMethodCall(
243 | 'reloadWidgets',
244 | arguments: {
245 | 'androidProviderName': 'TestProvider',
246 | 'androidPackageName': 'appname',
247 | },
248 | ),
249 | ]);
250 | });
251 |
252 | test('reloadWidgets diff package name', () async {
253 | final appWidgetPlugin = AppWidgetPlugin();
254 |
255 | expect(
256 | appWidgetPlugin.reloadWidgets(
257 | androidProviderName: 'TestProvider',
258 | androidPackageName: 'appname2',
259 | ),
260 | completion(true),
261 | );
262 |
263 | expect(log, [
264 | isMethodCall(
265 | 'reloadWidgets',
266 | arguments: {
267 | 'androidProviderName': 'TestProvider',
268 | 'androidPackageName': 'appname2',
269 | },
270 | ),
271 | ]);
272 | });
273 |
274 | test('widgetExist', () async {
275 | final appWidgetPlugin = AppWidgetPlugin();
276 |
277 | expect(
278 | appWidgetPlugin.widgetExist(12),
279 | completion(true),
280 | );
281 |
282 | expect(log, [
283 | isMethodCall(
284 | 'widgetExist',
285 | arguments: {
286 | 'widgetId': 12,
287 | },
288 | ),
289 | ]);
290 | });
291 | });
292 | }
293 |
--------------------------------------------------------------------------------
/app_widget/example/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | app_widget:
5 | dependency: "direct main"
6 | description:
7 | path: ".."
8 | relative: true
9 | source: path
10 | version: "0.3.1"
11 | app_widget_android:
12 | dependency: transitive
13 | description:
14 | name: app_widget_android
15 | sha256: "9a0aa193b373bf2e548d5af721e021b166f607ad932fc144334ce55b4ff40320"
16 | url: "https://pub.dev"
17 | source: hosted
18 | version: "0.4.0"
19 | app_widget_platform_interface:
20 | dependency: transitive
21 | description:
22 | name: app_widget_platform_interface
23 | sha256: a288112ec826c25e7638ddc30c33a5e7279cfc2eadba89ccb33bc8d3ddbb589c
24 | url: "https://pub.dev"
25 | source: hosted
26 | version: "0.4.0"
27 | async:
28 | dependency: transitive
29 | description:
30 | name: async
31 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
32 | url: "https://pub.dev"
33 | source: hosted
34 | version: "2.11.0"
35 | boolean_selector:
36 | dependency: transitive
37 | description:
38 | name: boolean_selector
39 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
40 | url: "https://pub.dev"
41 | source: hosted
42 | version: "2.1.1"
43 | characters:
44 | dependency: transitive
45 | description:
46 | name: characters
47 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
48 | url: "https://pub.dev"
49 | source: hosted
50 | version: "1.3.0"
51 | clock:
52 | dependency: transitive
53 | description:
54 | name: clock
55 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
56 | url: "https://pub.dev"
57 | source: hosted
58 | version: "1.1.1"
59 | collection:
60 | dependency: transitive
61 | description:
62 | name: collection
63 | sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
64 | url: "https://pub.dev"
65 | source: hosted
66 | version: "1.17.2"
67 | cupertino_icons:
68 | dependency: "direct main"
69 | description:
70 | name: cupertino_icons
71 | sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
72 | url: "https://pub.dev"
73 | source: hosted
74 | version: "1.0.5"
75 | fake_async:
76 | dependency: transitive
77 | description:
78 | name: fake_async
79 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
80 | url: "https://pub.dev"
81 | source: hosted
82 | version: "1.3.1"
83 | file:
84 | dependency: transitive
85 | description:
86 | name: file
87 | sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
88 | url: "https://pub.dev"
89 | source: hosted
90 | version: "6.1.4"
91 | flutter:
92 | dependency: "direct main"
93 | description: flutter
94 | source: sdk
95 | version: "0.0.0"
96 | flutter_driver:
97 | dependency: "direct dev"
98 | description: flutter
99 | source: sdk
100 | version: "0.0.0"
101 | flutter_lints:
102 | dependency: "direct dev"
103 | description:
104 | name: flutter_lints
105 | sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
106 | url: "https://pub.dev"
107 | source: hosted
108 | version: "2.0.1"
109 | flutter_test:
110 | dependency: "direct dev"
111 | description: flutter
112 | source: sdk
113 | version: "0.0.0"
114 | fuchsia_remote_debug_protocol:
115 | dependency: transitive
116 | description: flutter
117 | source: sdk
118 | version: "0.0.0"
119 | integration_test:
120 | dependency: "direct dev"
121 | description: flutter
122 | source: sdk
123 | version: "0.0.0"
124 | lints:
125 | dependency: transitive
126 | description:
127 | name: lints
128 | sha256: "5cfd6509652ff5e7fe149b6df4859e687fca9048437857cb2e65c8d780f396e3"
129 | url: "https://pub.dev"
130 | source: hosted
131 | version: "2.0.0"
132 | matcher:
133 | dependency: transitive
134 | description:
135 | name: matcher
136 | sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
137 | url: "https://pub.dev"
138 | source: hosted
139 | version: "0.12.16"
140 | material_color_utilities:
141 | dependency: transitive
142 | description:
143 | name: material_color_utilities
144 | sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
145 | url: "https://pub.dev"
146 | source: hosted
147 | version: "0.5.0"
148 | meta:
149 | dependency: transitive
150 | description:
151 | name: meta
152 | sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
153 | url: "https://pub.dev"
154 | source: hosted
155 | version: "1.9.1"
156 | path:
157 | dependency: transitive
158 | description:
159 | name: path
160 | sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
161 | url: "https://pub.dev"
162 | source: hosted
163 | version: "1.8.3"
164 | platform:
165 | dependency: transitive
166 | description:
167 | name: platform
168 | sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
169 | url: "https://pub.dev"
170 | source: hosted
171 | version: "3.1.0"
172 | plugin_platform_interface:
173 | dependency: transitive
174 | description:
175 | name: plugin_platform_interface
176 | sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
177 | url: "https://pub.dev"
178 | source: hosted
179 | version: "2.1.7"
180 | process:
181 | dependency: transitive
182 | description:
183 | name: process
184 | sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
185 | url: "https://pub.dev"
186 | source: hosted
187 | version: "4.2.4"
188 | sky_engine:
189 | dependency: transitive
190 | description: flutter
191 | source: sdk
192 | version: "0.0.99"
193 | source_span:
194 | dependency: transitive
195 | description:
196 | name: source_span
197 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
198 | url: "https://pub.dev"
199 | source: hosted
200 | version: "1.10.0"
201 | stack_trace:
202 | dependency: transitive
203 | description:
204 | name: stack_trace
205 | sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
206 | url: "https://pub.dev"
207 | source: hosted
208 | version: "1.11.0"
209 | stream_channel:
210 | dependency: transitive
211 | description:
212 | name: stream_channel
213 | sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
214 | url: "https://pub.dev"
215 | source: hosted
216 | version: "2.1.1"
217 | string_scanner:
218 | dependency: transitive
219 | description:
220 | name: string_scanner
221 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
222 | url: "https://pub.dev"
223 | source: hosted
224 | version: "1.2.0"
225 | sync_http:
226 | dependency: transitive
227 | description:
228 | name: sync_http
229 | sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961"
230 | url: "https://pub.dev"
231 | source: hosted
232 | version: "0.3.1"
233 | term_glyph:
234 | dependency: transitive
235 | description:
236 | name: term_glyph
237 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
238 | url: "https://pub.dev"
239 | source: hosted
240 | version: "1.2.1"
241 | test_api:
242 | dependency: transitive
243 | description:
244 | name: test_api
245 | sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
246 | url: "https://pub.dev"
247 | source: hosted
248 | version: "0.6.0"
249 | vector_math:
250 | dependency: transitive
251 | description:
252 | name: vector_math
253 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
254 | url: "https://pub.dev"
255 | source: hosted
256 | version: "2.1.4"
257 | vm_service:
258 | dependency: transitive
259 | description:
260 | name: vm_service
261 | sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f
262 | url: "https://pub.dev"
263 | source: hosted
264 | version: "11.7.1"
265 | web:
266 | dependency: transitive
267 | description:
268 | name: web
269 | sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
270 | url: "https://pub.dev"
271 | source: hosted
272 | version: "0.1.4-beta"
273 | webdriver:
274 | dependency: transitive
275 | description:
276 | name: webdriver
277 | sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49"
278 | url: "https://pub.dev"
279 | source: hosted
280 | version: "3.0.2"
281 | sdks:
282 | dart: ">=3.1.0-185.0.dev <4.0.0"
283 | flutter: ">=2.5.0"
284 |
--------------------------------------------------------------------------------
/app_widget/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 | import 'dart:math';
3 |
4 | import 'package:flutter/material.dart';
5 |
6 | import 'package:app_widget/app_widget.dart';
7 |
8 | void onClickWidget(String? payload) {
9 | // print('onClick Widget: $payload');
10 | }
11 |
12 | void main() {
13 | WidgetsFlutterBinding.ensureInitialized();
14 |
15 | runApp(const MyApp());
16 | }
17 |
18 | class MyApp extends StatefulWidget {
19 | const MyApp({Key? key}) : super(key: key);
20 |
21 | @override
22 | State createState() => _MyAppState();
23 | }
24 |
25 | class _MyAppState extends State {
26 | late final AppWidgetPlugin _appWidgetPlugin;
27 | late final TextEditingController _widgetIdcontroller;
28 | late final TextEditingController _layoutIdcontroller;
29 | late final TextEditingController _layoutNamecontroller;
30 | int? _widgetId;
31 | int? _layoutId;
32 | // ignore: unused_field
33 | String? _layoutName;
34 |
35 | @override
36 | void initState() {
37 | super.initState();
38 | _appWidgetPlugin = AppWidgetPlugin(
39 | // androidPackageName: 'tech.noxasch.app_widget_example',
40 | onConfigureWidget: onConfigureWidget,
41 | onClickWidget: onClickWidget,
42 | );
43 | _widgetIdcontroller = TextEditingController();
44 | _layoutIdcontroller = TextEditingController();
45 | _layoutNamecontroller = TextEditingController();
46 | }
47 |
48 | @override
49 | void dispose() {
50 | _widgetIdcontroller.dispose();
51 | _layoutIdcontroller.dispose();
52 | _layoutNamecontroller.dispose();
53 | super.dispose();
54 | }
55 |
56 | void onConfigureWidget(int widgetId, int layoutId, String layoutName) {
57 | setState(() {
58 | _widgetId = widgetId;
59 | _layoutId = layoutId;
60 | _layoutName = layoutName;
61 | });
62 | _widgetIdcontroller.text = widgetId.toString();
63 | _layoutIdcontroller.text = layoutId.toString();
64 | _layoutNamecontroller.text = layoutName.toString();
65 | // do something
66 | }
67 |
68 | @override
69 | Widget build(BuildContext context) {
70 | return MaterialApp(
71 | home: Scaffold(
72 | appBar: AppBar(
73 | title: const Text('AppWidgetPlugin example app'),
74 | ),
75 | body: Center(
76 | child: SingleChildScrollView(
77 | child: Column(
78 | mainAxisAlignment: MainAxisAlignment.center,
79 | children: [
80 | Padding(
81 | padding: const EdgeInsets.all(30.0),
82 | child: TextField(
83 | decoration: const InputDecoration(label: Text('Widget Id')),
84 | controller: _widgetIdcontroller,
85 | keyboardType: TextInputType.number,
86 | ),
87 | ),
88 | const SizedBox(
89 | height: 10,
90 | ),
91 | Padding(
92 | padding: const EdgeInsets.all(30.0),
93 | child: TextField(
94 | decoration: const InputDecoration(label: Text('Layout Id')),
95 | controller: _layoutIdcontroller,
96 | readOnly: true,
97 | ),
98 | ),
99 | const SizedBox(
100 | height: 10,
101 | ),
102 | Padding(
103 | padding: const EdgeInsets.all(30.0),
104 | child: TextField(
105 | decoration:
106 | const InputDecoration(label: Text('Layout Name')),
107 | controller: _layoutNamecontroller,
108 | readOnly: true,
109 | ),
110 | ),
111 | const SizedBox(
112 | height: 10,
113 | ),
114 | ConfigureButton(
115 | widgetId: _widgetId,
116 | layoutId: _layoutId,
117 | appWidgetPlugin: _appWidgetPlugin),
118 | const SizedBox(
119 | height: 10,
120 | ),
121 | WidgetExistButton(
122 | controller: _widgetIdcontroller,
123 | appWidgetPlugin: _appWidgetPlugin,
124 | ),
125 | const SizedBox(
126 | height: 10,
127 | ),
128 | ReloadWidgetButton(appWidgetPlugin: _appWidgetPlugin),
129 | const SizedBox(
130 | height: 10,
131 | ),
132 | UpdateWidgetButton(
133 | controller: _widgetIdcontroller,
134 | appWidgetPlugin: _appWidgetPlugin),
135 | const SizedBox(
136 | height: 10,
137 | ),
138 | GetWidgetIdsButton(appWidgetPlugin: _appWidgetPlugin),
139 | ],
140 | ),
141 | ),
142 | ),
143 | ),
144 | );
145 | }
146 | }
147 |
148 | class UpdateWidgetButton extends StatelessWidget {
149 | const UpdateWidgetButton({
150 | Key? key,
151 | required TextEditingController controller,
152 | required AppWidgetPlugin appWidgetPlugin,
153 | }) : _controller = controller,
154 | _appWidgetPlugin = appWidgetPlugin,
155 | super(key: key);
156 |
157 | final TextEditingController _controller;
158 | final AppWidgetPlugin _appWidgetPlugin;
159 |
160 | @override
161 | Widget build(BuildContext context) {
162 | return ElevatedButton(
163 | onPressed: () async {
164 | if (_controller.text.isNotEmpty) {
165 | // this means the app is started by the widget config event
166 | final widgetId = int.parse(_controller.text);
167 |
168 | // send configure
169 | await _appWidgetPlugin.updateWidget(
170 | widgetId: widgetId,
171 | widgetLayout: 'example_layout',
172 | textViews: {
173 | 'widget_title': 'App Widget',
174 | 'widget_message': 'Updated in flutter'
175 | },
176 | payload: jsonEncode({'number': Random.secure().nextInt(10)}),
177 | );
178 | }
179 | },
180 | child: const Text('Update Widget'),
181 | );
182 | }
183 | }
184 |
185 | class GetWidgetIdsButton extends StatelessWidget {
186 | const GetWidgetIdsButton({
187 | Key? key,
188 | required AppWidgetPlugin appWidgetPlugin,
189 | }) : _appWidgetPlugin = appWidgetPlugin,
190 | super(key: key);
191 |
192 | final AppWidgetPlugin _appWidgetPlugin;
193 |
194 | @override
195 | Widget build(BuildContext context) {
196 | return ElevatedButton(
197 | onPressed: () async {
198 | final messenger = ScaffoldMessenger.of(context);
199 | final widgetIds = await _appWidgetPlugin.getWidgetIds(
200 | androidProviderName: 'AppWidgetExampleProvider');
201 | messenger.showSnackBar(
202 | SnackBar(
203 | content: Text('widget ids: ${widgetIds ?? ""}'),
204 | ),
205 | );
206 | },
207 | child: const Text('Get WidgetIds'),
208 | );
209 | }
210 | }
211 |
212 | class ReloadWidgetButton extends StatelessWidget {
213 | const ReloadWidgetButton({
214 | Key? key,
215 | required AppWidgetPlugin appWidgetPlugin,
216 | }) : _appWidgetPlugin = appWidgetPlugin,
217 | super(key: key);
218 |
219 | final AppWidgetPlugin _appWidgetPlugin;
220 |
221 | @override
222 | Widget build(BuildContext context) {
223 | return ElevatedButton(
224 | onPressed: () async {
225 | final messenger = ScaffoldMessenger.of(context);
226 | await _appWidgetPlugin.reloadWidgets(
227 | androidProviderName: 'AppWidgetExampleProvider',
228 | );
229 | messenger.showSnackBar(
230 | const SnackBar(
231 | content:
232 | Text('Reload broadcast has been sent check Android debug log'),
233 | ),
234 | );
235 | },
236 | child: const Text('Reload Widgets'),
237 | );
238 | }
239 | }
240 |
241 | class WidgetExistButton extends StatelessWidget {
242 | const WidgetExistButton({
243 | Key? key,
244 | required TextEditingController controller,
245 | required AppWidgetPlugin appWidgetPlugin,
246 | }) : _controller = controller,
247 | _appWidgetPlugin = appWidgetPlugin,
248 | super(key: key);
249 |
250 | final TextEditingController _controller;
251 | final AppWidgetPlugin _appWidgetPlugin;
252 |
253 | @override
254 | Widget build(BuildContext context) {
255 | return ElevatedButton(
256 | onPressed: () async {
257 | if (_controller.text.isNotEmpty) {
258 | final messenger = ScaffoldMessenger.of(context);
259 |
260 | final widgetId = int.parse(_controller.text);
261 | final exist = (await _appWidgetPlugin.widgetExist(widgetId))!;
262 | if (exist) {
263 | messenger.showSnackBar(
264 | const SnackBar(
265 | content: Text('This widget exist.'),
266 | ),
267 | );
268 | } else {
269 | messenger.showSnackBar(
270 | SnackBar(
271 | content: Text('Widget with id $widgetId does not exist.'),
272 | ),
273 | );
274 | }
275 | }
276 | },
277 | child: const Text('check if Widget Exist'));
278 | }
279 | }
280 |
281 | class ConfigureButton extends StatelessWidget {
282 | const ConfigureButton({
283 | Key? key,
284 | required int? widgetId,
285 | required int? layoutId,
286 | required AppWidgetPlugin appWidgetPlugin,
287 | }) : _widgetId = widgetId,
288 | _layoutId = layoutId,
289 | _appWidgetPlugin = appWidgetPlugin,
290 | super(key: key);
291 |
292 | final int? _widgetId;
293 | final int? _layoutId;
294 | final AppWidgetPlugin _appWidgetPlugin;
295 |
296 | @override
297 | Widget build(BuildContext context) {
298 | return ElevatedButton(
299 | onPressed: () async {
300 | if (_widgetId != null) {
301 | // this means the app is started by the widget config event
302 | final messenger = ScaffoldMessenger.of(context);
303 |
304 | // send configure
305 | await _appWidgetPlugin.configureWidget(
306 | widgetId: _widgetId!,
307 | layoutId: _layoutId!,
308 | textViews: {
309 | 'widget_title': 'App Widget',
310 | 'widget_message': 'Configured in flutter'
311 | },
312 | payload: jsonEncode({'number': Random().nextInt(10)}),
313 | );
314 | messenger.showSnackBar(
315 | const SnackBar(content: Text('Widget has been configured!')));
316 | } else {
317 | ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
318 | content:
319 | Text('Opps, no widget id from WIDGET_CONFIGURE event')));
320 | }
321 | },
322 | child: const Text('Configure Widget'));
323 | }
324 | }
325 |
--------------------------------------------------------------------------------
/app_widget_android/android/src/main/kotlin/tech/noxasch/app_widget/AppWidgetMethodCallHandler.kt:
--------------------------------------------------------------------------------
1 | package tech.noxasch.app_widget
2 |
3 | import android.app.Activity
4 | import android.app.PendingIntent
5 | import android.appwidget.AppWidgetManager
6 | import android.content.ComponentName
7 | import android.content.Context
8 | import android.content.Intent
9 | import android.net.Uri
10 | import android.os.Build
11 | import android.widget.RemoteViews
12 | import androidx.annotation.NonNull
13 | import io.flutter.plugin.common.BinaryMessenger
14 | import io.flutter.plugin.common.MethodCall
15 | import io.flutter.plugin.common.MethodChannel
16 |
17 | class AppWidgetMethodCallHandler(private val context: Context, )
18 | : MethodChannel.MethodCallHandler {
19 |
20 | private var channel: MethodChannel? = null
21 | private var activity: Activity? = null
22 |
23 | fun open(binaryMessenger: BinaryMessenger) {
24 | channel = MethodChannel(binaryMessenger, AppWidgetPlugin.CHANNEL)
25 | channel!!.setMethodCallHandler(this)
26 | }
27 |
28 | fun setActivity(_activity: Activity?) {
29 | activity = _activity
30 | }
31 |
32 | fun close() {
33 | if (channel == null) return
34 |
35 | channel!!.setMethodCallHandler(null)
36 | channel = null
37 | activity = null
38 | }
39 |
40 | fun handleConfigureIntent(intent: Intent): Boolean {
41 | val widgetId = intent.extras!!.getInt("widgetId")
42 | val layoutId = intent.extras!!.getInt("layoutId")
43 | val layoutName = intent.extras!!.getString("layoutName")
44 | channel!!.invokeMethod(AppWidgetPlugin.ON_CONFIGURE_WIDGET_CALLBACK,
45 | mapOf(
46 | "widgetId" to widgetId,
47 | "layoutId" to layoutId,
48 | "layoutName" to layoutName
49 | )
50 | )
51 | return true
52 | }
53 |
54 |
55 | fun handleClickIntent(intent: Intent): Boolean {
56 | val payload = intent.extras?.getString(AppWidgetPlugin.EXTRA_PAYLOAD)
57 |
58 | channel!!.invokeMethod(AppWidgetPlugin.ON_ClICK_WIDGET_CALLBACK, mapOf(
59 | "payload" to payload
60 | ))
61 | return true
62 | }
63 |
64 | override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
65 | when (call.method) {
66 | "cancelConfigureWidget" -> cancelConfigureWidget(result)
67 | "configureWidget" -> configureWidget(call, result)
68 | "getWidgetIds" -> getWidgetIds(call, result)
69 | "reloadWidgets" -> reloadWidgets(call, result)
70 | "updateWidget" -> updateWidget(call, result)
71 | "widgetExist" -> widgetExist(call, result)
72 | else -> {
73 | result.notImplemented()
74 | }
75 | }
76 | }
77 |
78 | private fun getWidgetIds(call: MethodCall, result: MethodChannel.Result) {
79 | val androidPackageName = call.argument("androidPackageName")
80 | ?: context.packageName
81 | val widgetProviderName = call.argument("androidProviderName") ?: return result.error(
82 | "-1",
83 | "widgetProviderName is required!",
84 | null
85 | )
86 |
87 | return try {
88 | val widgetProviderClass = Class.forName("$androidPackageName.$widgetProviderName")
89 | val widgetProvider = ComponentName(context, widgetProviderClass)
90 | val widgetManager = AppWidgetManager.getInstance(context)
91 | val widgetIds = widgetManager.getAppWidgetIds(widgetProvider)
92 |
93 | result.success(widgetIds)
94 | } catch (exception: Exception) {
95 | result.error("-2", exception.message, exception)
96 | }
97 | }
98 |
99 | private fun cancelConfigureWidget(result: MethodChannel.Result) {
100 | return try {
101 | activity!!.setResult(Activity.RESULT_CANCELED)
102 | result.success(true)
103 | } catch (exception: Exception) {
104 | result.error("-2", exception.message, exception)
105 | }
106 | }
107 |
108 |
109 |
110 | /// This should be called when configuring individual widgets
111 | private fun configureWidget(call: MethodCall, result: MethodChannel.Result) {
112 | return try {
113 | if (activity == null) return result.error("-2", "Not attached to any activity!", null)
114 |
115 | val androidPackageName = call.argument("androidPackageName")
116 | ?: context.packageName
117 | val widgetId = call.argument("widgetId")
118 | ?: return result.error("-1", "widgetId is required!", null)
119 | val layoutId = call.argument("layoutId")
120 | ?: return result.error("-1", "layoutId is required!", null)
121 | val payload = call.argument("payload")
122 | val url = call.argument("url")
123 | val activityClass = Class.forName("${context.packageName}.MainActivity")
124 | val appWidgetManager = AppWidgetManager.getInstance(context)
125 | val pendingIntent = createPendingClickIntent(activityClass, widgetId, payload, url)
126 | val textViewsMap = call.argument