🙃

メソッドチャンネルを使ってみたい!

2023/07/23に公開

Flutterからネイティブのコードを呼ぶ

https://docs.flutter.dev/platform-integration/platform-channels
Flutterから、ネイティブのコードを呼ぶことができる仕組みがあるらしいので試してみた。
本当にできるのだろうか。。。。

🦜まずは、AppDelegate.swiftに、Flutterから呼ぶコードを書く

ここに書く。x-code開かなくてもできる。

ios/Runner/AppDelegate.swift
import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  private var methodChannel: FlutterMethodChannel?

  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)

    // FlutterViewControllerを取得
    guard let controller = window?.rootViewController as? FlutterViewController else {
      return false
    }

    // MethodChannelの定義
    methodChannel = FlutterMethodChannel(name: "samples.flutter.dev/dateAndTime", binaryMessenger: controller.binaryMessenger)

    // MethodChannelのメソッドコールのリスナー設定
    methodChannel?.setMethodCallHandler { [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) in
      // getDateAndTimeメソッドの実行
      if call.method == "getDateAndTime" {
        self?.getDateAndTime(result: result)
      } else {
        result(FlutterMethodNotImplemented)
      }
    }

    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }

  // 現在の日付と時刻を取得するメソッド
  private func getDateAndTime(result: FlutterResult) {
    let now = Date()
    let formatter = DateFormatter()
    formatter.dateStyle = .full
    formatter.timeStyle = .medium
    let dateTimeString = formatter.string(from: now)
    result(dateTimeString)
  }
}

🐧Flutter側のコード

メソッドチャンネルを使うコードを書く。今回は今日の曜日と時間を表示するメソッドをSwiftから呼び出す。表示は日本語でされてた!

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

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

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  static const platform =
      const MethodChannel('samples.flutter.dev/dateAndTime');
  String _dateAndTime = 'Unknown';

  
  void initState() {
    super.initState();
    _getDateAndTime();
  }
  // Swiftのコードのみ
  // Future<void> _getDateAndTime() async {
  //   String dateAndTime;
  //   try {
  //     final String result = await platform.invokeMethod('getDateAndTime');
  //     dateAndTime = 'Date and Time: $result';
  //   } on PlatformException catch (e) {
  //     dateAndTime = "Failed to get date and time: '${e.message}'.";
  //   }

  //   setState(() {
  //     _dateAndTime = dateAndTime;
  //   });
  // }
  // Androidのコードを追加
  Future<void> _getDateAndTime() async {
    String dateAndTime;
    try {
      final String result = await platform.invokeMethod('getDateAndTime');
      dateAndTime = 'Date and Time: $result';
    } on PlatformException catch (e) {
      dateAndTime = "Failed to get date and time: '${e.message}'.";
    } on MissingPluginException {
      // この例外処理を追加します
      dateAndTime =
          "Error: MissingPluginException. The functionality might not be available on this platform.";
    }

    setState(() {
      _dateAndTime = dateAndTime;
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Demo Home Page'),
      ),
      body: Center(
        child: Text(_dateAndTime),
      ),
    );
  }
}

実行結果

Androidの場合

こちらにコードを追加。ビルド遅すぎて、確認できてません。すいません🙇‍♂️

android/app/src/main/kotlin/com/jboy/method_channel_app/MainActivity.kt
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import java.text.SimpleDateFormat
import java.util.*

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

    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
            // Note: this method is invoked on the main thread.
            call, result ->
            if (call.method == "getDateAndTime") {
                val dateAndTime = getDateAndTime()

                if (dateAndTime != null) {
                    result.success(dateAndTime)
                } else {
                    result.error("UNAVAILABLE", "Date and Time not available.", null)
                }
            } else {
                result.notImplemented()
            }
        }
    }

    private fun getDateAndTime(): String? {
        val sdf = SimpleDateFormat("EEEE, MMMM d, yyyy 'at' h:mm a", Locale.US)
        return sdf.format(Date())
    }
}

まとめ

iOSは早くできるが、Androidがなぜか遅い?
もしかしたら、数十分ぐらいかかるのかも?
こちらが完成品のコード
https://github.com/sakurakotubaki/FlutterMethodChannel

Androidで使う方法

前回できなかったのは、build.gradleのapplicationIdと一致していないことが問題だったようです。
今回は、端末のストレージの容量を調べるコードをKotlinで作ってみました。

KotlinのコードをFlutterから呼び出す

Androidのネイティブのコードを呼ぶ方法

android/app/src/main/kotlinというKotlinのコードが書かれているフォルダに、
StorageUtil.ktというファイルを作成し、以下のコードを記述する。

このコードは、内部ストレージの情報を取得するコードである。

import android.os.Environment
import android.os.StatFs

object StorageUtil {
    fun getInternalStorageInfo(): String {
        val statFs = StatFs(Environment.getDataDirectory().path)
        val blockSize = statFs.blockSizeLong
        val totalBlocks = statFs.blockCountLong
        val availableBlocks = statFs.availableBlocksLong

        val totalSize = blockSize * totalBlocks
        val availableSize = blockSize * availableBlocks
        val usedSize = totalSize - availableSize

        return "Total: $totalSize, Available: $availableSize, Used: $usedSize"
    }
}

MainActivity.ktに以下のコードを追加する。
追加すると、Android端末の内部ストレージの情報がFlutterで表示される。
Flutterがビルドしたときに、MainActivity.ktを見つけるには、package名を合わせる必要がある。
今回だと、com.jboy.android_callというpackage名になっているので、
MainActivity.ktのpackage名もcom.jboy.android_callにする。

package com.jboy.android_call
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

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

    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
            if (call.method == "getInternalStorageInfo") {
                val storageInfo = StorageUtil.getInternalStorageInfo()
                result.success(storageInfo)
            } else {
                result.notImplemented()
            }
        }
    }
}

成功すると、FlutterでKotlinのコードを呼び出すことができるようになる。

こちらが完成品のソースコード
https://github.com/sakurakotubaki/MethodChannelleKotlin

Discussion