Flutter原生功能调用插件dart_native的使用
Flutter原生功能调用插件dart_native的使用
简介
DartNative 是一个用于在 Dart 和原生 API 之间进行通信的桥梁。它取代了性能较低的 Flutter Channel,提供了更快速和简洁的代码。DartNative 支持动态同步和异步通道,并且可以在不同语言接口之间直接调用,无需序列化参数和返回值。此外,DartNative 还支持 Dart finalizer 和自动生成简洁的桥接代码。
特性
- 动态同步与异步通道:DartNative 可以动态调用任何原生 API,支持同步和异步通道。
- 多语言接口直接调用:无需序列化参数和返回值,提供直接调用和自动对象marshalling。
- Dart finalizer:Dart finalizer 在 Flutter 3 (Dart 2.17) 及以上版本中可用,但通过 DartNative 可以在 Flutter 2.2.0 (Dart 2.13.0) 及以上版本中使用。
- 自动生成简洁的桥接代码:DartNative 支持自动类型转换,使得桥接代码更短、更简单。
支持平台
- iOS
- macOS
- Android
使用方法
基本用法:接口绑定
-
添加依赖: 在
pubspec.yaml
中添加dart_native
和build_runner
:dependencies: dart_native: ^0.7.0 dev_dependencies: build_runner: ^2.0.0
-
Dart 调用原生:
-
Dart 代码:
import 'package:dart_native/dart_native.dart'; final interface = Interface("MyFirstInterface"); String helloWorld() { return interface.invokeMethodSync('hello', args: ['world']); } Future<int> sum(int a, int b) { return interface.invokeMethod('sum', args: [a, b]); }
-
Objective-C 代码:
@implementation DNInterfaceDemo // 注册接口名称 InterfaceEntry(MyFirstInterface) // 注册方法 "hello" InterfaceMethod(hello, myHello:(NSString *)str) { return [NSString stringWithFormat:@"hello %@!", str]; } // 注册方法 "sum" InterfaceMethod(sum, addA:(int32_t)a withB:(int32_t)b) { return @(a + b); } @end
-
Java 代码:
// 加载 libdart_native.so DartNativePlugin.loadSo(); @InterfaceEntry(name = "MyFirstInterface") public class InterfaceDemo extends DartNativeInterface { @InterfaceMethod(name = "hello") public String hello(String str) { return "hello " + str; } @InterfaceMethod(name = "sum") public int sum(int a, int b) { return a + b; } }
-
-
原生调用 Dart:
-
Dart 代码:
interface.setMethodCallHandler('totalCost', (double unitCost, int count, List list) async { return {'totalCost: ${unitCost * count}': list}; });
-
Objective-C 代码:
[self invokeMethod:@"totalCost" arguments:@[@0.123456789, @10, @[@"testArray"]] result:^(id _Nullable result, NSError * _Nullable error) { NSLog(@"%@", result); }];
-
Java 代码:
invokeMethod("totalCost", new Object[]{0.123456789, 10, Arrays.asList("hello", "world")}, new DartNativeResult() { @Override public void onResult(@Nullable Object result) { Map retMap = (Map) result; // do something } @Override public void error(@Nullable String errorMessage) { // do something } } );
-
-
Dart finalizer:
final foo = Bar(); // 自定义实例 unitTest.addFinalizer(() { // 注册 finalizer 回调 print('The instance of \'foo\' has been destroyed!'); // 当 `foo` 被 GC 销毁时,执行此行代码 });
高级用法:动态调用方法
-
步骤 1:添加
dart_native
到依赖项,并将build_runner
添加到开发依赖项。 -
步骤 2:使用
@dartnative/codegen
或手动编写 Dart 代码生成 Dart 包装器代码。 -
步骤 3:使用
dart_native_gen
生成自动类型转换代码:-
3.1:使用
@native
注解 Dart 包装器类:@native class RuntimeSon extends RuntimeStub { RuntimeSon([Class isa]) : super(Class('RuntimeSon')); RuntimeSon.fromPointer(Pointer<Void> ptr) : super.fromPointer(ptr); }
-
3.2:使用
[@nativeRoot](/user/nativeRoot)
注解入口(如main()
):[@nativeRoot](/user/nativeRoot) void main() { runApp(App()); }
-
3.3:运行以下命令生成文件:
flutter packages pub run build_runner build --delete-conflicting-outputs
推荐先运行
clean
:flutter packages pub run build_runner clean
-
-
步骤 4:在生成的文件
<generated-name>.dn.dart
中调用自动生成的函数。函数名称由pubspec.yaml
中的name
决定。[@nativeRoot](/user/nativeRoot) void main() { // 函数名称由 pubspec.yaml 中的 name 生成 runDartNativeExample(); runApp(App()); }
-
步骤 5:编写代码。以下是一些示例:
-
iOS 示例:
-
Dart 代码(生成):
// 创建 Objective-C 对象 RuntimeStub stub = RuntimeStub(); // Dart 函数将被转换为 Objective-C block stub.fooBlock((NSObject a) { print('hello block! ${a.toString()}'); return 101; }); // 支持内置结构体 CGRect rect = stub.fooCGRect(CGRect(4, 3, 2, 1)); print(rect);
-
对应的 Objective-C 代码:
typedef int(^BarBlock)(NSObject *a); @interface RuntimeStub - (CGRect)fooCGRect:(CGRect)rect; - (void)fooBlock:(BarBlock)block; @end
-
-
Android 示例:
-
Dart 代码(生成):
// 创建 Java 对象 RuntimeStub stub = RuntimeStub(); // 获取 Java 列表 List list = stub.getList([1, 2, 3, 4]); // 支持接口 stub.setDelegateListener(DelegateStub());
-
对应的 Java 代码:
public class RuntimeStub { public List<Integer> getList(List<Integer> list) { List<Integer> returnList = new ArrayList<>(); returnList.add(1); returnList.add(2); return returnList; } public void setDelegateListener(SampleDelegate delegate) { delegate.callbackInt(1); } }
-
-
完整示例 Demo
以下是一个完整的示例应用,展示了如何使用 dart_native
插件进行 Dart 和原生代码之间的通信。
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:dart_native/dart_native.dart';
import 'package:dart_native_example/main.dn.dart';
import 'package:dart_native_gen/dart_native_gen.dart';
import 'log.dart';
import 'dn_unit_test.dart';
[@nativeRoot](/user/nativeRoot)
void main() {
Log.setLevel(LogLevel.verbose);
runDartNativeExample();
runApp(const DartNativeApp());
}
final interface = Interface("MyFirstInterface");
class DartNativeApp extends StatefulWidget {
const DartNativeApp({Key? key}) : super(key: key);
[@override](/user/override)
State createState() => _DartNativeAppState();
}
class _DartNativeAppState extends State<DartNativeApp> {
final TextEditingController _controllerA = TextEditingController();
final TextEditingController _controllerB = TextEditingController();
String result = '';
[@override](/user/override)
void initState() {
super.initState();
initPlatformState();
}
// 平台消息是异步的,因此我们在异步方法中初始化
Future<void> initPlatformState() async {
// 示例:原生调用 Dart
interface.setMethodCallHandler('totalCost',
(double unitCost, int count, List list) async {
return {'totalCost: ${unitCost * count}': list};
});
result = helloWorld();
final data = getUTF8Data(result);
// 字节数等于 uint8 列表的长度
final utf8Result =
data.bytes.cast<Utf8>().toDartString(length: data.lengthInBytes);
// 它们应该相等
assert(utf8Result == result);
final unitTest = DNUnitTest();
/// 运行所有测试用例
await unitTest.runAllUnitTests();
// 测试 finalizer
unitTest.addFinalizer(() {
print('The instance of \'unitTest\' has been destroyed!');
});
// 示例回调
testCallback();
}
String helloWorld() {
return interface.invokeMethodSync('hello', args: ['world']);
}
Future<T> sum<T>(T a, T b) {
return interface.invokeMethod('sum', args: [a, b]);
}
NativeByte getUTF8Data(String str) {
return interface.invokeMethodSync('getUTF8Data', args: [str]);
}
void testCallback() {
interface.invokeMethodSync('testCallback', args: [
(bool success, String result) {
if (success) {
print(result);
}
}
]);
}
Future<void> calculate() async {
final aStr = _controllerA.text;
final bStr = _controllerB.text;
String r;
if (aStr.isNotEmpty && bStr.isNotEmpty) {
int aPlusB = await sum(int.parse(aStr), int.parse(bStr));
r = aPlusB.toString();
} else {
r = helloWorld();
}
setState(() {
result = r;
});
}
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('DartNative example app'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
textAlign: TextAlign.center,
controller: _controllerA,
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
decoration: const InputDecoration(
hintText: 'Input integer A',
),
),
TextField(
textAlign: TextAlign.center,
controller: _controllerB,
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
decoration: const InputDecoration(
hintText: 'Input integer B',
),
),
Text(
result,
style: const TextStyle(fontSize: 20),
),
ElevatedButton(
onPressed: () {
calculate();
},
child: const Text('SUM'),
),
],
),
),
);
}
}
更多关于Flutter原生功能调用插件dart_native的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter原生功能调用插件dart_native的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,dart_native
是一个用于在 Flutter 中调用原生功能(如 Android 的 Java/Kotlin 代码和 iOS 的 Swift/Objective-C 代码)的插件。以下是如何在 Flutter 项目中使用 dart_native
的一个基本示例。
1. 添加依赖
首先,你需要在 pubspec.yaml
文件中添加 dart_native
的依赖:
dependencies:
flutter:
sdk: flutter
dart_native: ^最新版本号 # 请替换为实际的最新版本号
然后运行 flutter pub get
来获取依赖。
2. 配置原生代码
Android
在 android/app/src/main/java/com/yourapp/MainActivity.kt
(或对应的 Java 文件)中,添加一个原生方法:
package com.yourapp
import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant
class MainActivity: FlutterActivity() {
private val CHANNEL = "com.yourapp/channel"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == "getBatteryLevel") {
val batteryManager = applicationContext.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
val batteryLevel: Int = batteryManager.intProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
result.success(batteryLevel)
} else {
result.notImplemented()
}
}
}
}
iOS
在 ios/Runner/AppDelegate.swift
中,添加一个原生方法:
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let channel = FlutterMethodChannel(name: "com.yourapp/channel", binaryMessenger: controller.binaryMessenger)
channel.setMethodCallHandler {
(call: FlutterMethodCall, result: @escaping FlutterResult) in
if call.method == "getBatteryLevel" {
UIDevice.current.batteryLevel? { batteryLevel in
result(batteryLevel!)
}
} else {
result(FlutterMethodNotImplemented)
}
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
3. 在 Dart 中调用原生方法
在你的 Flutter Dart 代码中,你可以使用 dart_native
插件来调用上面定义的原生方法:
import 'package:flutter/material.dart';
import 'package:dart_native/dart_native.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _batteryLevel = 'Unknown battery level.';
@override
void initState() {
super.initState();
_getBatteryLevel();
}
Future<void> _getBatteryLevel() async {
try {
final NativeInterface nativeInterface = NativeInterface();
var result = await nativeInterface.invokeMethod<int>('getBatteryLevel');
setState(() {
_batteryLevel = 'Battery level at $result % .';
});
} catch (e) {
setState(() {
_batteryLevel = 'Failed to get battery level: \'$e\'.';
});
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Battery Level'),
),
body: Center(
child: Text(_batteryLevel),
),
),
);
}
}
注意事项
- 权限:在调用某些原生功能时,可能需要申请相应的权限(如电池信息、相机等)。
- 平台特定代码:确保在相应的平台(Android 和 iOS)中分别实现原生方法。
- 错误处理:在实际项目中,应添加更多的错误处理逻辑以应对各种可能的异常情况。
通过以上步骤,你可以在 Flutter 项目中成功调用原生功能。如果有更复杂的需求,可以参考 dart_native
的官方文档获取更多信息。