失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 教程:Flutter 和 Rust混合编程 使用flutter_rust_bridge自动生成ffi代码

教程:Flutter 和 Rust混合编程 使用flutter_rust_bridge自动生成ffi代码

时间:2022-10-06 03:21:03

相关推荐

教程:Flutter 和 Rust混合编程 使用flutter_rust_bridge自动生成ffi代码

实践环境:Arch Linux

flutter_rust_bridge官方文档

Flutter环境配置教程 | Rust环境配置教程

记录使用flutter_rust_bridge遇到的一些坑。

假设我们已经配置了Fluuter与Rust环境

现在直接使用flutter_rust_bridge模板创建自己的项目

运行:

git clone /Desdaemon/flutter_rust_bridge_template && cd flutter_rust_bridge_template

现在我们先让项目跑起来:

flutter run

添加新代码:

编辑 native/src/api.rs

安装代码生成器flutter_rust_bridge_codegen:

cargo install flutter_rust_bridge_codegen

按照flutter_rust_bridge文档示例运行代码生成器:

flutter_rust_bridge_codegen --rust-input native/src/api.rs \--dart-output lib/bridge_generated.dart

发现报错了:

找不到stdbool.h文件

bridge_generated.dart报错信息:

void store_dart_post_cobject(int ptr)package:flutter_rust_bridge_template/bridge_generated.dartNot to be used by normal users, but has to be public for generated codeCopied from FlutterRustBridgeWireBase.'NativeWire.store_dart_post_cobject' ('void Function(int)') isn't a valid override of 'FlutterRustBridgeWireBase.store_dart_post_cobject' ('void Function(Pointer<NativeFunction<Bool Function(Int64, Pointer<Void>)>>)').dartinvalid_overridestub.dart(21, 8): The member being overridden.

运行:

flutter_rust_bridge_codegen --rust-input native/src/api.rs \--dart-output lib/bridge_generated.dart \--c-output ios/Runner/bridge_generated.h

发现还是老样子?怎么办?

解决方案:

添加CPATH环境变量,重新运行代码生成器:

export CPATH="$(clang -v 2>&1 | grep "Selected GCC installation" | rev | cut -d' ' -f1 | rev)/include"

flutter_rust_bridge_codegen --rust-input native/src/api.rs \--dart-output lib/bridge_generated.dart \--c-output ios/Runner/bridge_generated.h

现在就正常了

打开lib/ffi.dart

看到有两处致命错误

报错信息:

package:flutter_rust_bridge_template/bridge_generated.dartThe name 'Native' is defined in the libraries 'package:flutter_rust_bridge_template/bridge_definitions.dart' and 'package:flutter_rust_bridge_template/bridge_generated.dart'.Try removing the export of one of the libraries, or explicitly hiding the name in one of the export directives.

[abstract class Native, abstract class Native]The name 'Native' is defined in the libraries 'package:flutter_rust_bridge_template/bridge_definitions.dart' and 'package:flutter_rust_bridge_template/bridge_generated.dart'.Try using 'as prefix' for one of the import directives, or hiding the name from all but one of the imports.

解决方案:

去掉import 'bridge_definitions.dart'; 和 export 'bridge_definitions.dart';

lib/ffi.dart原代码:

// This file initializes the dynamic library and connects it with the stub// generated by flutter_rust_bridge_codegen.import 'dart:ffi';import 'bridge_generated.dart';import 'bridge_definitions.dart';export 'bridge_definitions.dart';// Re-export the bridge so it is only necessary to import this file.export 'bridge_generated.dart';import 'dart:io' as io;const _base = 'native';// On MacOS, the dynamic library is not bundled with the binary,// but rather directly **linked** against the binary.final _dylib = io.Platform.isWindows ? '$_base.dll' : 'lib$_base.so';final Native api = NativeImpl(io.Platform.isIOS || io.Platform.isMacOS? DynamicLibrary.executable(): DynamicLibrary.open(_dylib));

lib/ffi.dart修改后的代码:

