当前位置:   article > 正文

【教程】Flutter与Rust完美交互,无需手写FFI代码_rust flutter

rust flutter

实践环境:Windows11

flutter_rust_bridge官方文档

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

新建一个全新的Flutter项目并运行:

flutter create example && cd example && flutter run

在Flutter项目根目录新建一个Rust项目:
cargo new native --lib
目录结构大概是这样的:

接下来,将这两行添加到 Cargo.toml

  1. [lib]
  2. crate-type = ["staticlib", "cdylib"]
  3. [dependencies]
  4. flutter_rust_bridge = "1.78.0"
  5. [build-dependencies]
  6. flutter_rust_bridge_codegen = "1.78.0"

 在native/src目录新建一个api.rs

添加以下示例代码:

  1. //api.rs
  2. pub fn hello() -> String {
  3. format!("{}", "你好,Rust!")
  4. }

   lib.rs

  1. //lib.rs
  2. mod api;

安装flutter_rust_bridge_codegen

cargo install flutter_rust_bridge_codegen

修改Flutter项目的pubspec.yaml配置文件

 pubspec.yaml 配置文件完整代码:
  1. name: mobile
  2. description: A new Flutter project.
  3. # The following line prevents the package from being accidentally published to
  4. # pub.dev using `flutter pub publish`. This is preferred for private packages.
  5. publish_to: "none" # Remove this line if you wish to publish to pub.dev
  6. # The following defines the version and build number for your application.
  7. # A version number is three numbers separated by dots, like 1.2.43
  8. # followed by an optional build number separated by a +.
  9. # Both the version and the builder number may be overridden in flutter
  10. # build by specifying --build-name and --build-number, respectively.
  11. # In Android, build-name is used as versionName while build-number used as versionCode.
  12. # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
  13. # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
  14. # Read more about iOS versioning at
  15. # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
  16. # In Windows, build-name is used as the major, minor, and patch parts
  17. # of the product and file versions while build-number is used as the build suffix.
  18. version: 1.0.0+1
  19. environment:
  20. sdk: ">=3.0.5 <4.0.0"
  21. # Dependencies specify other packages that your package needs in order to work.
  22. # To automatically upgrade your package dependencies to the latest versions
  23. # consider running `flutter pub upgrade --major-versions`. Alternatively,
  24. # dependencies can be manually updated by changing the version numbers below to
  25. # the latest version available on pub.dev. To see which dependencies have newer
  26. # versions available, run `flutter pub outdated`.
  27. dependencies:
  28. flutter:
  29. sdk: flutter
  30. # The following adds the Cupertino Icons font to your application.
  31. # Use with the CupertinoIcons class for iOS style icons.
  32. cupertino_icons: ^1.0.2
  33. flutter_rust_bridge: 1.77.1
  34. ffi: ^2.0.2
  35. dev_dependencies:
  36. flutter_test:
  37. sdk: flutter
  38. # The "flutter_lints" package below contains a set of recommended lints to
  39. # encourage good coding practices. The lint set provided by the package is
  40. # activated in the `analysis_options.yaml` file located at the root of your
  41. # package. See that file for information about deactivating specific lint
  42. # rules and activating additional ones.
  43. flutter_lints: ^2.0.0
  44. ffigen: ^8.0.2
  45. # For information on the generic Dart part of this file, see the
  46. # following page: https://dart.dev/tools/pub/pubspec
  47. # The following section is specific to Flutter packages.
  48. flutter:
  49. # The following line ensures that the Material Icons font is
  50. # included with your application, so that you can use the icons in
  51. # the material Icons class.
  52. uses-material-design: true
  53. # To add assets to your application, add an assets section, like this:
  54. # assets:
  55. # - images/a_dot_burr.jpeg
  56. # - images/a_dot_ham.jpeg
  57. # An image asset can refer to one or more resolution-specific "variants", see
  58. # https://flutter.dev/assets-and-images/#resolution-aware
  59. # For details regarding adding assets from package dependencies, see
  60. # https://flutter.dev/assets-and-images/#from-packages
  61. # To add custom fonts to your application, add a fonts section here,
  62. # in this "flutter" section. Each entry in this list should have a
  63. # "family" key with the font family name, and a "fonts" key with a
  64. # list giving the asset and other descriptors for the font. For
  65. # example:
  66. # fonts:
  67. # - family: Schyler
  68. # fonts:
  69. # - asset: fonts/Schyler-Regular.ttf
  70. # - asset: fonts/Schyler-Italic.ttf
  71. # style: italic
  72. # - family: Trajan Pro
  73. # fonts:
  74. # - asset: fonts/TrajanPro.ttf
  75. # - asset: fonts/TrajanPro_Bold.ttf
  76. # weight: 700
  77. #
  78. # For details regarding fonts from package dependencies,
  79. # see https://flutter.dev/custom-fonts/#from-packages
