Try native code calls from Flutter to Java (Android) and Objective-C (iOS) using MethodChannel It was.
I tried running the sample code described in Writing custom platform-specific code, so make a note of the actual flow.
Click here for Kotlin / Swift version I tried calling Kotlin / Swift native code from Flutter --Qiita
Click here for sample source https://github.com/unsolublesugar/flutter-sample-custom-platform-specific-code-java-objc
First, create a sample project. The sample app implements native code to get the battery level.
In Flutter v1.9.1, the default native languages were Kotlin and Swift, so create a project in Java / Objective-C with options.
$ flutter create -i objc -a java batterylevel
If you are creating a project with VSCode, you can switch the language used by selecting [Extensions]-> [Dart & Flutter] in the settings. You can access the settings in one shot by searching for "flutter create language".
Add the implementation of the sample screen widget and the battery level acquisition method. Below is the main.dart
file, which is an excerpt of only _MyHomePageState
.
main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
...
class _MyHomePageState extends State<MyHomePage> {
static const platform = const MethodChannel('samples.flutter.dev/battery');
String _batteryLevel = 'Unknown battery level.';
Future<void> _getBatteryLevel() async {
// Get battery level.
String batteryLevel;
try {
final int result = await platform.invokeMethod('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
@override
Widget build(BuildContext context) {
return Material(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
RaisedButton(
child: Text('Get Battery Level'),
onPressed: _getBatteryLevel,
),
Text(_batteryLevel),
],
),
),
);
}
}
The battery level acquisition method getBatteryLevel
called by MethodChannel
is defined on the Java and Objective-C sides, respectively.
Now let's write the Java (Android) code.
Added import to MainActivity.java
.
MainActivity.java
import android.os.Bundle;
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;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
Define MethodChannel
in the ʻonCreate
method of MainActivity
and call setMethodCallHandler
.
MainActivity.java
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "samples.flutter.dev/battery";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
new MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, Result result) {
}
});
}
}
ここで定義したチャンネル名samples.flutter.dev/battery
を、Flutter側の呼び出しで使用します。
Add code to get the battery level of your Android device.
MainActivity.java
private int getBatteryLevel() {
int batteryLevel = -1;
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
} else {
Intent intent = new ContextWrapper(getApplicationContext()).
registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /
intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
}
return batteryLevel;
}
Added code to call getBatteryLevel ()
on MethodChannel
.
MainActivity.java
@Override
public void onMethodCall(MethodCall call, Result result) {
// Note: this method is invoked on the main thread.
if (call.method.equals("getBatteryLevel")) {
int batteryLevel = getBatteryLevel();
if (batteryLevel != -1) {
result.success(batteryLevel);
} else {
result.error("UNAVAILABLE", "Battery level not available.", null);
}
} else {
result.notImplemented();
}
}
Now you are ready to call Java native code for Android.
If you build run the above code on the emulator, you will see a "Get Battery Level" button and "Unknown battery level." Text.
Get the battery level of your device with the tap of a button. The text display switches.
Next, I will write the Objective-C (iOS) code.
Open the ios folder in the target project and open ʻAppDelegate.m under the
Runner` folder.
Defines the implementation of MethodChannel
as in Android.
AppDelegate.m
#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;
FlutterMethodChannel* batteryChannel = [FlutterMethodChannel
methodChannelWithName:@"samples.flutter.dev/battery"
binaryMessenger:controller];
[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
}];
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
Added the battery level acquisition method getBatteryLevel
before @end
.
AppDelegate.m
- (int)getBatteryLevel {
UIDevice* device = UIDevice.currentDevice;
device.batteryMonitoringEnabled = YES;
if (device.batteryState == UIDeviceBatteryStateUnknown) {
return -1;
} else {
return (int)(device.batteryLevel * 100);
}
}
Call getBatteryLevel
with setMethodCallHandler
.
AppDelegate.m
__weak typeof(self) weakSelf = self;
[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
if ([@"getBatteryLevel" isEqualToString:call.method]) {
int batteryLevel = [weakSelf getBatteryLevel];
if (batteryLevel == -1) {
result([FlutterError errorWithCode:@"UNAVAILABLE"
message:@"Battery info unavailable"
details:nil]);
} else {
result(@(batteryLevel));
}
} else {
result(FlutterMethodNotImplemented);
}
}];
When you launch iOS Simulator, you will see buttons and text just like Android. When you tap the button, getBatteryLevel ()
is called via MethodChannel and the text is updated.
Since the battery API is not supported on iOS Simulator, the message "Battery info unavailable" is displayed. Now you can see that the code written on the Objective-C side is working.
Flutter has a terrifying amount of official documentation and is awesome.
Recommended Posts