// This file initializes the dynamic library and connects it with the stub// generated by flutter_rust_bridge_codegen.import 'dart:ffi';import 'bridge_generated.dart';// Re-export the bridge so it is only necessary to import this file.export 'bridge_generated.dart';import 'dart:io' as io;const _base = 'native';// On MacOS, the dynamic library is not bundled with the binary,// but rather directly **linked** against the binary.final _dylib = io.Platform.isWindows ? '$_base.dll' : 'lib$_base.so';final Native api = NativeImpl(io.Platform.isIOS || io.Platform.isMacOS? DynamicLibrary.executable(): DynamicLibrary.open(_dylib));

一切正常:

在main.dart调用rust函数

main.dart完整代码:

import 'package:flutter/material.dart';import 'ffi.dart' if (dart.library.html) 'ffi_web.dart';void main() {runApp(const MyApp());}class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);// This widget is the root of your application.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(// This is the theme of your application.//// Try running your application with "flutter run". You'll see the// application has a blue toolbar. Then, without quitting the app, try// changing the primarySwatch below to Colors.green and then invoke// "hot reload" (press "r" in the console where you ran "flutter run",// or simply save your changes to "hot reload" in a Flutter IDE).// Notice that the counter didn't reset back to zero; the application// is not restarted.primarySwatch: Colors.blue,),home: const MyHomePage(title: 'Flutter Demo Home Page'),);}}class MyHomePage extends StatefulWidget {const MyHomePage({Key? key, required this.title}) : super(key: key);// This widget is the home page of your application. It is stateful, meaning// that it has a State object (defined below) that contains fields that affect// how it looks.// This class is the configuration for the state. It holds the values (in this// case the title) provided by the parent (in this case the App widget) and// used by the build method of the State. Fields in a Widget subclass are// always marked "final".final String title;@overrideState<MyHomePage> createState() => _MyHomePageState();}class _MyHomePageState extends State<MyHomePage> {// These futures belong to the state and are only initialized once,// in the initState method.late Future<Platform> platform;late Future<bool> isRelease;late Future<String> test;@overridevoid initState() {super.initState();platform = api.platform();isRelease = api.rustReleaseMode();test = api.test();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text(widget.title),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[const Text("You're running on"),FutureBuilder<List<dynamic>>(future: Future.wait([test]),builder: (context, snap) {final data = snap.data;if (data == null) {return const Text("Loading");}return Text('${data[0]}');}),// To render the results of a Future, a FutureBuilder is used which// turns a Future into an AsyncSnapshot, which can be used to// extract the error state, the loading state and the data if// available.//// Here, the generic type that the FutureBuilder manages is// explicitly named, because if omitted the snapshot will have the// type of AsyncSnapshot<Object?>.FutureBuilder<List<dynamic>>(// We await two unrelated futures here, so the type has to be// List<dynamic>.future: Future.wait([platform, isRelease]),builder: (context, snap) {final style = Theme.of(context).textTheme.headline4;if (snap.error != null) {// An error has been encountered, so give an appropriate response and// pass the error details to an unobstructive tooltip.debugPrint(snap.error.toString());return Tooltip(message: snap.error.toString(),child: Text('Unknown OS', style: style),);}// Guard return here, the data is not ready yet.final data = snap.data;if (data == null) return const CircularProgressIndicator();// Finally, retrieve the data expected in the same order provided// to the FutureBuilder.future.final Platform platform = data[0];final release = data[1] ? 'Release' : 'Debug';final text = const {Platform.Android: 'Android',Platform.Ios: 'iOS',Platform.MacApple: 'MacOS with Apple Silicon',Platform.MacIntel: 'MacOS',Platform.Windows: 'Windows',Platform.Unix: 'Unix',Platform.Wasm: 'the Web',}[platform] ??'Unknown OS';return Text('$text ($release)', style: style);},)],),),);}}

运行项目:

flutter run

如果觉得《教程:Flutter 和 Rust混合编程 使用flutter_rust_bridge自动生成ffi代码》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。