Flutter Analysis and Practice: Native Capability-Based Plug-In Extension

2.1.1 Flutter Plug-Ins

Figure 2–1

2.1.2 Platform Channels

2.1.2.1 Flutter App Calls Native APIs

Figure 2–2

2.1.2.2 Platform Channel Architecture

Figure 2–3
  • The Flutter app (client) calls the MethodChannel class to send call messages to the platform.
  • The Android platform (host) calls the MethodChannel class to receive call messages.
  • The iOS platform (host) calls the FlutterMethodChannel class to receive call messages.

2.1.3 Flutter Battery Plug-In

Figure 2–4

2.1.3.1 Create a Plug-in

Figure 2–5

2.1.3.2 Plug-In in the Flutter Side

static const MethodChannel _methodChannel = const MethodChannel ('samples. flutter.io/battery');

//
Future<String> getBatteryLevel() async {
String batteryLevel;
try {
final int result = await _methodChannel.invokeMethod('getBatteryLevel', {'paramName':'paramVale'});
batteryLevel = 'Battery level: $result%.';
} catch(e) {
batteryLevel = 'Failed to get battery level.';
}
return batteryLevel;
}
static const EventChannel _eventChannel = const EventChannel('samples. flutter.io/charging');  void listenNativeEvent() {
_eventChannel.receiveBroadcastStream().listen(_onEvent, onError:_onError);
}
void _onEvent(Object event) {
print("Battery status: ${event == 'charging' ? '' : 'dis'}charging.");
}
void _onError(Object error) {
print('Battery status: unknown.');
}

2.1.3.3 Plug-In in the Android Side

import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}
}
public static void registerWith(Registrar registrar) {
/**
* Channel名称:必须与Flutter App的Channel名称一致
*/
private static final String METHOD_CHANNEL = "samples.flutter.io/battery";
private static final String EVENT_CHANNEL = "samples.flutter.io/charging";
// 实例Plugin,并绑定到Channel上
FlutterPluginBatteryLevel plugin = new FlutterPluginBatteryLevel();
final MethodChannel methodChannel = new MethodChannel (registrar. messenger(), METHOD_CHANNEL);
methodChannel.setMethodCallHandler(plugin);
final EventChannel eventChannel = new EventChannel(registrar.messenger(), EVENT_CHANNEL);
eventChannel.setStreamHandler(plugin);
}
  • Set Channel to be the same as the channel name of the Flutter app.
  • Pass the registrar FlutterActivity during the initialization of MethodChannel and EventChannel.
  • Set MethodCallHandler, the handler of MethodChannel.
  • Set EventChannel.StreamHandler, the handler of EventChannel.
public class FlutterPluginBatteryLevel implements MethodCallHandler, EventChannel.StreamHandler {    /**
* MethodCallHandler
*/
@Override
public void onMethodCall(MethodCall call, Result result) {
if (call.method.equals("getBatteryLevel")) {
Random random = new Random();
result.success(random.nextInt(100));
} else {
result.notImplemented();
}
}
/**
* EventChannel.StreamHandler
*/
@Override
public void onListen(Object obj, EventChannel.EventSink eventSink) {
BroadcastReceiver chargingStateChangeReceiver = createChargingState ChangeReceiver(events);
}
@Override
public void onCancel(Object obj) {
}
private BroadcastReceiver createChargingStateChangeReceiver(final EventSink events) {
return new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
if (status == BatteryManager.BATTERY_STATUS_UNKNOWN) {
events.error("UNAVAILABLE", "Charging status unavailable", null);
} else {
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL;
events.success(isCharging ? "charging" : "discharging");
}
}
};
}
}

2.1.3.4 Plug-In in the iOS Side

/**
* Channel名称:必须与Flutter App的Channel名称一致
*/
#define METHOD_CHANNEL "samples.flutter.io/battery";
#define EVENT_CHANNEL "samples.flutter.io/charging";
@implementation AppDelegate- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
/**
* 注册Plugin
*/
[GeneratedPluginRegistrant registerWithRegistry:self];

