[fp-rs] Set up basic demo for calling Rust from Flutter

This commit is contained in:
Lucas Silva Shepard
2023-08-13 16:51:13 -07:00
parent 80a3b508fa
commit 6074e1beba
5 changed files with 271 additions and 146 deletions
@@ -0,0 +1,15 @@
// AUTO GENERATED FILE, DO NOT EDIT.
// Generated by `flutter_rust_bridge`@ 1.79.0.
// ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const
import 'dart:convert';
import 'dart:async';
import 'package:meta/meta.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge.dart';
import 'package:uuid/uuid.dart';
abstract class Rust {
Future<String> hello({dynamic hint});
FlutterRustBridgeTaskConstMeta get kHelloConstMeta;
}
@@ -0,0 +1,203 @@
// AUTO GENERATED FILE, DO NOT EDIT.
// Generated by `flutter_rust_bridge`@ 1.79.0.
// ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const
import "bridge_definitions.dart";
import 'dart:convert';
import 'dart:async';
import 'package:meta/meta.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge.dart';
import 'package:uuid/uuid.dart';
import 'dart:convert';
import 'dart:async';
import 'package:meta/meta.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge.dart';
import 'package:uuid/uuid.dart';
import 'dart:ffi' as ffi;
class RustImpl implements Rust {
final RustPlatform _platform;
factory RustImpl(ExternalLibrary dylib) => RustImpl.raw(RustPlatform(dylib));
/// Only valid on web/WASM platforms.
factory RustImpl.wasm(FutureOr<WasmModule> module) =>
RustImpl(module as ExternalLibrary);
RustImpl.raw(this._platform);
Future<String> hello({dynamic hint}) {
return _platform.executeNormal(FlutterRustBridgeTask(
callFfi: (port_) => _platform.inner.wire_hello(port_),
parseSuccessData: _wire2api_String,
constMeta: kHelloConstMeta,
argValues: [],
hint: hint,
));
}
FlutterRustBridgeTaskConstMeta get kHelloConstMeta =>
const FlutterRustBridgeTaskConstMeta(
debugName: "hello",
argNames: [],
);
void dispose() {
_platform.dispose();
}
// Section: wire2api
String _wire2api_String(dynamic raw) {
return raw as String;
}
int _wire2api_u8(dynamic raw) {
return raw as int;
}
Uint8List _wire2api_uint_8_list(dynamic raw) {
return raw as Uint8List;
}
}
// Section: api2wire
// Section: finalizer
class RustPlatform extends FlutterRustBridgeBase<RustWire> {
RustPlatform(ffi.DynamicLibrary dylib) : super(RustWire(dylib));
// Section: api2wire
// Section: finalizer
// Section: api_fill_to_wire
}
// ignore_for_file: camel_case_types, non_constant_identifier_names, avoid_positional_boolean_parameters, annotate_overrides, constant_identifier_names
// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
// ignore_for_file: type=lint
/// generated by flutter_rust_bridge
class RustWire implements FlutterRustBridgeWireBase {
@internal
late final dartApi = DartApiDl(init_frb_dart_api_dl);
/// Holds the symbol lookup function.
final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
_lookup;
/// The symbols are looked up in [dynamicLibrary].
RustWire(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup;
/// The symbols are looked up with [lookup].
RustWire.fromLookup(
ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
lookup)
: _lookup = lookup;
void store_dart_post_cobject(
DartPostCObjectFnType ptr,
) {
return _store_dart_post_cobject(
ptr,
);
}
late final _store_dart_post_cobjectPtr =
_lookup<ffi.NativeFunction<ffi.Void Function(DartPostCObjectFnType)>>(
'store_dart_post_cobject');
late final _store_dart_post_cobject = _store_dart_post_cobjectPtr
.asFunction<void Function(DartPostCObjectFnType)>();
Object get_dart_object(
int ptr,
) {
return _get_dart_object(
ptr,
);
}
late final _get_dart_objectPtr =
_lookup<ffi.NativeFunction<ffi.Handle Function(ffi.UintPtr)>>(
'get_dart_object');
late final _get_dart_object =
_get_dart_objectPtr.asFunction<Object Function(int)>();
void drop_dart_object(
int ptr,
) {
return _drop_dart_object(
ptr,
);
}
late final _drop_dart_objectPtr =
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.UintPtr)>>(
'drop_dart_object');
late final _drop_dart_object =
_drop_dart_objectPtr.asFunction<void Function(int)>();
int new_dart_opaque(
Object handle,
) {
return _new_dart_opaque(
handle,
);
}
late final _new_dart_opaquePtr =
_lookup<ffi.NativeFunction<ffi.UintPtr Function(ffi.Handle)>>(
'new_dart_opaque');
late final _new_dart_opaque =
_new_dart_opaquePtr.asFunction<int Function(Object)>();
int init_frb_dart_api_dl(
ffi.Pointer<ffi.Void> obj,
) {
return _init_frb_dart_api_dl(
obj,
);
}
late final _init_frb_dart_api_dlPtr =
_lookup<ffi.NativeFunction<ffi.IntPtr Function(ffi.Pointer<ffi.Void>)>>(
'init_frb_dart_api_dl');
late final _init_frb_dart_api_dl = _init_frb_dart_api_dlPtr
.asFunction<int Function(ffi.Pointer<ffi.Void>)>();
void wire_hello(
int port_,
) {
return _wire_hello(
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)>();
void free_WireSyncReturn(
WireSyncReturn ptr,
) {
return _free_WireSyncReturn(
ptr,
);
}
late final _free_WireSyncReturnPtr =
_lookup<ffi.NativeFunction<ffi.Void Function(WireSyncReturn)>>(
'free_WireSyncReturn');
late final _free_WireSyncReturn =
_free_WireSyncReturnPtr.asFunction<void Function(WireSyncReturn)>();
}
final class _Dart_Handle extends ffi.Opaque {}
typedef DartPostCObjectFnType = ffi.Pointer<
ffi.NativeFunction<
ffi.Bool Function(DartPort port_id, ffi.Pointer<ffi.Void> message)>>;
typedef DartPort = ffi.Int64;
+35 -116
View File
@@ -1,125 +1,44 @@
import 'package:flutter/material.dart';
import 'package:demo/rust.dart';
void main() {
runApp(const MyApp());
runApp(const FastPairApp());
}
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;
class FastPairApp extends StatelessWidget {
const FastPairApp({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
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,
),
],
Widget build(BuildContext context) => MaterialApp(
title: 'Fast Pair',
theme: ThemeData(
primarySwatch: Colors.blue,
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
home: const HomePage(),
);
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: const Text("Fast Pair"),
),
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(),
);
},
),
),
);
}
+18
View File
@@ -0,0 +1,18 @@
// This file initializes the dynamic library and connects it with the stub
// generated by flutter_rust_bridge_codegen.
import 'dart:ffi';
import 'dart:io' as io;
import 'package:demo/bridge_generated.dart';
const _base = 'rust';
// On MacOS, the dynamic library is not bundled with the binary,
// but rather directly **linked** against the binary.
final _dylib = io.Platform.isWindows ? '$_base.dll' : 'lib$_base.so';
final api = RustImpl(io.Platform.isIOS || io.Platform.isMacOS
? DynamicLibrary.executable()
: DynamicLibrary.open(_dylib));
-30
View File
@@ -1,30 +0,0 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility in the flutter_test package. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:demo/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}