赞
踩
实践环境:Arch Linux
记录使用flutter_rust_bridge遇到的一些坑。
假设我们已经配置了Fluuter与Rust环境
现在直接使用flutter_rust_bridge模板创建自己的项目
运行:
git clone https://github.com/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.dart
-
- Not to be used by normal users, but has to be public for generated code
-
- Copied 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_override
- stub.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.dart
-
- 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 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.
- @override
- Widget 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;
-
- @override
- State<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;
-
- @override
- void initState() {
- super.initState();
- platform = api.platform();
- isRelease = api.rustReleaseMode();
-
- test = api.test();
- }
-
- @override
- Widget 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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。