/**
* FlutterViewController
*/
FlutterViewController* controller = (FlutterViewController*)self.window. rootViewController;
/**
* FlutterMethodChannel & Handler
*/
FlutterMethodChannel* batteryChannel = [FlutterMethodChannel methodChannelWithName:METHOD_CHANNEL binaryMessenger:controller];
[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
if ([@"getBatteryLevel" isEqualToString:call.method]) {
int batteryLevel = [self getBatteryLevel];
result(@(batteryLevel));
} else {
result(FlutterMethodNotImplemented);
}
}];

/**
* FlutterEventChannel & Handler
*/
FlutterEventChannel* chargingChannel = [FlutterEventChannel eventChannelWithName:EVENT_CHANNEL binaryMessenger:controller];
[chargingChannel setStreamHandler:self];

return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
@interface AppDelegate () <FlutterStreamHandler>@property (nonatomic, copy)   FlutterEventSink     eventSink;@end- (FlutterError*)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink {
self.eventSink = eventSink;
// 监听电池状态
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(onBatteryStateDidChange:)
name:UIDeviceBatteryStateDidChangeNotification
object:nil];
return nil;
}
- (FlutterError*)onCancelWithArguments:(id)arguments {
[[NSNotificationCenter defaultCenter] removeObserver:self];
self.eventSink = nil;
return nil;
}
- (void)onBatteryStateDidChange:(NSNotification*)notification {
if (self.eventSink == nil) return;
UIDeviceBatteryState state = [[UIDevice currentDevice] batteryState];
switch (state) {
case UIDeviceBatteryStateFull:
case UIDeviceBatteryStateCharging:
self.eventSink(@"charging");
break;
case UIDeviceBatteryStateUnplugged:
self.eventSink(@"discharging");
break;
default:
self.eventSink([FlutterError errorWithCode:@"UNAVAILABLE"
message:@"Charging status unavailable"
details:nil]);
break;
}
}

2.1.4 Plug-In Loading

2.1.4.1 Add a Package to the Flutter App

  • Edit the pubspec.yaml file in the app root directory to manage dependencies.
  • Run the flutter packages get command or click Packages Get in IntelliJ IDEA.
  • Reference a package and run the app again.

2.1.4.2 Hosted Packages

$flutter packages pub publish --dry-run
$flutter packages pub publish
dependencies:
url_launcher: ^3.0.0

2.1.4.3 Git Packages (Remote)

// cd 到 flutter_remote_package  
flutter_remote_package $:git init
flutter_remote_package $:git remote add origin git@gitlab.alibaba-inc. com:churui/flutter_remote_package.git
flutter_remote_package $:git add .
flutter_remote_package $:git commit
flutter_remote_package $:git commit -m"init"
flutter_remote_package $:git push -u origin master
flutter_remote_package $:git tag 0.0.1
dependencies:
flutter_remote_package:
git:
url: git@gitlab.alibaba-inc.com:churui/flutter_remote_package.git
ref: 0.0.1

2.1.4.4 Path Packages (Local)

Figure 2–6
dependencies:
flutter_plugin_batterylevel:
path: plugins/flutter_plugin_batterylevel

2.1.5 Problems

2.1.5.1 Use Xcode to Edit Plug-Ins

2.1.5.2 No Plug-in Is Found at Runtime When iOS Compilation is Correct

@implementation AppDelegate- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions {
// Plugin注册方法
[GeneratedPluginRegistrant registerWithRegistry:self];

// 显示Window
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[self.window setRootViewController:[[FlutterViewController alloc] initWithNibName:nil bundle:nil]]];
[self.window setBackgroundColor:[UIColor whiteColor]];
[self.window makeKeyAndVisible];

return [super application:application didFinishLaunchingWithOptions: launchOptions];
}
@end

2.1.5.3 A Native Project Fails to Call the Flutter App

2.1.5.4 iOS Plug-in Is Registered with the Specified FlutterViewController

- (NSObject<FlutterBinaryMessenger>*)binaryMessenger;
- (NSObject<FlutterTextureRegistry>*)textures;

2.1.6 More Discussions

Original Source:

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Alibaba Cloud

Alibaba Cloud

Follow me to keep abreast with the latest technology news, industry insights, and developer trends. Alibaba Cloud website:https://www.alibabacloud.com