运行:
 flutter pub get

安装 LLVM (不安装使用生成器的时候会报错):

ubuntu/linux:
sudo apt-get install libclang-dev
Windows:
  1. 安装具有 C++ 开发支持的 Visual Studio。
  2. 安装 LLVM 或使用命令:winget install -e --id LLVM.LLVM
MacOS:
  1. Install Xcode.
  2. Install LLVM - brew install llvm.

切换到flutter项目根目录,运行以下命令,使用flutter_rust_bridge生成器生成代码:

flutter_rust_bridge_codegen -r native/src/api.rs -d lib/ffi/rust_ffi.dart -c ios/Runner/bridge_generated.h

 现在我们可以看到多了这些文件:

接下来在 android/app/build.gradle 最底部插入以下代码:

  1. [
  2. new Tuple2('Debug', ''),
  3. new Tuple2('Profile', '--release'),
  4. new Tuple2('Release', '--release')
  5. ].each {
  6. def taskPostfix = it.first
  7. def profileMode = it.second
  8. tasks.whenTaskAdded { task ->
  9. if (task.name == "javaPreCompile$taskPostfix") {
  10. task.dependsOn "cargoBuild$taskPostfix"
  11. }
  12. }
  13. tasks.register("cargoBuild$taskPostfix", Exec) {
  14. // Until https://github.com/bbqsrc/cargo-ndk/pull/13 is merged,
  15. // this workaround is necessary.
  16. def ndk_command = """cargo ndk \
  17. -t armeabi-v7a -t arm64-v8a -t x86_64 -t x86 \
  18. -o ../android/app/src/main/jniLibs build $profileMode"""
  19. workingDir "../../native" //native是rust项目目录名称
  20. environment "ANDROID_NDK_HOME", "D:\\SDK\\Android\\Sdk\\ndk\\25.2.9519653" //这里填写ndk安装路径
  21. if (org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem.isWindows()) {
  22. commandLine 'cmd', '/C', ndk_command
  23. } else {
  24. commandLine 'sh', '-c', ndk_command
  25. }
  26. }
  27. }

 Android设置:

运行以下代码安装cargo-ndk

cargo install cargo-ndk

 添加工具链:

rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android i686-linux-android

我这里已经添加过了

进入Rust项目的根目录:

cd native

运行以下命令进行交叉编译:

  1. cargo ndk -t armeabi-v7a -o ./jniLibs build --release
  2. cargo ndk -t arm64-v8a -o ./jniLibs build --release
  3. cargo ndk -t x86_64 -o ./jniLibs build --release
  4. cargo ndk -t x86 -o ./jniLibs build --release

