当前位置:   article > 正文

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

flutter rust bridge

实践环境:Arch Linux

flutter_rust_bridge官方文档

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

记录使用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文档示例运行代码生成器:

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

发现报错了:

找不到stdbool.h文件

bridge_generated.dart报错信息:

  1. void store_dart_post_cobject(int ptr)
  2. package:flutter_rust_bridge_template/bridge_generated.dart
  3. Not to be used by normal users, but has to be public for generated code
  4. Copied from FlutterRustBridgeWireBase.
  5. '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
  6. stub.dart(21, 8): The member being overridden.

运行:

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

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

解决方案:

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

export CPATH="$(clang -v 2>&1 | grep "Selected GCC installation" | rev | cut -d' ' -f1 | rev)/include"
  1. flutter_rust_bridge_codegen --rust-input native/src/api.rs \
  2. --dart-output lib/bridge_generated.dart \
  3. --c-output ios/Runner/bridge_generated.h

现在就正常了

打开lib/ffi.dart

看到有两处致命错误

报错信息:

  1. package:flutter_rust_bridge_template/bridge_generated.dart
  2. 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'.
  3. Try removing the export of one of the libraries, or explicitly hiding the name in one of the export directives.
  1. [abstract class Native, abstract class Native]
  2. 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'.
  3. 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原代码:

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

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

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

一切正常:

main.dart调用rust函数

main.dart完整代码:

  1. import 'package:flutter/material.dart';
  2. import 'ffi.dart' if (dart.library.html) 'ffi_web.dart';
  3. void main() {
  4. runApp(const MyApp());
  5. }
  6. class MyApp extends StatelessWidget {
  7. const MyApp({Key? key}) : super(key: key);
  8. // This widget is the root of your application.
  9. @override
  10. Widget build(BuildContext context) {
  11. return MaterialApp(
  12. title: 'Flutter Demo',
  13. theme: ThemeData(
  14. // This is the theme of your application.
  15. //
  16. // Try running your application with "flutter run". You'll see the
  17. // application has a blue toolbar. Then, without quitting the app, try
  18. // changing the primarySwatch below to Colors.green and then invoke
  19. // "hot reload" (press "r" in the console where you ran "flutter run",
  20. // or simply save your changes to "hot reload" in a Flutter IDE).
  21. // Notice that the counter didn't reset back to zero; the application
  22. // is not restarted.
  23. primarySwatch: Colors.blue,
  24. ),
  25. home: const MyHomePage(title: 'Flutter Demo Home Page'),
  26. );
  27. }
  28. }
  29. class MyHomePage extends StatefulWidget {
  30. const MyHomePage({Key? key, required this.title}) : super(key: key);
  31. // This widget is the home page of your application. It is stateful, meaning
  32. // that it has a State object (defined below) that contains fields that affect
  33. // how it looks.
  34. // This class is the configuration for the state. It holds the values (in this
  35. // case the title) provided by the parent (in this case the App widget) and
  36. // used by the build method of the State. Fields in a Widget subclass are
  37. // always marked "final".
  38. final String title;
  39. @override
  40. State<MyHomePage> createState() => _MyHomePageState();
  41. }
  42. class _MyHomePageState extends State<MyHomePage> {
  43. // These futures belong to the state and are only initialized once,
  44. // in the initState method.
  45. late Future<Platform> platform;
  46. late Future<bool> isRelease;
  47. late Future<String> test;
  48. @override
  49. void initState() {
  50. super.initState();
  51. platform = api.platform();
  52. isRelease = api.rustReleaseMode();
  53. test = api.test();
  54. }
  55. @override
  56. Widget build(BuildContext context) {
  57. return Scaffold(
  58. appBar: AppBar(
  59. title: Text(widget.title),
  60. ),
  61. body: Center(
  62. child: Column(
  63. mainAxisAlignment: MainAxisAlignment.center,
  64. children: <Widget>[
  65. const Text("You're running on"),
  66. FutureBuilder<List<dynamic>>(
  67. future: Future.wait([test]),
  68. builder: (context, snap) {
  69. final data = snap.data;
  70. if (data == null) {
  71. return const Text("Loading");
  72. }
  73. return Text('${data[0]}');
  74. }),
  75. // To render the results of a Future, a FutureBuilder is used which
  76. // turns a Future into an AsyncSnapshot, which can be used to
  77. // extract the error state, the loading state and the data if
  78. // available.
  79. //
  80. // Here, the generic type that the FutureBuilder manages is
  81. // explicitly named, because if omitted the snapshot will have the
  82. // type of AsyncSnapshot<Object?>.
  83. FutureBuilder<List<dynamic>>(
  84. // We await two unrelated futures here, so the type has to be
  85. // List<dynamic>.
  86. future: Future.wait([platform, isRelease]),
  87. builder: (context, snap) {
  88. final style = Theme.of(context).textTheme.headline4;
  89. if (snap.error != null) {
  90. // An error has been encountered, so give an appropriate response and
  91. // pass the error details to an unobstructive tooltip.
  92. debugPrint(snap.error.toString());
  93. return Tooltip(
  94. message: snap.error.toString(),
  95. child: Text('Unknown OS', style: style),
  96. );
  97. }
  98. // Guard return here, the data is not ready yet.
  99. final data = snap.data;
  100. if (data == null) return const CircularProgressIndicator();
  101. // Finally, retrieve the data expected in the same order provided
  102. // to the FutureBuilder.future.
  103. final Platform platform = data[0];
  104. final release = data[1] ? 'Release' : 'Debug';
  105. final text = const {
  106. Platform.Android: 'Android',
  107. Platform.Ios: 'iOS',
  108. Platform.MacApple: 'MacOS with Apple Silicon',
  109. Platform.MacIntel: 'MacOS',
  110. Platform.Windows: 'Windows',
  111. Platform.Unix: 'Unix',
  112. Platform.Wasm: 'the Web',
  113. }[platform] ??
  114. 'Unknown OS';
  115. return Text('$text ($release)', style: style);
  116. },
  117. )
  118. ],
  119. ),
  120. ),
  121. );
  122. }
  123. }

运行项目:

flutter run

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/121342
推荐阅读
相关标签
  

闽ICP备14008679号