教程

为了掌握基本概念,最好跟随一个分步教程。

在开始之前,请确保您的 widget 树中某处有一个 Column,它将包含我们的教程 widget。

lib/main.dart
Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: [],
)

从 Dart 到 Rust

假设要在 Dart 中创建一个新按钮,并通过这个按钮向 Rust 发送一个数字数组和一个字符串。需要有一个信号来通知 Rust 用户事件已发生。

hub crate 中编写一个新的信号结构体。请注意,消息上方应标注 DartSignal 属性。

native/hub/src/signals/mod.rs
use rinf::DartSignal;
use serde::Deserialize;

#[derive(Deserialize, DartSignal)]
pub struct MyPreciousData {
  pub input_numbers: Vec<i32>,
  pub input_string: String,
}

接下来,从标注的信号结构体生成 Dart 代码。

CLI
rinf gen

在 Dart 中创建一个接受用户输入的按钮 widget。

lib/main.dart
import 'package:my_app/src/bindings/bindings.dart';

Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    ElevatedButton(
      onPressed: () async {
        MyPreciousData(
          inputNumbers: [3, 4, 5],
          inputString: 'Zero-cost abstraction',
        ).sendSignalToRust(); // GENERATED
      },
      child: Text('Send a Signal from Dart to Rust'),
    ),
  ]
)

让我们在 Rust 中监听此消息。这个简单的函数会将数组中的每个元素加 1,并将字符串中的所有字母大写。

native/hub/src/tutorial_functions.rs
use crate::signals::MyPreciousData;
use rinf::{DartSignal, debug_print};

pub async fn calculate_precious_data() {
  let receiver = MyPreciousData::get_dart_signal_receiver(); // GENERATED
  while let Some(signal_pack) = receiver.recv().await {
    let my_precious_data = signal_pack.message;

    let new_numbers: Vec<i32> = my_precious_data
      .input_numbers
      .into_iter()
      .map(|x| x + 1)
      .collect();
    let new_string = my_precious_data.input_string.to_uppercase();

    debug_print!("{:?}", new_numbers);
    debug_print!("{}", new_string);
  }
}
native/hub/src/lib.rs
mod tutorial_functions;

use tokio::spawn;
use tutorial_functions::calculate_precious_data;

#[tokio::main]
async fn main() {
  spawn(calculate_precious_data());
  dart_shutdown().await;
}

现在用 flutter run 运行应用。点击按钮后,我们可以在命令行中看到打印输出!

Output
flutter: [4, 5, 6]
flutter: ZERO-COST ABSTRACTION

从 Rust 到 Dart

假设您想每秒从 Rust 向 Dart 发送递增的数字。

定义信号结构体。请注意,结构体上方应标注 RustSignal 属性。

native/hub/src/signals/mod.rs
use rinf::RustSignal;
use serde::Serialize;

#[derive(Serialize, RustSignal)]
pub struct MyAmazingNumber {
  pub current_number: i32,
}

生成 Dart 信号类。

CLI
rinf gen

定义一个永远运行的异步 Rust 函数,每秒向 Dart 发送数字。

native/hub/src/tutorial_functions.rs
use crate::signals::MyAmazingNumber;
use rinf::RustSignal;
use std::time::Duration;
use tokio::time::interval;

pub async fn stream_amazing_number() {
  let mut current_number: i32 = 1;
  let mut time_interval = interval(Duration::from_secs(1));
  loop {
    time_interval.tick().await;
    MyAmazingNumber { current_number }.send_signal_to_dart(); // 已生成
    current_number += 1;
  }
}
native/hub/src/lib.rs
mod tutorial_functions;

use tokio::spawn;
use tutorial_functions::stream_amazing_number;

#[tokio::main]
async fn main() {
  // ...
  tokio::spawn(stream_amazing_number());
  dart_shutdown().await;
}

最后,在 Dart 中使用 StreamBuilder 接收信号,并相应地重建 widget。

lib/main.dart
import 'package:my_app/src/bindings/bindings.dart';

Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    // ...
    StreamBuilder(
      stream: MyAmazingNumber.rustSignalStream, // 已生成
      builder: (context, snapshot) {
        final signalPack = snapshot.data;
        if (signalPack == null) {
          return Text('Nothing received yet');
        }
        final myAmazingNumber = signalPack.message;
        final currentNumber = myAmazingNumber.currentNumber;
        return Text(currentNumber.toString());
      },
    ),
  ],
)

双向通信

您可以通过结合这两种消息传递方式,轻松地在屏幕上显示更新后的状态。

native/hub/src/signals/mod.rs
use rinf::{DartSignal, RustSignal};
use serde::{Deserialize, Serialize};

#[derive(Deserialize, DartSignal)]
pub struct MyTreasureInput {}

#[derive(Serialize, RustSignal)]
pub struct MyTreasureOutput {
  pub current_value: i32,
}
CLI
rinf gen
lib/main.dart
import 'package:my_app/src/bindings/bindings.dart';

children: [
  // ...
  StreamBuilder(
    stream: MyTreasureOutput.rustSignalStream, // 已生成
    builder: (context, snapshot) {
      final signalPack = snapshot.data;
      if (signalPack == null) {
        return Text('No value yet');
      }
      final myTreasureOutput = signalPack.message;
      final currentNumber = myTreasureOutput.currentValue;
      return Text('Output value is $currentNumber');
    },
  ),
  ElevatedButton(
    onPressed: () {
      MyTreasureInput().sendSignalToRust(); // 已生成
    },
    child: Text('Send the input'),
  ),
]
native/hub/src/tutorial_functions.rs
use crate::signals::{MyTreasureInput, MyTreasureOutput};
use rinf::{DartSignal, RustSignal};

pub async fn tell_treasure() {
  let mut current_value: i32 = 1;

  let receiver = MyTreasureInput::get_dart_signal_receiver(); // 已生成
  while let Some(_) = receiver.recv().await {
    MyTreasureOutput { current_value }.send_signal_to_dart(); // 已生成
    current_value += 1;
  }
}
native/hub/src/lib.rs
mod tutorial_functions;

use tokio::spawn;
use tutorial_functions::tell_treasure;

#[tokio::main]
async fn main() {
  // ...
  spawn(tell_treasure());
  dart_shutdown().await;
}