复制Rust项目里的jniLibs目录

 粘贴到flutter 项目 android/app/main目录

 编辑flutter项目lib/main.dart文件

  1. import 'dart:ffi';
  2. import 'ffi/rust_ffi.dart';

 找到_MyHomePageState,加入以下代码

  1. late Future<String> hello;
  2. @override
  3. void initState() {
  4. super.initState();
  5. hello = NativeImpl(DynamicLibrary.open('libnative.so')).hello();
  6. }

 

 调用:

  1. FutureBuilder<List<dynamic>>(
  2. future: Future.wait([hello]),
  3. builder: (context, snap) {
  4. final data = snap.data;
  5. if (data == null) return const CircularProgressIndicator();
  6. // ignore: non_constant_identifier_names
  7. final Platform = data[0];
  8. return Text('$Platform');
  9. },
  10. )

 

 main.dart 完整代码:

  1. import 'package:flutter/material.dart';
  2. import 'dart:ffi';
  3. import 'ffi/rust_ffi.dart';
  4. void main() {
  5. runApp(const MyApp());
  6. }
  7. class MyApp extends StatelessWidget {
  8. const MyApp({super.key});
  9. // This widget is the root of your application.
  10. @override
  11. Widget build(BuildContext context) {
  12. return MaterialApp(
  13. title: 'Flutter Demo',
  14. theme: ThemeData(
  15. // This is the theme of your application.
  16. //
  17. // TRY THIS: Try running your application with "flutter run". You'll see
  18. // the application has a blue toolbar. Then, without quitting the app,
  19. // try changing the seedColor in the colorScheme below to Colors.green
  20. // and then invoke "hot reload" (save your changes or press the "hot
  21. // reload" button in a Flutter-supported IDE, or press "r" if you used
  22. // the command line to start the app).
  23. //
  24. // Notice that the counter didn't reset back to zero; the application
  25. // state is not lost during the reload. To reset the state, use hot
  26. // restart instead.
  27. //
  28. // This works for code too, not just values: Most code changes can be
  29. // tested with just a hot reload.
  30. colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
  31. useMaterial3: true,
  32. ),
  33. home: const MyHomePage(title: 'Flutter Demo Home Page'),
  34. );
  35. }
  36. }
  37. class MyHomePage extends StatefulWidget {
  38. const MyHomePage({super.key, required this.title});
  39. // This widget is the home page of your application. It is stateful, meaning
  40. // that it has a State object (defined below) that contains fields that affect
  41. // how it looks.
  42. // This class is the configuration for the state. It holds the values (in this
  43. // case the title) provided by the parent (in this case the App widget) and
  44. // used by the build method of the State. Fields in a Widget subclass are
  45. // always marked "final".
  46. final String title;
  47. @override
  48. State<MyHomePage> createState() => _MyHomePageState();
  49. }
  50. class _MyHomePageState extends State<MyHomePage> {
  51. int _counter = 0;
  52. late Future<String> hello;
  53. @override
  54. void initState() {
  55. super.initState();
  56. hello = NativeImpl(DynamicLibrary.open('libnative.so')).hello();
  57. }
  58. void _incrementCounter() {
  59. setState(() {
  60. // This call to setState tells the Flutter framework that something has
  61. // changed in this State, which causes it to rerun the build method below
  62. // so that the display can reflect the updated values. If we changed
  63. // _counter without calling setState(), then the build method would not be
  64. // called again, and so nothing would appear to happen.
  65. _counter++;
  66. });
  67. }
  68. @override
  69. Widget build(BuildContext context) {
  70. // This method is rerun every time setState is called, for instance as done
  71. // by the _incrementCounter method above.
  72. //
  73. // The Flutter framework has been optimized to make rerunning build methods
  74. // fast, so that you can just rebuild anything that needs updating rather
  75. // than having to individually change instances of widgets.
  76. return Scaffold(
  77. appBar: AppBar(
  78. // TRY THIS: Try changing the color here to a specific color (to
  79. // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
  80. // change color while the other colors stay the same.
  81. backgroundColor: Theme.of(context).colorScheme.inversePrimary,
  82. // Here we take the value from the MyHomePage object that was created by
  83. // the App.build method, and use it to set our appbar title.
  84. title: Text(widget.title),
  85. ),
  86. body: Center(
  87. // Center is a layout widget. It takes a single child and positions it
  88. // in the middle of the parent.
  89. child: Column(
  90. // Column is also a layout widget. It takes a list of children and
  91. // arranges them vertically. By default, it sizes itself to fit its
  92. // children horizontally, and tries to be as tall as its parent.
  93. //
  94. // Column has various properties to control how it sizes itself and
  95. // how it positions its children. Here we use mainAxisAlignment to
  96. // center the children vertically; the main axis here is the vertical
  97. // axis because Columns are vertical (the cross axis would be
  98. // horizontal).
  99. //
  100. // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
  101. // action in the IDE, or press "p" in the console), to see the
  102. // wireframe for each widget.
  103. mainAxisAlignment: MainAxisAlignment.center,
  104. children: <Widget>[
  105. const Text(
  106. 'You have pushed the button this many times:',
  107. ),
  108. Text(
  109. '$_counter',
  110. style: Theme.of(context).textTheme.headlineMedium,
  111. ),
  112. FutureBuilder<List<dynamic>>(
  113. future: Future.wait([hello]),
  114. builder: (context, snap) {
  115. final data = snap.data;
  116. if (data == null) return const CircularProgressIndicator();
  117. // ignore: non_constant_identifier_names
  118. final Platform = data[0];
  119. return Text('$Platform');
  120. },
  121. )
  122. ],
  123. ),
  124. ),
  125. floatingActionButton: FloatingActionButton(
  126. onPressed: _incrementCounter,
  127. tooltip: 'Increment',
  128. child: const Icon(Icons.add),
  129. ), // This trailing comma makes auto-formatting nicer for build methods.
  130. );
  131. }
  132. }

  大功告成,现在重新运行项目:

flutter run

 

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

闽ICP备14008679号