From 21ccfa459fcfb0609da46299fed6e10c9e77332b Mon Sep 17 00:00:00 2001 From: Koji Wakamiya Date: Wed, 20 Mar 2024 01:27:15 +0900 Subject: [PATCH] feat(battery_plus)!: Migrate to package:web (#2720) --- .../battery_plus/example/lib/main.dart | 129 +++++++++++------ .../battery_plus/example/pubspec.yaml | 3 +- .../lib/src/battery_plus_web.dart | 131 +++++++++++------- .../battery_plus/battery_plus/pubspec.yaml | 5 +- 4 files changed, 171 insertions(+), 97 deletions(-) diff --git a/packages/battery_plus/battery_plus/example/lib/main.dart b/packages/battery_plus/battery_plus/example/lib/main.dart index a6f348224d..5645d82b33 100644 --- a/packages/battery_plus/battery_plus/example/lib/main.dart +++ b/packages/battery_plus/battery_plus/example/lib/main.dart @@ -42,9 +42,24 @@ class _MyHomePageState extends State { @override void initState() { super.initState(); - _battery.batteryState.then(_updateBatteryState); - _batteryStateSubscription = - _battery.onBatteryStateChanged.listen(_updateBatteryState); + try { + _battery.batteryState.then( + _updateBatteryState, + onError: (e) { + _showError('onError: batteryState: $e'); + _updateBatteryState(BatteryState.unknown); + }, + ); + _batteryStateSubscription = _battery.onBatteryStateChanged.listen( + _updateBatteryState, + onError: (e) { + _showError('onError: onBatteryStateChanged: $e'); + _updateBatteryState(BatteryState.unknown); + }, + ); + } on Error catch (e) { + _showError('catch: batteryState: $e'); + } } void _updateBatteryState(BatteryState state) { @@ -54,6 +69,16 @@ class _MyHomePageState extends State { }); } + void _showError(String message) { + // see https://github.com/fluttercommunity/plus_plugins/pull/2720 + // The exception may not be caught in the package and an exception may occur in the caller, so use try-catch as needed. + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(message), + ), + ); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -77,55 +102,69 @@ class _MyHomePageState extends State { const SizedBox(height: 24), ElevatedButton( onPressed: () { - _battery.batteryLevel.then( - (batteryLevel) { - showDialog( - context: context, - builder: (_) => AlertDialog( - content: Text('Battery: $batteryLevel%'), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context); - }, - child: const Text('OK'), - ) - ], - ), - ); - }, - ); + try { + _battery.batteryLevel.then( + (batteryLevel) { + showDialog( + context: context, + builder: (_) => AlertDialog( + content: Text('Battery: $batteryLevel%'), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: const Text('OK'), + ) + ], + ), + ); + }, + onError: (e) { + _showError('onError: batteryLevel: $e'); + }, + ); + } on Error catch (e) { + _showError('catch: batteryLevel: $e'); + } }, child: const Text('Get battery level'), ), const SizedBox(height: 24), ElevatedButton( onPressed: () { - _battery.isInBatterySaveMode.then( - (isInPowerSaveMode) { - showDialog( - context: context, - builder: (_) => AlertDialog( - title: const Text( - 'Is in Battery Save mode?', - style: TextStyle(fontSize: 20), - ), - content: Text( - "$isInPowerSaveMode", - style: const TextStyle(fontSize: 18), + try { + _battery.isInBatterySaveMode.then( + (isInPowerSaveMode) { + showDialog( + context: context, + builder: (_) => AlertDialog( + title: const Text( + 'Is in Battery Save mode?', + style: TextStyle(fontSize: 20), + ), + content: Text( + "$isInPowerSaveMode", + style: const TextStyle(fontSize: 18), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: const Text('Close'), + ) + ], ), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context); - }, - child: const Text('Close'), - ) - ], - ), - ); - }, - ); + ); + }, + onError: (e) { + _showError('onError: isInBatterySaveMode: $e'); + }, + ); + } on Error catch (e) { + _showError('catch: isInBatterySaveMode: $e'); + } }, child: const Text('Is in Battery Save mode?'), ) diff --git a/packages/battery_plus/battery_plus/example/pubspec.yaml b/packages/battery_plus/battery_plus/example/pubspec.yaml index 3832e0afb2..241ca8edc4 100644 --- a/packages/battery_plus/battery_plus/example/pubspec.yaml +++ b/packages/battery_plus/battery_plus/example/pubspec.yaml @@ -2,7 +2,8 @@ name: battery_plus_example description: Demonstrates how to use the battery_plus plugin. environment: - sdk: '>=2.18.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' + flutter: '>=3.19.0' dependencies: flutter: diff --git a/packages/battery_plus/battery_plus/lib/src/battery_plus_web.dart b/packages/battery_plus/battery_plus/lib/src/battery_plus_web.dart index fe5bd4213d..97e1610e9f 100644 --- a/packages/battery_plus/battery_plus/lib/src/battery_plus_web.dart +++ b/packages/battery_plus/battery_plus/lib/src/battery_plus_web.dart @@ -1,85 +1,100 @@ import 'dart:async'; -import 'dart:html' as html show window, BatteryManager, Navigator; -import 'dart:js_util'; +import 'dart:js_interop'; + import 'package:battery_plus_platform_interface/battery_plus_platform_interface.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +import 'package:web/web.dart' as web; /// The web implementation of the BatteryPlatform of the Battery plugin. +/// +/// The Battery Status API is not supported by Firefox and Safari. +/// Therefore, when using this plugin, recommend that test not only in Chrome but also in Firefox and Safari. +/// In some environments, accessing this plugin from Firefox or Safari will cause unexpected exception. +/// If an unexpected Exception occurs, try-catch at the point where the method is being called. class BatteryPlusWebPlugin extends BatteryPlatform { /// Constructs a BatteryPlusPlugin. - BatteryPlusWebPlugin(html.Navigator navigator) - : _getBattery = navigator.getBattery; - - /// A check to determine if this version of the plugin can be used. - // ignore: unnecessary_null_comparison - bool get isSupported => html.window.navigator.getBattery != null; + BatteryPlusWebPlugin(); - late final Future Function() _getBattery; + /// Return [BatteryManager] if the BatteryManager API is supported by the User Agent. + Future _getBatteryManager() async { + try { + return await web.window.navigator.getBattery().toDart; + } on NoSuchMethodError catch (_) { + // BatteryManager API is not supported this User Agent. + return null; + } on Object catch (_) { + // Unexpected exception occurred. + return null; + } + } /// Factory method that initializes the Battery plugin platform with an instance /// of the plugin for the web. static void registerWith(Registrar registrar) { - BatteryPlatform.instance = BatteryPlusWebPlugin(html.window.navigator); + BatteryPlatform.instance = BatteryPlusWebPlugin(); } /// Returns the current battery level in percent. @override Future get batteryLevel async { - if (isSupported) { - // level is a number representing the system's battery charge level scaled to a value between 0.0 and 1.0 - final batteryManager = await _getBattery() as html.BatteryManager; - final level = batteryManager.level ?? 0; - return level * 100 as int; + final batteryManager = await _getBatteryManager(); + if (batteryManager == null) { + return 0; } - return 0; + + // level is a number representing the system's battery charge level scaled to a value between 0.0 and 1.0 + final level = batteryManager.level; + return level * 100 as int; } /// Returns the current battery state. @override Future get batteryState async { - if (isSupported) { - final battery = await _getBattery() as html.BatteryManager; - if (battery.charging != null) { - return _checkBatteryChargingState(battery.charging!); - } + final batteryManager = await _getBatteryManager(); + if (batteryManager == null) { + return BatteryState.unknown; } - return BatteryState.unknown; + + return _checkBatteryChargingState(batteryManager.charging); } StreamController? _batteryChangeStreamController; - late Stream _batteryChange; + Stream? _batteryChange; /// Returns a Stream of BatteryState changes. @override - Stream get onBatteryStateChanged { - if (_batteryChangeStreamController == null && isSupported) { - _batteryChangeStreamController = StreamController(); - - _getBattery().then( - (battery) { - _batteryChangeStreamController! - .add(_checkBatteryChargingState(battery.charging)); - setProperty( - battery, - 'onchargingchange', - allowInterop( - (event) { - _batteryChangeStreamController! - .add(_checkBatteryChargingState(battery.charging)); - }, - ), - ); - }, + Stream get onBatteryStateChanged async* { + final batteryManager = await _getBatteryManager(); + if (batteryManager == null) { + yield BatteryState.unknown; + return; + } + + if (_batteryChange != null) { + yield* _batteryChange!; + return; + } + + _batteryChangeStreamController = StreamController(); + _batteryChangeStreamController?.add( + _checkBatteryChargingState(batteryManager.charging), + ); + + batteryManager.onchargingchange = (web.Event _) { + _batteryChangeStreamController?.add( + _checkBatteryChargingState(batteryManager.charging), ); + }.toJS; - _batteryChange = - _batteryChangeStreamController!.stream.asBroadcastStream(); + _batteryChangeStreamController?.onCancel = () { + _batteryChangeStreamController?.close(); - _batteryChangeStreamController?.onCancel = () { - _batteryChangeStreamController?.close(); - }; - } - return _batteryChange; + _batteryChangeStreamController = null; + _batteryChange = null; + }; + + _batteryChange = _batteryChangeStreamController!.stream.asBroadcastStream(); + yield* _batteryChange!; } BatteryState _checkBatteryChargingState(bool charging) { @@ -90,3 +105,21 @@ class BatteryPlusWebPlugin extends BatteryPlatform { } } } + +extension on web.Navigator { + /// https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getBattery + external JSPromise getBattery(); +} + +/// BatteryManager API +/// https://developer.mozilla.org/en-US/docs/Web/API/BatteryManager +extension type BatteryManager(JSObject _) implements JSObject { + /// https://developer.mozilla.org/en-US/docs/Web/API/BatteryManager/level + external double get level; + + /// https://developer.mozilla.org/en-US/docs/Web/API/BatteryManager/charging + external bool get charging; + + /// https://developer.mozilla.org/en-US/docs/Web/API/BatteryManager/chargingchange_event + external set onchargingchange(JSFunction fn); +} diff --git a/packages/battery_plus/battery_plus/pubspec.yaml b/packages/battery_plus/battery_plus/pubspec.yaml index 46f1bfa9d7..00474bbd11 100644 --- a/packages/battery_plus/battery_plus/pubspec.yaml +++ b/packages/battery_plus/battery_plus/pubspec.yaml @@ -31,6 +31,7 @@ dependencies: battery_plus_platform_interface: ^2.0.0 meta: ^1.8.0 upower: ^0.7.0 + web: ^0.5.0 dev_dependencies: flutter_test: @@ -41,5 +42,5 @@ dev_dependencies: plugin_platform_interface: ^2.1.4 environment: - sdk: ">=2.18.0 <4.0.0" - flutter: ">=3.3.0" + sdk: ">=3.3.0 <4.0.0" + flutter: ">=3.19.0"