I tried calling Java / Objective-C native code from Flutter

I want to call the native API from Flutter

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

Creating a sample project

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". スクリーンショット 2019-11-07 14.18.38.png

Call code on the Flutter side

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.

Native code on Java side (Android)

Now let's write the Java (Android) code.

Add required import

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;

Implement getBatteryLevel

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.

Run on emulator

If you build run the above code on the emulator, you will see a "Get Battery Level" button and "Unknown battery level." Text.

batterylevel_objc_java.gif

Get the battery level of your device with the tap of a button. The text display switches.

Native code on the Objective-C side (iOS)

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.

Implement getBatteryLevel

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. batterylevel_objc.gif 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

I tried calling Java / Objective-C native code from Flutter
I tried hitting a Java method from ABCL
I tried Drools (Java, InputStream)
I tried using Java REPL
I tried source code analysis
I tried metaprogramming in Java
[JDBC] I tried to access the SQLite3 database from Java.
I tried to interact with Java
I tried UDP communication with Java
I tried the Java framework "Quarkus"
I tried using Java8 Stream API
I tried using JWT in Java
I tried to summarize Java learning (1)
What I learned from doing Java work with Visual Studio Code
I tried to summarize Java 8 now
Sample code using Minio from Java
I tried using Java memo LocalDate
I tried using GoogleHttpClient of Java
I tried using Elasticsearch API in Java
I tried migrating Processing to VS Code
I tried using UICollectionViewListCell added from Xcode12.
I tried Cassandra's Object Mapper for Java
I tried to summarize Java lambda expressions
Calling java from C ++ on Android NDK
I tried the new era in Java
I tried using OpenCV with Java + Tomcat
[Java] Flow from source code to execution
I tried to build Ruby 3.0.0 from source
Try calling Nim from Java via JNI
I tried Google's entrance exam (unofficial) [java]
What I learned from Java monetary calculation
Execute Java code from cpp with cocos2dx
I tried putting Java on my Mac easily
Run R from Java I want to run rJava
Use native libraries from Scala via Java CPP + Java
[Rails] I tried to raise the Rails version from 5.0 to 5.2
java I tried to break a simple block
Try calling the CORBA service from Spring (Java)
I tried to implement deep learning in Java
[API] I tried using the zip code search API
I tried to output multiplication table in Java
I want to write quickly from java to sqlite
I tried to create Alexa skill in Java
I tried to break a block with java (1)
I tried running Java on a Mac terminal
I tried Firebase Local Emulator Suite in Flutter
I tried "Visual Studio Code Installer for Java" which can create Java development environment immediately