├── .gitignore ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── android ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── com │ └── mjohnsullivan │ └── flutterwear │ └── wear │ └── WearPlugin.kt ├── example ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── mjohnsullivan │ │ │ │ │ └── flutterwear │ │ │ │ │ └── wear_example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── 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 │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── lib │ └── main.dart └── pubspec.yaml ├── lib ├── src │ ├── ambient_widget.dart │ ├── shape_widget.dart │ └── wear.dart └── wear.dart └── pubspec.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | .idea/ 4 | .pub/ 5 | .vscode/ 6 | build/ 7 | .packages 8 | pubspec.lock 9 | *.iml 10 | 11 | # iOS not required for this plugin 12 | ios/** 13 | windows/** 14 | macos/** 15 | -------------------------------------------------------------------------------- /.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 and should not be manually edited. 5 | 6 | version: 7 | revision: c4ed4555f4ec04167b0ac024af6f895e9a3c9e76 8 | channel: dev_custom 9 | 10 | project_type: plugin 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 1.1.0 2 | 3 | - Fix issue with non-windows builds breaking. 4 | 5 | # 1.0.0 6 | 7 | - Null Safety Migration (Finally!) 8 | - Thanks to Rexios and Peter Ullrich. 9 | - Min Dart 2.12 / Flutter 2.5 10 | - Updated native component versions: 11 | - Gradle 6.5 12 | - Android Gradle Plugin 4.1.0 13 | - Android compileSdkVersion 31 14 | - AndroidX Wear 1.2.0 15 | - Kotlin 1.5.10 16 | - Removed `jcenter()` repo requirement. 17 | 18 | # 0.1.1 19 | 20 | - Fix Kotlin/Android compileOnly dep on com.google.android.wearable:wearable. 21 | 22 | # 0.1.0 23 | 24 | - Updated to AndroidX and Android embedding v2. 25 | - Renamed `Shape` is now `WearShape`. 26 | - Renamed `Mode` is now `WearMode`. 27 | - Deprecated `InheritedShape`. 28 | _Add `WatchShape` instead and use `WatchShape.of(context)` to get the shape value._ 29 | 30 | # 0.0.1 31 | 32 | - Initial release 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 The Chromium Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following 11 | disclaimer in the documentation and/or other materials provided 12 | with the distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived 15 | from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter Wear Plugin 2 | 3 | A plugin that offers Flutter support for Wear OS by Google (Android Wear). 4 | 5 | __To use this plugin you must set your `minSdkVersion` to `23`.__ 6 | 7 | 8 | # Tutorial 9 | 10 | https://medium.com/flutter-community/flutter-building-wearos-app-fedf0f06d1b4 11 | 12 | 13 | # Widgets 14 | 15 | There currently three widgets provided by the plugin: 16 | 17 | * WatchShape: determines whether the watch is square or round. 18 | * AmbientMode: builder that provides what mode the watch is in. The widget will rebuild whenever the watch changes mode. 19 | 20 | 21 | ## Example 22 | 23 | Typically, all three of these widgets would be used near the root of your app's widget tree: 24 | 25 | ```dart 26 | class WatchScreen extends StatelessWidget { 27 | @override 28 | Widget build(BuildContext context) { 29 | return WatchShape( 30 | builder: (BuildContext context, WearShape shape, Widget? child) { 31 | return AmbientMode( 32 | builder: (context, mode, child) { 33 | return mode == Mode.active ? ActiveWatchFace() : AmbientWatchFace(); 34 | }, 35 | ); 36 | }, 37 | ); 38 | } 39 | } 40 | ``` 41 | 42 | # Old Requirements 43 | 44 | **You DO NOT need to modify these files anymore:** 45 | 46 | You can remove all the old wearable references from the previous release. This plugin 47 | automatically adds all required references and settings. 48 | 49 | 1. `build.gradle`: _wearable dependencies_ 50 | 51 | 2. `AndroidManifest.xml`: _`WAKE_LOCK` and `android.hardware.type.watch` 52 | and `com.google.android.wearable.standalone`._ 53 | 54 | 3. `MainActivity.kt` or `MainActivity.java`: _all `AmbientMode` references._ 55 | 56 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | local.properties 4 | .DS_Store 5 | .idea/ 6 | build/ 7 | /captures 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group 'com.mjohnsullivan.flutterwear.wear' 2 | version '1.0-SNAPSHOT' 3 | 4 | buildscript { 5 | ext.kotlin_version = '1.5.10' 6 | repositories { 7 | google() 8 | mavenCentral() 9 | } 10 | 11 | dependencies { 12 | classpath 'com.android.tools.build:gradle:4.1.0' 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 | //apply plugin: 'kotlin-android-extensions' 27 | 28 | android { 29 | compileSdkVersion 31 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | defaultConfig { 35 | minSdkVersion 23 36 | } 37 | lintOptions { 38 | disable 'InvalidPackage' 39 | } 40 | } 41 | 42 | dependencies { 43 | //implementation "androidx.core:core-ktx:+" 44 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 45 | implementation 'androidx.wear:wear:1.2.0' 46 | implementation 'com.google.android.support:wearable:2.8.1' 47 | compileOnly 'com.google.android.wearable:wearable:2.8.1' 48 | 49 | // compileOnly files('C:\\Android\\flutter\\bin\\cache\\artifacts\\engine\\android-x64\\flutter.jar') 50 | } 51 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | android.useAndroidX=true 2 | android.enableJetifier=true 3 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'wear' 2 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /android/src/main/kotlin/com/mjohnsullivan/flutterwear/wear/WearPlugin.kt: -------------------------------------------------------------------------------- 1 | package com.mjohnsullivan.flutterwear.wear 2 | 3 | import android.os.Bundle 4 | import androidx.core.view.ViewCompat.getRootWindowInsets 5 | import androidx.lifecycle.Lifecycle 6 | import androidx.lifecycle.LifecycleObserver 7 | import androidx.lifecycle.OnLifecycleEvent 8 | import com.google.android.wearable.compat.WearableActivityController 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.embedding.engine.plugins.lifecycle.HiddenLifecycleReference 13 | import io.flutter.plugin.common.MethodCall 14 | import io.flutter.plugin.common.MethodChannel 15 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler 16 | import io.flutter.plugin.common.MethodChannel.Result 17 | 18 | 19 | class WearPlugin : FlutterPlugin, ActivityAware, MethodCallHandler, LifecycleObserver { 20 | private var mAmbientCallback = WearableAmbientCallback() 21 | private var mMethodChannel: MethodChannel? = null 22 | private var mActivityBinding: ActivityPluginBinding? = null 23 | private var mAmbientController: WearableActivityController? = null 24 | 25 | companion object { 26 | const val TAG = "WearPlugin" 27 | const val BURN_IN_PROTECTION = WearableActivityController.EXTRA_BURN_IN_PROTECTION 28 | const val LOW_BIT_AMBIENT = WearableActivityController.EXTRA_LOWBIT_AMBIENT 29 | } 30 | 31 | override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { 32 | mMethodChannel = MethodChannel(binding.binaryMessenger, "wear") 33 | mMethodChannel!!.setMethodCallHandler(this) 34 | } 35 | 36 | override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { 37 | mMethodChannel?.setMethodCallHandler(this) 38 | mMethodChannel = null 39 | } 40 | 41 | override fun onAttachedToActivity(binding: ActivityPluginBinding) { 42 | attachAmbientController(binding) 43 | } 44 | 45 | override fun onDetachedFromActivityForConfigChanges() { 46 | detachAmbientController() 47 | } 48 | 49 | override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { 50 | attachAmbientController(binding) 51 | } 52 | 53 | override fun onDetachedFromActivity() { 54 | detachAmbientController() 55 | } 56 | 57 | private fun attachAmbientController(binding: ActivityPluginBinding) { 58 | mActivityBinding = binding 59 | mAmbientController = WearableActivityController(TAG, binding.activity, mAmbientCallback) 60 | mAmbientController?.setAmbientEnabled() 61 | val reference = (binding.lifecycle as HiddenLifecycleReference) 62 | reference.lifecycle.addObserver(this) 63 | } 64 | 65 | private fun detachAmbientController() { 66 | mActivityBinding?.let { 67 | val reference = (it.lifecycle as HiddenLifecycleReference) 68 | reference.lifecycle.removeObserver(this) 69 | } 70 | mActivityBinding = null 71 | 72 | } 73 | 74 | override fun onMethodCall(call: MethodCall, result: Result) { 75 | when (call.method) { 76 | "getShape" -> { 77 | val activity = mActivityBinding?.activity 78 | when { 79 | activity == null -> { 80 | result.error("no-activity", "No android activity available.", null) 81 | } 82 | activity.resources.configuration.isScreenRound -> { 83 | result.success("round") 84 | } 85 | else -> { 86 | result.success("square") 87 | } 88 | } 89 | } 90 | "isAmbient" -> { 91 | result.success(mAmbientController?.isAmbient ?: false) 92 | } 93 | "setAutoResumeEnabled" -> { 94 | val enabled = call.argument("enabled") 95 | if (mAmbientController == null || enabled == null) { 96 | result.error("not-ready", "Ambient mode controller not ready", null) 97 | } else { 98 | mAmbientController!!.setAutoResumeEnabled(enabled) 99 | result.success(null) 100 | } 101 | } 102 | "setAmbientOffloadEnabled" -> { 103 | val enabled = call.argument("enabled") 104 | if (mAmbientController == null || enabled == null) { 105 | result.error("not-ready", "Ambient mode controller not ready", null) 106 | } else { 107 | mAmbientController!!.setAmbientOffloadEnabled(enabled) 108 | result.success(null) 109 | } 110 | } 111 | else -> result.notImplemented() 112 | } 113 | } 114 | 115 | @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) 116 | fun onCreate() { 117 | mAmbientController?.onCreate() 118 | } 119 | 120 | @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) 121 | fun onResume() { 122 | mAmbientController?.onResume() 123 | } 124 | 125 | @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) 126 | fun onPause() { 127 | mAmbientController?.onPause() 128 | } 129 | 130 | @OnLifecycleEvent(Lifecycle.Event.ON_STOP) 131 | fun onStop() { 132 | mAmbientController?.onStop() 133 | } 134 | 135 | @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) 136 | fun onDestroy() { 137 | mAmbientController?.onDestroy() 138 | } 139 | 140 | inner class WearableAmbientCallback : WearableActivityController.AmbientCallback() { 141 | override fun onEnterAmbient(ambientDetails: Bundle) { 142 | val burnInProtection = ambientDetails.getBoolean(BURN_IN_PROTECTION, false) 143 | val lowBitAmbient = ambientDetails.getBoolean(LOW_BIT_AMBIENT, false) 144 | mMethodChannel?.invokeMethod("onEnterAmbient", mapOf( 145 | "burnInProtection" to burnInProtection, 146 | "lowBitAmbient" to lowBitAmbient 147 | )) 148 | } 149 | 150 | override fun onExitAmbient() { 151 | mMethodChannel?.invokeMethod("onExitAmbient", null) 152 | } 153 | 154 | override fun onUpdateAmbient() { 155 | mMethodChannel?.invokeMethod("onUpdateAmbient", null) 156 | } 157 | 158 | override fun onInvalidateAmbientOffload() { 159 | mMethodChannel?.invokeMethod("onInvalidateAmbientOffload", null) 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # iOS not required for this plugin 44 | ios/** 45 | windows/** 46 | macos/** 47 | -------------------------------------------------------------------------------- /example/.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 and should not be manually edited. 5 | 6 | version: 7 | revision: c4ed4555f4ec04167b0ac024af6f895e9a3c9e76 8 | channel: dev_custom 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # wear_example 2 | 3 | Demonstrates how to use the wear plugin. 4 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /.idea 7 | local.properties 8 | GeneratedPluginRegistrant.java 9 | 10 | # Remember to never publicly share your keystore. 11 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 12 | key.properties 13 | -------------------------------------------------------------------------------- /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 FileNotFoundException("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 31 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 41 | applicationId "com.mjohnsullivan.flutterwear.wear_example" 42 | minSdkVersion 23 43 | targetSdkVersion 25 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | } 47 | 48 | buildTypes { 49 | release { 50 | // TODO: Add your own signing config for the release build. 51 | // Signing with the debug keys for now, so `flutter run --release` works. 52 | signingConfig signingConfigs.debug 53 | } 54 | } 55 | } 56 | 57 | flutter { 58 | source '../..' 59 | } 60 | 61 | dependencies { 62 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 63 | } 64 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 9 | 13 | 20 | 24 | 28 | 33 | 37 | 38 | 39 | 40 | 41 | 42 | 44 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/mjohnsullivan/flutterwear/wear_example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.mjohnsullivan.flutterwear.wear_example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_wear_plugin/2d087627a6f5c32245a7f19eed999dee6b3c5085/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_wear_plugin/2d087627a6f5c32245a7f19eed999dee6b3c5085/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_wear_plugin/2d087627a6f5c32245a7f19eed999dee6b3c5085/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_wear_plugin/2d087627a6f5c32245a7f19eed999dee6b3c5085/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_wear_plugin/2d087627a6f5c32245a7f19eed999dee6b3c5085/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.5.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.0' 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 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | android.useAndroidX=true 2 | android.enableJetifier=true 3 | -------------------------------------------------------------------------------- /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-6.5-all.zip 7 | -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:wear/wear.dart'; 3 | 4 | void main() => runApp(MyApp()); 5 | 6 | class MyApp extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return MaterialApp( 10 | home: Scaffold( 11 | body: Center( 12 | child: WatchShape( 13 | builder: (BuildContext context, WearShape shape, Widget? child) { 14 | return Column( 15 | mainAxisAlignment: MainAxisAlignment.center, 16 | children: [ 17 | Text( 18 | 'Shape: ${shape == WearShape.round ? 'round' : 'square'}', 19 | ), 20 | child!, 21 | ], 22 | ); 23 | }, 24 | child: AmbientMode( 25 | builder: (BuildContext context, WearMode mode, Widget? child) { 26 | return Text( 27 | 'Mode: ${mode == WearMode.active ? 'Active' : 'Ambient'}', 28 | ); 29 | }, 30 | ), 31 | ), 32 | ), 33 | ), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: wear_example 2 | description: Demonstrates how to use the wear plugin. 3 | publish_to: 'none' 4 | 5 | environment: 6 | sdk: ">=2.12.0 <3.0.0" 7 | flutter: ">=2.5.0" 8 | 9 | dependencies: 10 | wear: 11 | path: ../ 12 | flutter: 13 | sdk: flutter 14 | flutter_lints: ^1.0.4 15 | 16 | dev_dependencies: 17 | flutter_test: 18 | sdk: flutter 19 | 20 | flutter: 21 | uses-material-design: true 22 | -------------------------------------------------------------------------------- /lib/src/ambient_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:wear/src/wear.dart'; 3 | 4 | /// Ambient modes for a Wear device 5 | enum WearMode { active, ambient } 6 | 7 | /// Builds a child for [AmbientMode] 8 | typedef Widget AmbientModeWidgetBuilder(BuildContext context, WearMode mode, Widget? child); 9 | 10 | /// Widget that listens for when a Wear device enters full power or ambient mode, 11 | /// and provides this in a builder. It optionally takes an [onUpdate] function that's 12 | /// called every time the wear device triggers an ambient update request. 13 | @immutable 14 | class AmbientMode extends StatefulWidget { 15 | const AmbientMode({ 16 | Key? key, 17 | required this.builder, 18 | this.child, 19 | this.onUpdate, 20 | }) : super(key: key); 21 | 22 | final AmbientModeWidgetBuilder builder; 23 | final Widget? child; 24 | 25 | /// Called each time the the wear device triggers an ambient update request. 26 | final VoidCallback? onUpdate; 27 | 28 | /// Get current [WearMode]. 29 | static WearMode wearModeOf(BuildContext context) { 30 | return context.dependOnInheritedWidgetOfExactType<_InheritedAmbientMode>()!.mode; 31 | } 32 | 33 | /// Get current [AmbientDetails]. 34 | static AmbientDetails ambientDetailsOf(BuildContext context) { 35 | return context.dependOnInheritedWidgetOfExactType<_InheritedAmbientMode>()!.details; 36 | } 37 | 38 | @override 39 | _AmbientModeState createState() => _AmbientModeState(); 40 | } 41 | 42 | class _AmbientModeState extends State with AmbientCallback { 43 | var _ambientMode = WearMode.active; 44 | var _ambientDetails = AmbientDetails(false, false); 45 | 46 | @override 47 | void initState() { 48 | super.initState(); 49 | Wear.instance.registerAmbientCallback(this); 50 | Wear.instance.isAmbient().then(_updateMode); 51 | } 52 | 53 | @override 54 | void dispose() { 55 | Wear.instance.unregisterAmbientCallback(this); 56 | super.dispose(); 57 | } 58 | 59 | @override 60 | Widget build(BuildContext context) { 61 | return _InheritedAmbientMode( 62 | mode: _ambientMode, 63 | details: _ambientDetails, 64 | child: Builder( 65 | builder: (BuildContext context) { 66 | return widget.builder(context, _ambientMode, widget.child); 67 | }, 68 | ), 69 | ); 70 | } 71 | 72 | void _updateMode(bool isAmbient) { 73 | if (mounted) { 74 | setState(() => _ambientMode = isAmbient ? WearMode.ambient : WearMode.active); 75 | } 76 | } 77 | 78 | @override 79 | void onEnterAmbient(AmbientDetails ambientDetails) => _updateMode(true); 80 | 81 | @override 82 | void onExitAmbient() => _updateMode(false); 83 | 84 | @override 85 | void onUpdateAmbient() { 86 | _updateMode(true); 87 | widget.onUpdate?.call(); 88 | } 89 | } 90 | 91 | class _InheritedAmbientMode extends InheritedWidget { 92 | const _InheritedAmbientMode({ 93 | Key? key, 94 | required this.mode, 95 | required this.details, 96 | required Widget child, 97 | }) : super(key: key, child: child); 98 | 99 | final WearMode mode; 100 | final AmbientDetails details; 101 | 102 | @override 103 | bool updateShouldNotify(_InheritedAmbientMode old) { 104 | return mode != old.mode || details != old.details; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /lib/src/shape_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:wear/src/wear.dart'; 3 | 4 | /// Shape of a Wear device 5 | enum WearShape { square, round } 6 | 7 | /// Builds a child for a [WatchShape] 8 | typedef Widget WatchShapeBuilder(BuildContext context, WearShape shape, Widget? child); 9 | 10 | /// Builder widget for watch shapes 11 | @immutable 12 | class WatchShape extends StatefulWidget { 13 | const WatchShape({ 14 | Key? key, 15 | required this.builder, 16 | this.child, 17 | }) : super(key: key); 18 | 19 | final WatchShapeBuilder builder; 20 | final Widget? child; 21 | 22 | /// Call [WatchShape.of(context)] to retrieve the shape further down 23 | /// in the widget hierarchy. 24 | static WearShape of(BuildContext context) { 25 | // ignore: deprecated_member_use_from_same_package 26 | return InheritedShape.of(context).shape; 27 | } 28 | 29 | @override 30 | _WatchShapeState createState() => _WatchShapeState(); 31 | } 32 | 33 | class _WatchShapeState extends State { 34 | late WearShape _shape; 35 | 36 | @override 37 | void initState() { 38 | super.initState(); 39 | // Default to round until the platform returns the shape 40 | // round being the most common form factor for WearOS 41 | _shape = WearShape.round; 42 | Wear.instance.getShape().then((String shape) { 43 | if (mounted) { 44 | setState(() => _shape = (shape == 'round' ? WearShape.round : WearShape.square)); 45 | } 46 | }); 47 | } 48 | 49 | @override 50 | Widget build(BuildContext context) { 51 | // ignore: deprecated_member_use_from_same_package 52 | return InheritedShape( 53 | shape: _shape, 54 | child: Builder( 55 | builder: (BuildContext context) { 56 | return widget.builder(context, _shape, widget.child); 57 | }, 58 | ), 59 | ); 60 | } 61 | } 62 | 63 | /// An inherited widget that holds the shape of the Watch 64 | @Deprecated("Add WatchShape instead and use WatchShape.of(context) to get the shape value.") 65 | class InheritedShape extends InheritedWidget { 66 | const InheritedShape({ 67 | Key? key, 68 | required this.shape, 69 | required Widget child, 70 | }) : super(key: key, child: child); 71 | 72 | final WearShape shape; 73 | 74 | static InheritedShape of(BuildContext context) { 75 | return context.dependOnInheritedWidgetOfExactType()!; 76 | } 77 | 78 | @override 79 | bool updateShouldNotify(InheritedShape old) => shape != old.shape; 80 | } 81 | -------------------------------------------------------------------------------- /lib/src/wear.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart' show debugPrint; 2 | import 'package:flutter/services.dart' show MethodChannel, MethodCall, PlatformException; 3 | 4 | class Wear { 5 | static const MethodChannel _channel = MethodChannel('wear'); 6 | 7 | factory Wear() => instance; 8 | 9 | static final instance = Wear._(); 10 | 11 | Wear._() { 12 | _channel.setMethodCallHandler(_onMethodCallHandler); 13 | } 14 | 15 | final _ambientCallbacks = []; 16 | 17 | /// Register callback for ambient notifications 18 | void registerAmbientCallback(AmbientCallback callback) { 19 | _ambientCallbacks.add(callback); 20 | } 21 | 22 | /// Unregister callback for ambient notifications 23 | void unregisterAmbientCallback(AmbientCallback callback) { 24 | _ambientCallbacks.remove(callback); 25 | } 26 | 27 | Future _onMethodCallHandler(MethodCall call) async { 28 | switch (call.method) { 29 | case 'onEnterAmbient': 30 | final args = (call.arguments as Map).cast(); 31 | final details = AmbientDetails(args['burnInProtection']!, args['lowBitAmbient']!); 32 | _notifyAmbientCallbacks((callback) => callback.onEnterAmbient(details)); 33 | break; 34 | case 'onExitAmbient': 35 | _notifyAmbientCallbacks((callback) => callback.onExitAmbient()); 36 | break; 37 | case 'onUpdateAmbient': 38 | _notifyAmbientCallbacks((callback) => callback.onUpdateAmbient()); 39 | break; 40 | case 'onInvalidateAmbientOffload': 41 | _notifyAmbientCallbacks((callback) => callback.onInvalidateAmbientOffload()); 42 | break; 43 | } 44 | } 45 | 46 | void _notifyAmbientCallbacks(Function(AmbientCallback callback) fn) { 47 | final callbacks = List.from(_ambientCallbacks); 48 | for (final callback in callbacks) { 49 | try { 50 | fn(callback); 51 | } catch (e, st) { 52 | debugPrint('Failed callback: $callback\n$e\n$st'); 53 | } 54 | } 55 | } 56 | 57 | /// Fetches the shape of the watch face 58 | Future getShape() async { 59 | try { 60 | return (await _channel.invokeMethod('getShape'))!; 61 | } on PlatformException catch (e, st) { 62 | // Default to round 63 | debugPrint('Error calling getShape: $e\n$st'); 64 | return 'round'; 65 | } 66 | } 67 | 68 | /// Tells the application if we are currently in ambient mode 69 | Future isAmbient() async { 70 | try { 71 | return (await _channel.invokeMethod('isAmbient'))!; 72 | } on PlatformException catch (e, st) { 73 | debugPrint('Error calling isAmbient: $e\n$st'); 74 | return false; 75 | } 76 | } 77 | 78 | /// Sets whether this activity's task should be moved to the front when 79 | /// the system exits ambient mode. 80 | /// 81 | /// If true, the activity's task may be moved to the front if it was the 82 | /// last activity to be running when ambient started, depending on how 83 | /// much time the system spent in ambient mode. 84 | /// 85 | Future setAutoResumeEnabled(bool enabled) async { 86 | try { 87 | await _channel.invokeMethod( 88 | 'setAutoResumeEnabled', 89 | {'enabled': enabled}, 90 | ); 91 | } on PlatformException catch (e, st) { 92 | debugPrint('Error calling setAutoResumeEnabled: $e\n$st'); 93 | rethrow; 94 | } 95 | } 96 | 97 | /// Sets whether this activity is currently in a state that supports ambient offload mode. 98 | Future setAmbientOffloadEnabled(bool enabled) async { 99 | try { 100 | await _channel.invokeMethod( 101 | 'setAmbientOffloadEnabled', 102 | {'enabled': enabled}, 103 | ); 104 | } on PlatformException catch (e, st) { 105 | debugPrint('Error calling setAmbientOffloadEnabled: $e\n$st'); 106 | rethrow; 107 | } 108 | } 109 | } 110 | 111 | /// Provides details of current ambient mode configuration. 112 | class AmbientDetails { 113 | const AmbientDetails(this.burnInProtection, this.lowBitAmbient); 114 | 115 | /// Used to indicate whether burn-in protection is required. 116 | /// 117 | /// When this property is set to true, views must be shifted around 118 | /// periodically in ambient mode. To ensure that content isn't 119 | /// shifted off the screen, avoid placing content within 10 pixels 120 | /// of the edge of the screen. Activities should also avoid solid 121 | /// white areas to prevent pixel burn-in. Both of these 122 | /// requirements only apply in ambient mode, and only when 123 | /// this property is set to true. 124 | final bool burnInProtection; 125 | 126 | /// Used to indicate whether the device has low-bit ambient mode. 127 | /// 128 | /// When this property is set to true, the screen supports fewer bits 129 | /// for each color in ambient mode. In this case, activities should 130 | /// disable anti-aliasing in ambient mode. 131 | final bool lowBitAmbient; 132 | } 133 | 134 | /// Callback to receive ambient mode state changes. 135 | abstract class AmbientCallback { 136 | /// Called when an activity is entering ambient mode. 137 | void onEnterAmbient(AmbientDetails ambientDetails) {} 138 | 139 | /// Called when an activity should exit ambient mode. 140 | void onExitAmbient() {} 141 | 142 | /// Called when the system is updating the display for ambient mode. 143 | void onUpdateAmbient() {} 144 | 145 | /// Called to inform an activity that whatever decomposition it has sent to 146 | /// Sidekick is no longer valid and should be re-sent before enabling ambient offload. 147 | void onInvalidateAmbientOffload() {} 148 | } 149 | -------------------------------------------------------------------------------- /lib/wear.dart: -------------------------------------------------------------------------------- 1 | library wear; 2 | 3 | export 'src/ambient_widget.dart'; 4 | export 'src/shape_widget.dart'; 5 | export 'src/wear.dart'; 6 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: wear 2 | description: A plugin that offers Flutter support for Wear OS by Google 3 | version: 1.1.0 4 | homepage: https://github.com/fluttercommunity/flutter_wear_plugin 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | flutter: ">=2.5.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | 14 | flutter: 15 | plugin: 16 | platforms: 17 | android: 18 | package: com.mjohnsullivan.flutterwear.wear 19 | pluginClass: WearPlugin 20 | --------------------------------------------------------------------------------