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

news2024/9/30 5:36:13

实践环境:Windows11

flutter_rust_bridge官方文档

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

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

flutter create example && cd example && flutter run

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

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

[lib]
crate-type = ["staticlib", "cdylib"]


[dependencies]
flutter_rust_bridge = "1.78.0"

[build-dependencies]
flutter_rust_bridge_codegen = "1.78.0"

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

添加以下示例代码:

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

   lib.rs

//lib.rs
mod api;

安装flutter_rust_bridge_codegen

cargo install flutter_rust_bridge_codegen

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

 pubspec.yaml 配置文件完整代码:

name: mobile
description: A new Flutter project.
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: "none" # Remove this line if you wish to publish to pub.dev

# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1

environment:
  sdk: ">=3.0.5 <4.0.0"

# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
  flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.2
  flutter_rust_bridge: 1.77.1
  ffi: ^2.0.2

dev_dependencies:
  flutter_test:
    sdk: flutter

  # The "flutter_lints" package below contains a set of recommended lints to
  # encourage good coding practices. The lint set provided by the package is
  # activated in the `analysis_options.yaml` file located at the root of your
  # package. See that file for information about deactivating specific lint
  # rules and activating additional ones.
  flutter_lints: ^2.0.0
  ffigen: ^8.0.2
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

# The following section is specific to Flutter packages.
flutter:
  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  # assets:
  #   - images/a_dot_burr.jpeg
  #   - images/a_dot_ham.jpeg

  # An image asset can refer to one or more resolution-specific "variants", see
  # https://flutter.dev/assets-and-images/#resolution-aware

  # For details regarding adding assets from package dependencies, see
  # https://flutter.dev/assets-and-images/#from-packages

  # To add custom fonts to your application, add a fonts section here,
  # in this "flutter" section. Each entry in this list should have a
  # "family" key with the font family name, and a "fonts" key with a
  # list giving the asset and other descriptors for the font. For
  # example:
  # fonts:
  #   - family: Schyler
  #     fonts:
  #       - asset: fonts/Schyler-Regular.ttf
  #       - asset: fonts/Schyler-Italic.ttf
  #         style: italic
  #   - family: Trajan Pro
  #     fonts:
  #       - asset: fonts/TrajanPro.ttf
  #       - asset: fonts/TrajanPro_Bold.ttf
  #         weight: 700
  #
  # For details regarding fonts from package dependencies,
  # 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 最底部插入以下代码:

[
    new Tuple2('Debug', ''),
    new Tuple2('Profile', '--release'),
    new Tuple2('Release', '--release')
].each {
    def taskPostfix = it.first
    def profileMode = it.second
    tasks.whenTaskAdded { task ->
        if (task.name == "javaPreCompile$taskPostfix") {
            task.dependsOn "cargoBuild$taskPostfix"
        }
    }
    tasks.register("cargoBuild$taskPostfix", Exec) {
        // Until https://github.com/bbqsrc/cargo-ndk/pull/13 is merged,
        // this workaround is necessary.

        def ndk_command = """cargo ndk \
            -t armeabi-v7a -t arm64-v8a -t x86_64 -t x86 \
            -o ../android/app/src/main/jniLibs build $profileMode"""

        workingDir "../../native" //native是rust项目目录名称
        environment "ANDROID_NDK_HOME", "D:\\SDK\\Android\\Sdk\\ndk\\25.2.9519653" //这里填写ndk安装路径
        if (org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem.isWindows()) {
            commandLine 'cmd', '/C', ndk_command
        } else {
            commandLine 'sh', '-c', ndk_command
        }
    }
}

 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

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

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

复制Rust项目里的jniLibs目录

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

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

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

 找到_MyHomePageState,加入以下代码

 late Future<String> hello;

  @override
  void initState() {
    super.initState();
    hello = NativeImpl(DynamicLibrary.open('libnative.so')).hello();
  }

 

 调用:

            FutureBuilder<List<dynamic>>(
              future: Future.wait([hello]),
              builder: (context, snap) {
                final data = snap.data;

                if (data == null) return const CircularProgressIndicator();

                // ignore: non_constant_identifier_names
                final Platform = data[0];

                return Text('$Platform');
              },
            )

 

 main.dart 完整代码:

