🍣

ケツリカ(結論から理解)するFlutterでAndroid固有の機能を使う方法

2024/08/10に公開

どうやってFlutterでAndroid固有の機能を作るのか

なぜ、DartにMainActivity.ktをインポートする必要がないのか

固有の機能を含めたUIを構築

lib\main.dart

main
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

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

class MainApp extends StatefulWidget {
  const MainApp({super.key});

  
  State<MainApp> createState() => _MainAppState();
}

class _MainAppState extends State<MainApp> {
  static const platform = MethodChannel('samples.flutter.dev/battery');
  // バッテリーレベルを格納する変数
  String _batteryLevel = 'Unknown battery level.';

  Future<void> _getBatteryLevel() async {
    String batteryLevel;
    try {
      final result = await platform.invokeMethod<int>('getBatteryLevel');
      batteryLevel = 'Battery level at $result % .';
    } on PlatformException catch (e) {
      batteryLevel = "Failed to get battery level: '${e.message}'.";
    }
    setState(() {
      _batteryLevel = batteryLevel;
    });
  }

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Directionality(
        textDirection: TextDirection.ltr,
        child: Scaffold(
          appBar: AppBar(
            title: const Text('Battery Level'),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                ElevatedButton(
                  onPressed: _getBatteryLevel,
                  child: const Text('Get Battery Level'),
                ),
                Text(_batteryLevel),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

kotlinでバッテリーを取得

android\app\src\main\kotlin\com\example\batterylevel\MainActivity.kt

MainActivity
package com.example.batterylevel

import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES

class MainActivity: FlutterActivity() {
  private val CHANNEL = "samples.flutter.dev/battery"

  override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
      // このメソッドはメインスレッドで呼び出されます。
      call, result ->
      if (call.method == "getBatteryLevel") {
        val batteryLevel = getBatteryLevel()

        if (batteryLevel != -1) {
          result.success(batteryLevel)
        } else {
          result.error("UNAVAILABLE", "バッテリーレベルが利用できません。", null)
        }
      } else {
        result.notImplemented()
      }
    }
  }

  private fun getBatteryLevel(): Int {
    val batteryLevel: Int
    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
      val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
      batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
    } else {
      val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
      batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
    }

    return batteryLevel
  }
}
解説

パッケージ宣言

package com.example.batterylevel

アプリケーションのパッケージ名を指定している。逆ドメイン形式で書かれ、アプリケーションの一意性を保つために使用される。

インポート文

import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES

FlutterActivity: Flutterのメインエントリポイントで、Flutter UIが表示される場所。
FlutterEngine: Flutterのエンジンを管理し、Dartコードとネイティブコードのブリッジを提供する。
MethodChannel: Flutterとネイティブ(Android)間で通信するためのチャネル。
Context や Intent などのAndroid API: Androidデバイスのコンテキスト情報を取得したり、バッテリー情報を取得するために使用される。

クラス宣言

class MainActivity: FlutterActivity() {
  private val CHANNEL = "samples.flutter.dev/battery"

FlutterActivityを継承することで、FlutterアプリがAndroidで正しく動くための基本的な機能をすぐに使えるようになり、特別な設定をしなくても、Dartで書いたコードがそのままAndroidの画面に表示されるようになる。
CHANNEL: MethodChannelの名前。この名前を使ってDartコードと通信する。

configureFlutterEngine メソッド

override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
      call, result ->
      if (call.method == "getBatteryLevel") {
        val batteryLevel = getBatteryLevel()

        if (batteryLevel != -1) {
          result.success(batteryLevel)

configureFlutterEngine: Flutterエンジンを設定するためのメソッドで、FlutterActivityのライフサイクルに沿って呼び出される。
MethodChannel: Flutter側からこのチャネルを介して送信されるメッセージ(例: getBatteryLevel)を受け取り、それに応じた処理を行う。

バッテリー情報を取得するロジック

getBatteryLevel() というメソッドを呼び出して、デバイスのバッテリー情報を取得し、それをresult.successメソッドでFlutterに返す。
バッテリー情報が取得できなかった場合や何か問題があった場合、result.errorを呼び出してエラーメッセージをFlutterに送信する。

参考文献:
https://docs.flutter.dev/platform-integration/platform-channels#codec

感想:参考文献はコードが意外と古いので自分なにりアレンジすると良いかもしれません。

Discussion