使用Flutter Rust Bridge进行跨平台开发
跨平台开发让创建者能够建立强大的应用程序集合,这些应用程序可以在各种操作系统上运行,只需使用一个单一的代码库。这有助于节省时间和资源,因为它消除了为不同平台编写独立代码的需要。本文将解释如何使用Flutter Rust Bridge包来简化跨平台开发。
用于跨平台开发的最突出框架是Flutter,因为它使用Dart编程语言从单一源代码开发编译的移动应用程序以及Web和桌面应用程序。除了Flutter之外,Rust因其性能和安全性而被认可,特别是在系统编程和需要并发执行的应用程序中。您可以通过使用Rust来改善Flutter应用程序的功能。这已经通过Flutter Rust Bridge (FRB)成为可能,它简化了Flutter (Dart)和Rust之间的交互。
这个项目旨在创建一个基本的电池测试应用程序,展示使用Flutter和Rust一起进行跨平台编程的有效性。该软件将收集并呈现设备电池的可用电量及其当前充电状态。我们打算通过利用Flutter提供的灵活UI和Rust的可靠性能来开发一个快速高效的应用程序。这个旅程将带我们经历每个程序,从准备开发环境到创建和集成Rust后端与Flutter前端。
先决条件
在我们开始创建电池测试应用程序之前,值得准备开发环境并确保我们拥有所有必要的设备和技术。如果您想密切关注这个项目,您应该熟悉Flutter和Rust。一方面,Flutter是一个UI工具包,用于为手机、Web和桌面平台构建应用程序,所有这些都来自一个代码库。另一方面,Rust是一种系统编程语言,允许编写并发程序而不牺牲安全性或性能。
您想要使用Flutter SDK、Rust编程语言和Flutter Rust Bridge (FRB)来处理这个项目。要开发跨平台前端,您将需要Flutter SDK。关于后端逻辑,Rust可以做得更好。FRB允许人们在Flutter和Rust之间进行通信,在Flutter应用程序中通过Rust语言使用功能和安全性的所有优势。
设置Flutter和Rust环境
初始步骤是安装Flutter SDK,可以从官方网站获得。下载后,按照为您的操作系统提供的安装说明进行操作。一旦安装了SDK,使用Visual Studio Code或Android Studio作为集成开发环境(IDE)来设置您的开发环境。它们为使用Flutter创建应用程序提供了出色的支持。要检查它是否已正确安装在您的系统上,请在终端中输入此命令:
flutter doctor
此命令评估您环境的状况并生成关于您安装当前状态的报告。
下一步涉及从官方Rust网站安装Rust。该网站提供了适用于多个操作系统的简单安装脚本。完成安装后,通过遵循说明设置您的Rust环境。要确认您已正确安装它,请在终端上运行:
rustc --version
此命令可以确定Rust已正确安装并可以通过您的命令行界面访问。
由于您已经设置了Flutter和Rust评估设置,现在是时候创建一个Flutter项目了。打开您的命令窗口并执行命令:
flutter create battery_test_app
该命令生成一个新的Flutter项目,包含其操作系统所需的所有必要目录和文件。生成项目后,通过在命令提示符屏幕上运行以下命令进入您的项目文件夹:
cd battery_test_app
这些只是创建用于开发测试电池应用程序的操作环境应该采取的一些基本步骤。这种准备很重要,因为它为您提供了每个工具和设置,确保将Rust集成到Flutter中并使用Flutter Rust Bridge无缝工作。
构建Rust后端
由于我们正在制作电池测试应用程序,我们必须使用Rust创建其后端。在本节中,我们将向您展示如何配置Rust项目,添加电池状态功能,并准备Rust库以与Flutter前端连接。
创建Rust库
首先,创建一个新的Rust库。使用终端,移动到一个方便的文件夹,然后输入命令。
cargo new --lib battery_test_lib
此命令将生成一个新的Rust库项目,包含所有必需的文件和文件夹结构。使用以下命令移动到创建的文件夹中:
cd battery_test_lib
接下来,Cargo.toml文件是您需要放置一些依赖项的地方,这些依赖项将有助于检索电池信息。例如,您可以使用battery crate来启用跨平台API来查询电池状态。只需在您的Cargo.toml文件中位于[dependencies]部分的这一行中包含它:
battery = "0.7"
此命令告诉您正在使用battery crate的特定版本,称为版本0.7。
实现电池状态功能
继续打开src/lib.rs文件。这是您将描述库如何工作的地方。您必须创建函数,在battery crate的帮助下获取电池电量和充电状态。示例实现如下:
use battery::Manager;
use battery::Result;
pub fn get_battery_status() -> Result<(i32, String)> {
let manager = Manager::new()?;
let batteries = manager.batteries()?;
let battery = batteries.peekable().next().unwrap()?;
let level = (battery.state_of_charge().value * 100.0) as i32;
let status = format!("{:?}", battery.state());
Ok((level, status))
}
这是Rust编程语言中电池状态的代码。名为get_battery_status的方法创建一个Battery实例,然后获取第一个可用的电池并读取其当前SoC(充电状态)级别和状态。虽然电池电量返回百分比,但状态返回字符串。此函数的返回值是包含整数电池电量和字符串充电状态的元组。
编译和导出
为了让Flutter前端使用Rust语言的函数,首先重要的是创建一个Rust库,然后在Flutter Rust Bridge (FRB)的帮助下生成绑定。首先,运行将在发布模式下编译库的命令,创建优化的二进制文件:
cargo build --release
在target/release文件夹中找到输出文件。
下一步是将Flutter Rust Bridge依赖项添加到您的Rust项目中。为此,将FRB添加到您的Cargo.toml文件中:
[dependencies]
flutter_rust_bridge = "0.5"
使用所需的注释和导入修改您的src/lib.rs文件,以通过使用FRB导出Rust函数:
use flutter_rust_bridge::RustToDart;
use battery::Result;
#[derive(RustToDart)]
pub struct BatteryStatus {
level: i32,
status: String,
}
pub fn get_battery_status() -> Result<BatteryStatus> {
let manager = Manager::new()?;
let batteries = manager.batteries()?;
let battery = batteries.peekable().next().unwrap()?;
let level = (battery.state_of_charge().value * 100.0) as i32;
let status = format!("{:?}", battery.state());
Ok(BatteryStatus { level, status })
}
提供的rust代码与flutter_rust_bridge crate一起工作,允许在Flutter应用程序界面中暴露rust函数。首先,我们需要导入所有相关的crate并创建一个BatteryStatus结构,该结构将存储整数电池电量和充电状态的字符串。此结构用RustToDart注释;因此,它可以被flutter_rust_bridge的任何代码生成实用程序使用。
接下来,get_battery_status函数返回包含BatteryStatus结构的结果。在此函数内,首先初始化电池管理器,然后获取可用的电池缓存。电池电量百分比包含在level字段中,而status字段包含字符串格式的充电状态。最后,此函数返回具有调整值的BatteryStatus结构;因此,可以从Flutter应用程序调用get_battery_status,提供有关设备能源的结构化信息。
要创建绑定,运行FRB工具并在终端中输入:
flutter_rust_bridge_codegen
此命令将创建适当的代码来连接Flutter和Rust,实现两个环境之间的平滑函数调用和数据传输。
在实现了准备集成的Rust后端后,您将其连接到Flutter前端。这种安排允许您在跨平台Flutter应用程序中利用Rust的性能和安全功能,从而为电池测试应用程序提供坚固的基础。
将Rust与Flutter集成
连接Rust和Flutter包括安排Flutter Rust Bridge (FRB),以便它们可以轻松交互。本部分将指导您将编译的Rust库与Flutter项目链接,使用FRB生成所需的绑定,并演示如何从Web应用程序UI调用用Rust编写的函数。
设置桥接
首先将构建的Rust库链接到您的Flutter项目。Rust库已在之前的会话中生成并放置到target/release文件夹中。因此,它们需要从您的Flutter项目中可访问。
后续步骤涉及在Flutter项目目录内为编译的Rust库建立新目录。例如,您可以创建一个名为rust_libs的文件夹。
mkdir rust_libs
获取target/release文件夹中编译的Rust库并将它们放在rust_libs文件夹中。需要复制的特定文件将取决于您针对的平台(libbattery_test_lib.so - 用于Linux,libbattery_test_lib.dylib - 用于macOS,battery_test_lib.dll - 用于Windows)。
您的Flutter项目现在准备通过进行必要的调整来使用这些库文件。通过在Android部分添加以下内容来更改android/app/build.gradle文件:
sourceSets {
main {
jniLibs.srcDirs = ['../rust_libs']
}
}
Android构建必须包含Rust库文件,其配置提供给Gradle。
在iOS上,您可以在Xcode中打开ios/Runner.xcodeproj并将Rust库文件添加到项目中。因此,在项目导航器中右键单击Runner文件夹,选择"Add Files to 'Runner'…",然后选择rust_libs目录中的Rust库文件。
使用FRB生成绑定
您应该使用Flutter Rust Bridge (FRB)代码生成工具来生成Flutter和Rust通信所需的绑定。首先,确保您有FRB工具。您可以使用Cargo安装它。
cargo install flutter_rust_bridge_codegen
之后,您可以使用FRB工具生成绑定。在Flutter项目的目录中,创建一个名为build.sh的新脚本,内容如下:
#!/bin/bash
flutter_rust_bridge_codegen \
-r rust_libs/battery_test_lib.h \
-d lib/rust_bridge_generated.dart
使用此脚本,工具为Rust库生成Dart绑定并将它们放在lib/rust_bridge_generated.dart文件下。当然,要运行脚本,必须通过执行以下命令使其可执行:
chmod +x build.sh
执行命令以生成绑定。
./build.sh
您可以通过Flutter代码直接执行Rust函数,因为生成的Dart绑定将允许这样做。
从Flutter调用Rust函数
在绑定之后,您可以在Flutter代码中使用Rust功能。要导入您生成的绑定,首先在Flutter项目中打开lib/main.dart文件,然后按如下所示导入它们:
import 'rust_bridge_generated.dart';
然后,使用方法调用Rust中的get_battery_status函数并在Flutter应用程序中显示结果。以下是如何实现这一点的示例:
import 'package:flutter/material.dart';
import 'rust_bridge_generated.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: BatteryStatusScreen(),
);
}
}
class BatteryStatusScreen extends StatefulWidget {
@override
_BatteryStatusScreenState createState() => _BatteryStatusScreenState();
}
class _BatteryStatusScreenState extends State<BatteryStatusScreen> {
String batteryStatus = 'Fetching battery status...';
@override
void initState() {
super.initState();
fetchBatteryStatus();
}
Future<void> fetchBatteryStatus() async {
try {
final batteryStatusResult = await api.getBatteryStatus();
setState(() {
batteryStatus = 'Battery Level: ${batteryStatusResult.level}%\n'
'Charging Status: ${batteryStatusResult.status}';
});
} catch (e) {
setState(() {
batteryStatus = 'Failed to fetch battery status: $e';
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Battery Status'),
),
body: Center(
child: Text(batteryStatus),
),
);
}
}
这里的Flutter代码正是这样做的;它创建了一个简单的应用程序,显示来自Rust后端的电池电量。它导入所需的Flutter material包和为Rust函数制作的Dart文件。main方法通过执行MyApp来启动应用程序,MyApp是widget的实例。它由StatelessWidget组成,因此MaterialApp将BatteryStatusScreen作为其第一个屏幕。StatefulWidget带来了BatteryStatusScreen,它通过_BatteryStatusScreenState类创建其状态。BatteryStatus是存储在此类实例变量batteryStatus中的空字符串。
在initState方法中调用从Rust后端获取电池状态的函数,该函数是fetchBatteryStatus函数。我们使用这个异步fetchBatteryStatus和api.getBatteryStatus来获取电池状态。如果调用返回成功响应,那么通过它获得的值将用于使用与该值对应的电量和充电状态更新batteryStatus变量;否则,如果该调用失败,则此变量将使用错误消息更新。build方法构造UI,使得存在标记为"Battery Status"的AppBar,以及包含显示batteryStatus状态变量当前值的Text widget的Center widget。这样的设计将确保用户界面反映通过Rust后端获取的电池发生的任何情况。
构建Flutter前端
现在我们的Flutter应用程序有了平稳运行的Rust后端,是时候考虑前端了。本节将专注于创建显示电池详细信息并与Rust后端通信以获取实时信息的用户界面(UI)。
设计UI
良好的用户体验很大程度上取决于创建简单直观的界面。Flutter丰富的widget和工具集合简化了设计响应式UI。我们的电池测试应用程序应该有一个显示电池电量和充电状态的界面。在您的Flutter项目中,打开lib/main.dart。如我们之前部分所述,您应该已经有了BatteryStatusScreen的必要导入。现在让我们考虑对UI设计进行修改以改进它。因此,让我们向BatteryStatusScreen widget添加更多样式元素:
import 'package:flutter/material.dart';
import 'rust_bridge_generated.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Battery Test App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: BatteryStatusScreen(),
);
}
}
class BatteryStatusScreen extends StatefulWidget {
@override
_BatteryStatusScreenState createState() => _BatteryStatusScreenState();
}
class _BatteryStatusScreenState extends State<BatteryStatusScreen> {
String batteryStatus = 'Fetching battery status...';
@override
void initState() {
super.initState();
fetchBatteryStatus();
}
Future<void> fetchBatteryStatus() async {
try {
final batteryStatusResult = await api.getBatteryStatus();
setState(() {
batteryStatus = 'Battery Level: ${batteryStatusResult.level}%\n'
'Charging Status: ${batteryStatusResult.status}';
});
} catch (e) {
setState(() {
batteryStatus = 'Failed to fetch battery status: $e';
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Battery Status'),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.battery_full,
size: 100.0,
color: Colors.green,
),
SizedBox(height: 20.0),
Text(
batteryStatus,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20.0),
),
],
),
),
),
);
}
}
这个应用程序在Flutter代码中显示从Rust后端检索的电池状态。它首先导入基本的Flutter material包并为Rust函数生成dart绑定。应用程序从main函数开始,其myapp widget的实例运行以启动它。这个应用程序的主要结构由MyApp布局,它扩展了StatelessWidget。它称自己为电池测试应用程序,并在其设计中使用深色主题。BatteryStatusScreen被设置为此设备的主屏幕。
BatteryStatusScreen widget扩展StatefulWidget类,以便其界面可以根据设备上的当前电池状况而变化。其状态的实现在_BatteryStatusScreenState类中找到,该类持有定义为常量占位符字符串的单个变量batteryStatus。在此过程开始时,在我们的initState函数中,我们将调用fetchBatteryStatus函数,因为我们希望能够从中读取,从而知道我们是否电量不足,而不必每次都询问;否则,我们将不断有死机!此外,知道我们只剩下15%的电量也很有帮助,因为知道是否值得带上我们的PowerBank可能会派上用场。当此方法成功获取数据时,batteryStatus变量更改为当前充电级别并指示设备是否正在充电。但是,当在传递这些类型的信息时出现问题时,状态变量会变成错误消息。
与Rust后端交互
您期望使用Flutter前端与Rust后端通信,以实现动态变化的用户界面和有关电池寿命的实时信息。这通过位于BatteryStatusScreen widget内的fetchBatteryStatus方法实现,它调用用Rust编写的getBatteryStatus,更改batteryStatus的状态变量,然后执行setState,这导致更新UI。这将为用户简化事情。向BatteryStatusScreen widget添加FloatingActionButton组件提供了通过按钮轻松刷新电池状态的选项。
import 'package:flutter/material.dart';
import 'rust_bridge_generated.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Battery Test App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: BatteryStatusScreen(),
);
}
}
class BatteryStatusScreen extends StatefulWidget {
@override
_BatteryStatusScreenState createState() => _BatteryStatusScreenState();
}
class _BatteryStatusScreenState extends State<BatteryStatusScreen> {
String batteryStatus = 'Fetching battery status...';
@override
void initState() {
super.initState();
fetchBatteryStatus();
}
Future<void> fetchBatteryStatus() async {
try {
final batteryStatusResult = await api.getBatteryStatus();
setState(() {
batteryStatus = 'Battery Level: ${batteryStatusResult.level}%\n'
'Charging Status: ${batteryStatusResult.status}';
});
} catch (e) {
setState(() {
batteryStatus = 'Failed to fetch battery status: $e';
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Battery Status'),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.battery_full,
size: 100.0,
color: Colors.green,
),
SizedBox(height: 20.0),
Text(
batteryStatus,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20.0),
),
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: fetchBatteryStatus,
tooltip: 'Refresh',
child: Icon(Icons.refresh),
),
);
}
}
这个Flutter代码建立了一个基本应用程序,呈现从Rust后端获得的电池状态详细信息。MyApp widget在main函数中作为实例运行,通过提供标题、主题和BatteryStatusScreen作为其主屏幕来启动应用程序。BatteryStatusScreen widget是有状态的,由_BatteryStatusScreenState管理。起初,batteryStatus设置为"Fetching battery status…"。在initState方法中,fetchBatteryStatus函数使用api.getBatteryStatus从Rust检索电池状态。如果调用通过,它将使用电池电量和是否正在充电来更新batteryStatus,但如果失败,将显示错误消息。
UI在build方法中构建,包括带有标题"Battery Status"的AppBar、描绘电池的居中图标widget、用于间隔不同组件的SizedBox和显示电池状态的Text widget。如果您想手动刷新它,FloatingActionButton调用fetchBatteryStatus方法以获取有关其当前状态的最新信息。我们如何保持UI与有关我们电源单元如何充电的实时消息同步取决于Rust服务器获取服务。
运行应用程序
一旦Flutter界面已经开发并且Rust后端已经添加到其中,这些是启动运行应用程序要采取的步骤:
构建Rust代码:在启动Flutter项目之前,确保编译您的Rust代码并共享输出库。您可以运行以下命令来实现这一点:
cargo build --release
链接Rust库:确保您的Flutter项目链接到编译的库。根据您使用的平台(Android或iOS),需要某些步骤来集成Rust库。
对于Android:您应该将组装的Rust库(例如,libyourlibrary.so)放入Flutter项目的适当文件夹中,通常在android/app/src/main/jniLibs/中找到。
接下来,以包含此库的方式更改您的android/app/build.gradle:
android {
...
sourceSets {
main {
jniLibs.srcDirs = ['src/main/jniLibs']
}
}
}
对于iOS:您的Flutter项目应该在正确的位置有编译的Rust库(例如,libyourlibrary.a),通常是ios/。
要链接Rust库,您应该按如下方式更新您的Xcode项目设置:
- 在Xcode中打开ios/Runner.xcworkspace。
- 从项目导航器中选择您的项目。
- 选择您应用程序的目标。
- 转到"Build Phases"选项卡。
- 将Rust库添加到"Link Binary With Libraries"。
运行应用程序:打开终端,导航到Flutter项目的目录,并执行:
flutter run
该命令构建您的Flutter应用程序并在模拟器或连接的设备上运行它。如果每个配置都正确,您的应用程序应该显示从Rust后端获得的电池状态信息。
输出:GIF-240807_153740[1]
从Rust后端,Flutter应用程序获取电池状态信息。当应用程序启动时,它最初显示正在获取电池状态的消息。然后,一旦检索到电池状态,它通过UI更新显示,显示诸如"Battery Level: 98%"和"Charging Status: Charging"之类的内容。
结论
最终,使用Flutter和Rust的电池测试应用程序是如何将两种跨平台开发技术结合起来的良好演示。该应用程序将Flutter的用户界面功能与Rust的性能和系统级访问相结合,创建了一个强大的电池状态监控平台。设置Flutter环境是这个过程的第一步。接下来是配置Rust并通过Flutter-Rust Bridge (FRB)将两者链接起来。然后我们创建了一个Rust后端,可以访问我们的电池状态查询,连接到我们应用程序的前端,该前端使用Flutter构建,并以更用户友好的方式输出信息。
本文首发公众号 猩猩程序员 欢迎关注