Messaging¶
You can use signal traits to mark a struct as a stream endpoint.[1]
When you generate Dart signal class code from Rust structs using the rinf gen
command, the hub
crate is analyzed, and the resulting Dart modules are placed in lib/src/bindings
folder by default.
rinf gen
If you add the optional argument -w
or --watch
to the rinf gen
command, the message code will be automatically generated when Rust files are modified. If you add this argument, the command will not exit on its own.
rinf gen --watch
Channel Signals¶
The RustSignal
trait generates a signal stream from Rust to Dart.[2] Use the RustSignalBinary
trait to include binary data without the overhead of serialization.
#[derive(Serialize, RustSignal)]
struct MyDataOutput {
my_field: bool,
}
MyDataOutput { my_field: true }.send_signal_to_dart();
// Rebuild the widget from Rust signals on each render frame.
// Some Rust signals between frames may be ignored.
StreamBuilder(
stream: MyDataOutput.rustSignalStream,
builder: (context, snapshot) {
final signalPack = snapshot.data;
if (signalPack == null) {
// Return an empty widget.
}
MyDataOutput message = signalPack.message;
// Below requires `RustSignalBinary`.
Uint8List binary = signalPack.binary;
// Return a filled widget.
},
);
// Alternatively, handle every Rust signal.
// Don't forget to cancel the subscription when it's no longer needed!
final subscription = MyDataOutput.rustSignalStream.listen((signalPack) {
MyDataOutput message = signalPack.message;
})
The DartSignal
trait generates a signal stream from Dart to Rust. Use the DartSignalBinary
trait to include binary data without the overhead of serialization.
#[derive(Deserialize, DartSignal)]
struct MyDataInput {
my_field: bool,
}
MyDataInput(my_field: true).sendSignalToRust();
let receiver = MyDataInput::get_dart_signal_receiver();
while let Some(signal_pack) = receiver.recv().await {
let message: MyDataInput = signal_pack.message;
// Below requires `DartSignalBinary`.
let binary: Vec<u8> = signal_pack.binary;
// Custom Rust logic goes here.
}
Now let’s delve into the meaning of each field of a signal pack.
Field
message
: It represents a message of a type annotated by a signal trait. This field is always filled.Field
binary
: This is a field designed to handle large binary data, potentially up to a few gigabytes. You can send any kind of binary data you wish, such as a high-resolution image or file data. This field carries emptyUint8List
orVec<u8>
if the message is not marked as binary signal.
It’s important to note that creating a signal larger than a few megabytes is not recommended. For large data, split it into multiple signals or use the binary
field provided by the RustSignalBinary
or DartSignalBinary
traits instead.[3]
Nested Signals¶
To nest a struct inside a signal struct, use the SignalPiece
trait. A SignalPiece
cannot be passed between languages independently, but it can be nested inside a RustSignal
, DartSignal
, or another SignalPiece
.
#[derive(Serialize, RustSignal)]
struct Outer {
middle: Middle,
}
#[derive(Serialize, SignalPiece)]
struct Middle {
inner: Inner,
}
#[derive(Serialize, SignalPiece)]
struct Inner {
my_field: bool,
}