【Flutter】バッテリーの充電状態を検知する方法

本記事はFlutter Advent Calendar2021 3日目の記事です。

スマホを使うにあたって、切っても切れない関係にあるのがバッテリーですよね。
今回はFlutterでバッテリーの充電状態を検知する方法を提供するパッケージ、
battery_plusを紹介します。

https://pub.dev/packages/battery_plus

こちらについて日本語での解説記事が見つからないため、
今回紹介しようと考えた次第です。

本記事では、

- buttery_plusパッケージでできること
- 実装サンプル

を記載します。
参考になれば幸いです。

##buttery_plusパッケージでできること##

buttery_plusパッケージでできることは以下の3つです。

- 充電量(%)を取得する
- 充電状態(後述)の変化を検知する
- パワーセーフモードか否かを取得する

やれることはシンプルですね。

一つ一つ解説していきます。
####充電量(%)を取得する####
``batteryLevel``のメソッドで現在の充電量を%で取得します。
このメソッドの返り値は``Future``型なのは注意です。

```dart
//Batteryのインスタンスを取得
battery = Battery();
//充電量をawaitで取得
int batteryLevel = await battery.batteryLevel;
```
####充電状態の変化を検知する####

充電状態``(BatteryState)``は``enum``で定義されており、
以下の4つの状態があります。

- ``charging``:充電中の状態
- ``discharging``：充電されておらず、バッテリーが減っている状態
- ``full``：バッテリーが満タンの状態
- ``unknown``：充電状態が検知できない状態

これらの変化を検知するメソッド``onBatteryStateChanged``が用意されています。
返り値が``Stream``型なのがポイントです。
（使用例は実装サンプルをご覧ください。）

####パワーセーフモードか否かを取得する####

スマホがパワーセーフモードか否かを取得します。
取得は``isInBatterySaveMode``のメソッドで行います。
これも返り値が``Future``型なのに注意です。

```dart
//Batteryのインスタンスを取得
battery = Battery();
//パワーセーブモードか否かをawaitで取得
bool isInPowerSaveMode = await _battery.isInBatterySaveMode;
```


##実装サンプル##
実装サンプルを紹介します。
公式のExampleをベースに、
不要部分の削除、日本語のコメントを追加したものとなります。

まず最初に``pubspec.yaml``に以下のコードを追加し、``pub get``を行なってください。
（バージョンはパッケージの紹介ページを見て最新に更新してください。）

```yaml
battery_plus: ^2.0.1
```

https://pub.dev/packages/battery_plus/install

###サンプルコード###

```dart
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:battery_plus/battery_plus.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Battery Plus Demo',
      home: BatteryPage(title: 'Battery Plus Demo'),
    );
  }
}

class BatteryPage extends StatefulWidget {
  const BatteryPage({Key? key, this.title}) : super(key: key);

  final String? title;

  @override
  _BatteryPageState createState() => _BatteryPageState();
}

class _BatteryPageState extends State<BatteryPage> {
  //バッテリーのインスタンスを取得
  final Battery _battery = Battery();

  //バッテリーの状態(充電中等)をStateとして保持
  BatteryState? _batteryState;
  //StreamSubscriptionで監視する（初期化はinitStateで行う）
  StreamSubscription<BatteryState>? _batteryStateSubscription;

  @override
  void initState() {
    super.initState();
    //Batteryの状態の変化を検知し、setStateするように設定
    _batteryStateSubscription =
        _battery.onBatteryStateChanged.listen((BatteryState state) {
          setState(() {
            _batteryState = state;
          });
        });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Plugin example app'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [

            //バッテリーの状態を表示
            Text('$_batteryState'),
            ElevatedButton(
              onPressed: () async {
                //充電残量の取得
                final batteryLevel = await _battery.batteryLevel;
                showDialog<void>(
                  context: context,
                  builder: (_) => AlertDialog(
                    content: Text('Battery: $batteryLevel%'),
                    actions: <Widget>[
                      TextButton(
                        onPressed: () {
                          Navigator.pop(context);
                        },
                        child: const Text('OK'),
                      )
                    ],
                  ),
                );
              },
              child: const Text('Get battery level'),
            ),
            ElevatedButton(
                onPressed: () async {
                  //パワーセーブモードか否かの取得
                  final isInPowerSaveMode = await _battery.isInBatterySaveMode;
                  showDialog<void>(
                    context: context,
                    builder: (_) => AlertDialog(
                      content: Text('Is on low power mode: $isInPowerSaveMode'),
                      actions: <Widget>[
                        TextButton(
                          onPressed: () {
                            Navigator.pop(context);
                          },
                          child: const Text('OK'),
                        )
                      ],
                    ),
                  );
                },
                child: const Text('Is on low power mode'))
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
    //Subscriptionのストップ
    if (_batteryStateSubscription != null) {
      _batteryStateSubscription!.cancel();
    }
  }
}
```

####ポイント####

``StreamSubscription<BatteryState>``で充電状態の検知を行なっているのがポイントです。
``listen``でリスナーを設定し、
充電状態が変化するたびに``setState``されるように設定しています。
``dispose``で``Subscription``を停止するのを忘れないようにしましょう。

####実行時の注意点####
このアプリを実行するときは、``flutter run --release``で実行することをお勧めします。
iPhoneでDebugモードで実行すると接続コードを抜いた瞬間にアプリが落ちてしまいます。
releaseモードで実行することで、接続コードを抜いてもアプリを使用することができます。
充電コードの抜き差しを確認するには必要テクニックですので、ぜひやってみてください。

####GitHub####
本記事で紹介したサンプルコードはGitHubにて公開しています。
気軽にクローンいただければと思います。

https://github.com/Umigishi-Aoi/BatteryPlusSample

##まとめ##

今回はFlutterでバッテリー周りの情報を提供するパッケージ、battery_plusの紹介を行いました。

- buttery_plusパッケージでできること
- 実装サンプル

を記載しました。

バッテリーアプリを作成することはなかなかないかもしれませんが、
バッテリーの数値を表示する、とか、
充電されている時に〜するとか、
実装できることを知っているだけでも
アプリ制作の幅が広がって良いかと思います。

Flutterのアドベントカレンダーで3日目がちょうど空いていたので書いた記事でしたが、いかがでしたでしょうか。
本記事が参考になれば幸いです。