[fp-rs] Invoking Rust code from Dart, retrieving stream of incoming bluetooth advertisements.

This commit is contained in:
Lucas Silva Shepard
2023-08-13 16:52:26 -07:00
parent 2c8809c1e1
commit bf05ab5db4
7 changed files with 147 additions and 38 deletions
@@ -9,7 +9,11 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge.dart';
import 'package:uuid/uuid.dart';
abstract class Rust {
Future<String> hello({dynamic hint});
Future<void> init({dynamic hint});
FlutterRustBridgeTaskConstMeta get kHelloConstMeta;
FlutterRustBridgeTaskConstMeta get kInitConstMeta;
Stream<String> eventStream({dynamic hint});
FlutterRustBridgeTaskConstMeta get kEventStreamConstMeta;
}
+45 -11
View File
@@ -25,19 +25,35 @@ class RustImpl implements Rust {
factory RustImpl.wasm(FutureOr<WasmModule> module) =>
RustImpl(module as ExternalLibrary);
RustImpl.raw(this._platform);
Future<String> hello({dynamic hint}) {
Future<void> init({dynamic hint}) {
return _platform.executeNormal(FlutterRustBridgeTask(
callFfi: (port_) => _platform.inner.wire_hello(port_),
parseSuccessData: _wire2api_String,
constMeta: kHelloConstMeta,
callFfi: (port_) => _platform.inner.wire_init(port_),
parseSuccessData: _wire2api_unit,
constMeta: kInitConstMeta,
argValues: [],
hint: hint,
));
}
FlutterRustBridgeTaskConstMeta get kHelloConstMeta =>
FlutterRustBridgeTaskConstMeta get kInitConstMeta =>
const FlutterRustBridgeTaskConstMeta(
debugName: "hello",
debugName: "init",
argNames: [],
);
Stream<String> eventStream({dynamic hint}) {
return _platform.executeStream(FlutterRustBridgeTask(
callFfi: (port_) => _platform.inner.wire_event_stream(port_),
parseSuccessData: _wire2api_String,
constMeta: kEventStreamConstMeta,
argValues: [],
hint: hint,
));
}
FlutterRustBridgeTaskConstMeta get kEventStreamConstMeta =>
const FlutterRustBridgeTaskConstMeta(
debugName: "event_stream",
argNames: [],
);
@@ -57,6 +73,10 @@ class RustImpl implements Rust {
Uint8List _wire2api_uint_8_list(dynamic raw) {
return raw as Uint8List;
}
void _wire2api_unit(dynamic raw) {
return;
}
}
// Section: api2wire
@@ -168,17 +188,31 @@ class RustWire implements FlutterRustBridgeWireBase {
late final _init_frb_dart_api_dl = _init_frb_dart_api_dlPtr
.asFunction<int Function(ffi.Pointer<ffi.Void>)>();
void wire_hello(
void wire_init(
int port_,
) {
return _wire_hello(
return _wire_init(
port_,
);
}
late final _wire_helloPtr =
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>('wire_hello');
late final _wire_hello = _wire_helloPtr.asFunction<void Function(int)>();
late final _wire_initPtr =
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>('wire_init');
late final _wire_init = _wire_initPtr.asFunction<void Function(int)>();
void wire_event_stream(
int port_,
) {
return _wire_event_stream(
port_,
);
}
late final _wire_event_streamPtr =
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>(
'wire_event_stream');
late final _wire_event_stream =
_wire_event_streamPtr.asFunction<void Function(int)>();
void free_WireSyncReturn(
WireSyncReturn ptr,
+15 -17
View File
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:demo/rust.dart';
void main() {
api.init();
runApp(const FastPairApp());
}
@@ -23,22 +24,19 @@ class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: const Text("Fast Pair"),
appBar: AppBar(title: const Text("Fast Pair")),
body: Center(
child: StreamBuilder(
// All Rust functions are called as Future's
stream: api.eventStream(), // The Rust function we are calling.
builder: (context, data) {
if (data.hasData) {
return Text(data.data!); // The string to display
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
body: Center(
child: FutureBuilder(
// All Rust functions are called as Future's
future: api.hello(), // The Rust function we are calling.
builder: (context, data) {
if (data.hasData) {
return Text(data.data!); // The string to display
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
),
);
));
}
+4
View File
@@ -9,4 +9,8 @@ edition = "2021"
crate-type = ["lib", "cdylib", "staticlib"]
[dependencies]
anyhow = "1.0"
bluetooth = { version = "0.1", path = "../../bluetooth" }
flutter_rust_bridge = "1"
futures = { version = "0.3", features = ["executor"] }
tracing = "0.1.37"
+56 -2
View File
@@ -1,3 +1,57 @@
pub fn hello() -> String {
String::from("Rust says hi!")
use std::sync::RwLock;
use bluetooth::{
api::{BleAdapter, BleDevice},
BleDataTypeId, Platform,
};
use flutter_rust_bridge::StreamSink;
use futures::executor;
use tracing::info;
// Sends a device name to Flutter via `StreamSink` FFI layer.
static NAME_STREAM: RwLock<Option<StreamSink<String>>> = RwLock::new(None);
/// Sets up initial constructs and infinitely polls for advertisements.
pub fn init() {
let run = async {
info!("start making adapter");
let mut adapter = Platform::default_adapter().await.unwrap();
adapter.start_scan().unwrap();
let datatype_selector = vec![BleDataTypeId::ServiceData16BitUuid];
loop {
let advertisement = adapter
.next_advertisement(Some(&datatype_selector))
.await
.unwrap();
for service_data in advertisement.service_data_16bit_uuid().unwrap() {
let uuid = service_data.uuid();
// This is a Fast Pair device.
if uuid == 0x2cfe {
let addr = advertisement.address();
let ble_device = Platform::new_ble_device(addr).await.unwrap();
let name = ble_device.name().unwrap();
info!("device: {}", name);
match NAME_STREAM.read().unwrap().as_ref() {
Some(s) => {
s.add(name);
}
None => info!("Stream is None"),
}
}
}
}
};
executor::block_on(run)
}
/// Sets up `StreamSink` for Dart-Rust FFI.
pub fn event_stream(s: StreamSink<String>) -> Result<(), anyhow::Error> {
let mut stream = NAME_STREAM.write().unwrap();
*stream = Some(s);
Ok(())
}
@@ -2,8 +2,13 @@ use super::*;
// Section: wire functions
#[no_mangle]
pub extern "C" fn wire_hello(port_: i64) {
wire_hello_impl(port_)
pub extern "C" fn wire_init(port_: i64) {
wire_init_impl(port_)
}
#[no_mangle]
pub extern "C" fn wire_event_stream(port_: i64) {
wire_event_stream_impl(port_)
}
// Section: allocate functions
@@ -22,14 +22,24 @@ use std::sync::Arc;
// Section: wire functions
fn wire_hello_impl(port_: MessagePort) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, String>(
fn wire_init_impl(port_: MessagePort) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, ()>(
WrapInfo {
debug_name: "hello",
debug_name: "init",
port: Some(port_),
mode: FfiCallMode::Normal,
},
move || move |task_callback| Ok(hello()),
move || move |task_callback| Ok(init()),
)
}
fn wire_event_stream_impl(port_: MessagePort) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, ()>(
WrapInfo {
debug_name: "event_stream",
port: Some(port_),
mode: FfiCallMode::Stream,
},
move || move |task_callback| event_stream(task_callback.stream_sink::<_, String>()),
)
}
// Section: wrapper structs