import 'package:flutter/material.dart';
import 'dart:ffi';
import 'ffi/rust_ffi.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.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 THIS: Try running your application with "flutter run". You'll see
        // the application has a blue toolbar. Then, without quitting the app,
        // try changing the seedColor in the colorScheme below to Colors.green
        // and then invoke "hot reload" (save your changes or press the "hot
        // reload" button in a Flutter-supported IDE, or press "r" if you used
        // the command line to start the app).
        //
        // Notice that the counter didn't reset back to zero; the application
        // state is not lost during the reload. To reset the state, use hot
        // restart instead.
        //
        // This works for code too, not just values: Most code changes can be
        // tested with just a hot reload.
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  // 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> {
  int _counter = 0;

  late Future<String> hello;

  @override
  void initState() {
    super.initState();
    hello = NativeImpl(DynamicLibrary.open('libnative.so')).hello();
  }

  void _incrementCounter() {
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Scaffold(
      appBar: AppBar(
        // TRY THIS: Try changing the color here to a specific color (to
        // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
        // change color while the other colors stay the same.
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Column(
          // Column is also a layout widget. It takes a list of children and
          // arranges them vertically. By default, it sizes itself to fit its
          // children horizontally, and tries to be as tall as its parent.
          //
          // Column has various properties to control how it sizes itself and
          // how it positions its children. Here we use mainAxisAlignment to
          // center the children vertically; the main axis here is the vertical
          // axis because Columns are vertical (the cross axis would be
          // horizontal).
          //
          // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
          // action in the IDE, or press "p" in the console), to see the
          // wireframe for each widget.
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
            FutureBuilder<List<dynamic>>(
              future: Future.wait([hello]),
              builder: (context, snap) {
                final data = snap.data;

                if (data == null) return const CircularProgressIndicator();

                // ignore: non_constant_identifier_names
                final Platform = data[0];

                return Text('$Platform');
              },
            )
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

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

flutter run

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/687495.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从0到1精通自动化,接口自动化测试——数据驱动DDT实战

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 DDT简介 名称&am…

FPGA解码 MIPI 视频 OV4689采集 4line 2.7K分辨率 提供工程源码和技术支持

目录 1、前言2、Xilinx官方主推的MIPI解码方案3、本 MIPI CSI2 模块性能及其优越性4、我这里已有的 MIPI 编解码方案5、vivado工程介绍5、上板调试验证6、福利&#xff1a;工程代码的获取 1、前言 FPGA图像采集领域目前协议最复杂、技术难度最高的应该就是MIPI协议了&#xff…

python spider 爬虫 之 解析 xpath 、jsonpath、BeautifulSoup (三)

BeautifulSoup 简称&#xff1a;bs4 BeautifulSoup跟lxml 一样&#xff0c;是一个html文档的解析器&#xff0c;主要功能也是解析和提取数据 优缺点 缺点&#xff1a;效率没有lxml的效率高 优点&#xff1a;接口接口人性化&#xff0c;使用方便 延用了css选择器 安装Beautifu…

故障处理程序框图原理

一、故障处理程序框图 故障处理程序包括保护软压板的投切检查、保护定值比较、保护逻辑判断、跳闸处理程序和后加速部分。故障处理程序框图如图2&#xff0d;8所示。保护逻辑判断程序将在第三章中详述。 进入故障处理程序入口&#xff0c;首先置标志位KST为1&#xff0c;驱动起…

传统表格还是思维导图?哪种基本功能测试用例模式更好?

这个问题先抛出我的观点&#xff1a; 具体选择哪种形式更好&#xff0c;需要根据具体情况来考虑。 如果测试用例较为简单&#xff0c;可以选择表格形式&#xff1b;如果测试用例较为复杂&#xff0c;可以选择思维导图形式。但实际工作中&#xff0c;二者一般是结合使用的。 …

php正则匹配

一、基础内容 1、通用原子 2、元字符 符号意义.除了换行以外的所有字符*匹配前面的内容出现 0 次及以上?匹配前面的内容出现 0 次或 1 次出现一次或多次$必须以它结尾{n}恰巧出现 n 次{n,}大于等于 n 次{n,m}大于等于n,小于等于 m[]是一个集合&#xff0c;匹配中括号中的任…

实时数据管理与生产控制:MES系统的作用和优势解析

一、什么是MES系统&#xff1f; MES系统&#xff0c;全称为制造执行系统&#xff08;Manufacturing Execution System&#xff09;&#xff0c;是一种用于管理和监控制造过程的信息系统。它通过实时收集、分析和共享生产数据&#xff0c;提供全面的生产计划、调度、追踪和报告…

jenkins选择不同构建环境

1、业务在有些情况下需要选择不同的环境来构建服务&#xff0c;使用同一套代码读取不同的配置 2、jenkins使用如下配置即可实现构建环境的选择 2.1、配置构建选项 2.2、配置构建tag 2.3、选择构建时间参数 3、使用如下pipeline实现jenkins构建环境选择 pipeline {agent any…

黑马程序员前端 Vue3 小兔鲜电商项目——(十二)会员中心

文章目录 路由配置模板代码会员中心个人信息用户订单 配置路由 个人中心信息渲染使用 Pinia 数据渲染个人信息 猜你喜欢封装接口渲染数据 我的订单基础列表渲染tab切换实现分页实现 细节优化默认三级路由设置订单状态显示适配 路由配置 模板代码 会员中心 创建 src\views\Me…

XR云新未来圆桌精彩回顾 | XR应用场景迭代下的新商业模式

6月15日&#xff0c;由平行云联合首都在线共同主办&#xff0c;中关村软件园协办&#xff0c;以“XR云新未来|弹性算力赋能可交互、沉浸式商业实践”为主题的XR行业交流盛会在北京成功举办。 本次会议我们邀请到平行云科技创始人兼CEO 李岩、XREAL 云XR负责人 吴维、瑞帆科技…

吃透JAVA的Stream流操作,多年实践总结

在JAVA中&#xff0c;涉及到对数组、Collection等集合类中的元素进行操作的时候&#xff0c;通常会通过循环的方式进行逐个处理&#xff0c;或者使用Stream的方式进行处理。 例如&#xff0c;现在有这么一个需求&#xff1a; 从给定句子中返回单词长度大于5的单词列表&#xf…

Java应用在线debug--bistoury介绍

Bistoury介绍 Bistoury 是去哪儿网开源的一个对应用透明&#xff0c;无侵入的java应用诊断工具&#xff0c;用于提升开发人员的诊断效率和能力&#xff0c;可以让开发人员无需登录机器或修改系统&#xff0c;就可以从日志、内存、线程、类信息、调试、机器和系统属性等各个方面…

技术管理第三板斧招聘与解聘-“能落地

1.既要帮&#xff0c;也要严 “既要帮&#xff0c;也要严”是我定义的“能落地”的核心原则&#xff0c;“帮”与“严”是双向要求&#xff1a;帮是指帮助新同学融入团队&#xff08;针对的是师兄和 Leader&#xff09;&#xff1b;严是要让新同学在团队中提升自己&#xff0c…

火爆全网,接口测试总结汇总,全知识点扫描卷起来...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 第一部分&#xf…

JavaSE-11 【内部类】

文章目录 JavaSE-11 【内部类】第一章 成员内部类和局部内部类1.1 四种权限修饰符1.2 内部类的概念和分类1.3 成员内部类的定义格式1.4 成员内部类的使用1.5 内部类的同名变量访问1.6 局部内部类定义1.7 局部内部类的final问题 第二章 匿名内部类2.1 匿名内部类2.2 匿名内部类的…

Sangfor华东天勇战队:shiro注入filter内存马

注入步骤 https://github.com/yyhuni/shiroMemshell&#xff08;实验环境&#xff09; 这里用的 pom.xml加入 <dependency><groupId>org.javassist</groupId><artifactId>javassist</artifactId><version>3.28.0-GA</version> <…

阿里企业邮箱域名解析MX记录值设置

阿里企业邮箱配置需要为域名添加MX解析记录&#xff0c;不只是MX域名解析记录值&#xff0c;还需要为域名添加pop3、imap、smtp及mail等CNAME解析类型&#xff0c;阿里云百科分享阿里云企业邮箱域名MX解析记录类型、记录值及服务器地址&#xff1a; 目录 新版阿里企业邮箱域名…

FPGA解码 4K MIPI 视频自定义IP版 纯vhdl实现 CSI2 RX 采集OV13850 提供工程源码和技术支持

目录 1、前言2、Xilinx官方主推的MIPI解码方案3、本 MIPI CSI2 模块性能及其优越性4、我这里已有的 MIPI 编解码方案5、vivado工程介绍6、上板调试验证7、福利&#xff1a;工程代码的获取 1、前言 FPGA图像采集领域目前协议最复杂、技术难度最高的应该就是MIPI协议了&#xff…

div转data:image/svg编码图片

前言 将div转base64图片 <!DOCTYPE html> <html><head><meta charset"UTF-8"><title>div-svg-base64</title><script type"text/javascript" src"/js/jquery-3.2.1.min.js"></script><scr…

使用 Jetpack Compose 的 TextButton 组件

Jetpack Compose 是 Google 推出的一种声明式 UI 框架&#xff0c;它使 Android UI 开发变得更加简单和直观。在本篇博客中&#xff0c;我们将深入探索 Jetpack Compose 中的 TextButton 组件。 一、TextButton的使用 二、自定义TextButton 三、Button和TextButton的区别 一…