diff --git a/.config/dictionaries/project.dic b/.config/dictionaries/project.dic
index bccaac193..74723fad7 100644
--- a/.config/dictionaries/project.dic
+++ b/.config/dictionaries/project.dic
@@ -1,6 +1,7 @@
aarch
bindgen
binstall
+bkioshn
buildkit
camelcase
canvaskit
@@ -46,6 +47,7 @@ gopls
gosec
graphviz
idents
+JDBC
jorm
jormungandr
junitreport
@@ -89,6 +91,7 @@ PYTHONDONTWRITEBYTECODE
rankdir
rivo
runewidth
+rustc
rustdoc
rustdocflags
rustflags
@@ -122,4 +125,3 @@ xerrors
xvfb
zstd
zstdcat
-JDBC
\ No newline at end of file
diff --git a/docs/src/guides/languages/flutter.md b/docs/src/guides/languages/flutter.md
index 819e91152..7472d7aa8 100644
--- a/docs/src/guides/languages/flutter.md
+++ b/docs/src/guides/languages/flutter.md
@@ -157,6 +157,39 @@ please follow this [guide](./../../onboarding/index.md).
It is pretty strait forward for this builder process,
because as a part of `+build` target we already creating a docker image.
+## Enhancing Flutter
+
+### Integrating Flutter with Rust using `flutter_rust_bridge`
+
+The `flutter_rust_bridge` allows you to integrate Rust with Flutter app, while maintaining the rest of the app in
+Dart.
+This can be useful for situations where you need to run complex algorithms, handle data
+processing, or interact with low-level system APIs, but still want to leverage the Flutter ecosystem
+for UI and app management.
+
+Start by creating a new builder where all the necessary setup is done under the `flutter_rust_bridge+builder`,
+then copy the Flutter project that already have `flutter_rust_bridge` setup.
+Refer to for how to setup the project.
+
+```Earthfile
+builder-frb:
+ FROM flutter_rust_bridge+builder
+ COPY . .
+```
+
+Then generate a binding between Rust and Flutter
+
+```Earthfile
+# Generated necessary files for running Flutter web locally and save it locally.
+code-generator-web:
+ FROM +builder-frb
+ DO flutter_rust_bridge+CODE_GENERATOR_WEB
+
+ SAVE ARTIFACT ./assets/js AS LOCAL ./assets/js
+ SAVE ARTIFACT ./rust/src/frb_generated.rs AS LOCAL ./rust/src/frb_generated.rs
+ SAVE ARTIFACT ./lib/src AS LOCAL ./lib/src
+```
+
## Conclusion
You can see the final `Earthfile`
diff --git a/earthly/flutter/Earthfile b/earthly/flutter/Earthfile
index 0e78ece8b..bad1d7bba 100644
--- a/earthly/flutter/Earthfile
+++ b/earthly/flutter/Earthfile
@@ -4,21 +4,37 @@ VERSION 0.8
IMPORT ./installer AS installer
-# flutter-src save Flutter source code as artifact.
-flutter-src:
- FROM busybox
+# TODO(bkioshn): https://github.com/input-output-hk/catalyst-ci/issues/322
+# Install flutter.
+INSTALL_FLUTTER:
+ FUNCTION
ARG version=3.24.1
ARG TARGETARCH
- RUN mkdir -p /flutter
+ # Install Flutter
IF [ "$TARGETARCH" = "amd64" ]
RUN wget -qO - https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_$version-stable.tar.xz \
- | tar -xJ -C /flutter
+ | tar -xJ -C /usr/local
+ ELSE IF [ "$TARGETARCH" = "arm64" ]
+ GIT CLONE --branch $version https://github.com/flutter/flutter.git /usr/local
ELSE
- GIT CLONE --branch $version https://github.com/flutter/flutter.git /flutter
+ RUN echo >&2 "unsupported architecture: ${TARGETARCH}"; exit 1
END
- SAVE ARTIFACT /flutter flutter
+ RUN git config --global --add safe.directory /usr/local/flutter
+ ENV PATH="/usr/local/flutter/bin:/usr/local/flutter/bin/cache/dart-sdk/bin:$HOME/.pub-cache/bin:${PATH}"
+
+ ENV CI="true"
+ RUN flutter config --no-analytics
+ RUN flutter --version
+ RUN flutter doctor -v
+ RUN flutter config --enable-web
+ RUN dart pub global activate melos
+ RUN dart pub global activate junitreport
+ RUN dart pub global activate coverage
+ RUN dart pub global activate combine_coverage
+ RUN dart pub global activate license_checker
+
# flutter-base installs required tools and packages.
flutter-base:
FROM debian:bookworm-20240513-slim
@@ -26,7 +42,9 @@ flutter-base:
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update --fix-missing
- RUN apt-get install -y apt-utils git curl gzip unzip bzip2 bash jq gpg lcov
+ RUN apt-get install -y apt-utils git curl gzip unzip bzip2 bash jq gpg lcov tar wget xz-utils
+
+ DO +INSTALL_FLUTTER
WORKDIR frontend
@@ -37,21 +55,6 @@ flutter-base:
ARG edge_package_release = 1
DO installer+INSTALL_EDGE_LINUX64 --edge_version=$edge_version --edge_package_release=$edge_package_release
- COPY +flutter-src/flutter /usr/local
- ENV PATH="/usr/local/flutter/bin:/usr/local/flutter/bin/cache/dart-sdk/bin:$HOME/.pub-cache/bin:${PATH}"
- # Flutter prints warnings when used by root user but omits them if has CI env flag found.
- # script: https://github.com/flutter/flutter/blob/master/bin/internal/shared.sh#L214
- ENV CI="true"
- RUN flutter config --no-analytics
- RUN flutter --version
- RUN flutter doctor -v
- RUN flutter config --enable-web
- RUN dart pub global activate melos
- RUN dart pub global activate junitreport
- RUN dart pub global activate coverage
- RUN dart pub global activate combine_coverage
- RUN dart pub global activate license_checker
-
# test-flutter-base-amd64 : installs required tools and packages for amd64.
test-flutter-base-amd64:
BUILD --platform=linux/amd64 +flutter-base
diff --git a/earthly/flutter_rust_bridge/Earthfile b/earthly/flutter_rust_bridge/Earthfile
new file mode 100644
index 000000000..3a4f6992e
--- /dev/null
+++ b/earthly/flutter_rust_bridge/Earthfile
@@ -0,0 +1,38 @@
+VERSION 0.8
+
+IMPORT ../flutter AS flutter-ci
+IMPORT ../rust AS rust-ci
+
+# This will be refactored in the future
+# TODO(bkioshn): https://github.com/input-output-hk/catalyst-ci/issues/322
+# builder: Setup necessary tools for `flutter_rust_bridge`
+builder:
+ FROM debian:stable-slim
+
+ WORKDIR /work
+
+ RUN apt-get update \
+ && apt-get install -y \
+ apt-utils \
+ wget \
+ tar \
+ xz-utils \
+ git \
+ build-essential \
+ curl \
+ unzip
+
+ DO flutter-ci+INSTALL_FLUTTER
+ DO rust-ci+INSTALL_RUST
+ DO rust-ci+INSTALL_TOOLS
+
+# Generated necessary files for running Flutter web.
+CODE_GENERATOR_WEB:
+ FUNCTION
+
+ RUN flutter_rust_bridge_codegen generate --default-external-library-loader-web-prefix=/assets/packages/catalyst_key_derivation/assets/js/
+ RUN flutter_rust_bridge_codegen build-web
+
+ RUN mkdir -p assets/js && cp -rf ./web/pkg/* assets/js/
+ # Don't want this gitignore file.
+ RUN rm -rf ./assets/js/.gitignore
diff --git a/earthly/rust/Earthfile b/earthly/rust/Earthfile
index 3ce1775e8..9d59251aa 100644
--- a/earthly/rust/Earthfile
+++ b/earthly/rust/Earthfile
@@ -112,7 +112,8 @@ COPY_TOOL:
FUNCTION
ARG --required tool
- COPY rust-tools+tool-$tool/$tool $CARGO_HOME/bin/$tool
+ ARG bin = $tool
+ COPY rust-tools+tool-$tool/$bin $CARGO_HOME/bin/$bin
# rust-base-plus-tools : Add all tools we use for rust builds to the base builder image.
@@ -360,4 +361,91 @@ REMOVE_SOURCE_FINGERPRINTS:
find . -maxdepth 1 -regex "\./$source_lib-[^-]+" -exec bash -c 'echo "deleting $(readlink -f {})"; rm -rf {}' \; ; \
done \
fi; \
- done;
\ No newline at end of file
+ done;
+
+# TODO(bkioshn): https://github.com/input-output-hk/catalyst-ci/issues/322
+# Installing Rust
+# Code reference from [rust1.81.0-slim-bookworm](https://github.com/rust-lang/docker-rust/blob/63f877a36f8ba9d9b4b35cd49df3327264510886/stable/bookworm/slim/Dockerfile)
+INSTALL_RUST:
+ FUNCTION
+
+ ARG TARGETARCH
+
+ ENV RUSTUP_HOME=/usr/local/rustup
+ ENV CARGO_HOME=/usr/local/cargo
+ ENV PATH=/usr/local/cargo/bin:$PATH
+ ENV RUST_VERSION=1.81.0
+
+ IF [ "$TARGETARCH" = "amd64" ]
+ LET PLATFORM = "x86_64-unknown-linux-gnu"
+ LET RUSTUP_SHA = "6aeece6993e902708983b209d04c0d1dbb14ebb405ddb87def578d41f920f56d"
+ ELSE IF [ "$TARGETARCH" = "arm64" ]
+ LET PLATFORM = "aarch64-unknown-linux-gnu"
+ LET RUSTUP_SHA = "1cffbf51e63e634c746f741de50649bbbcbd9dbe1de363c9ecef64e278dba2b2"
+ ELSE
+ RUN echo >&2 "unsupported architecture: ${TARGETARCH}"; exit 1
+ END
+
+ LET URL = "https://static.rust-lang.org/rustup/archive/1.27.1/${PLATFORM}/rustup-init"
+ RUN wget $URL && echo "${RUSTUP_SHA} *rustup-init" | sha256sum -c - \
+ && chmod +x rustup-init \
+ && ./rustup-init -y --no-modify-path --profile minimal --default-toolchain $RUST_VERSION --default-host $PLATFORM \
+ && rm rustup-init \
+ && chmod -R a+w $RUSTUP_HOME $CARGO_HOME \
+ && rustup --version \
+ && cargo --version \
+ && rustc --version \
+ && rm -rf /var/lib/apt/lists/*
+
+ # Make sure we have cargo.
+ RUN rustup component add cargo
+
+ # Make sure we have the clippy linter.
+ RUN rustup component add clippy
+
+ # Needed to generate code coverage.
+ RUN rustup component add llvm-tools-preview
+
+ # Install a nightly toolchain which matches.
+ RUN rustup toolchain install nightly --component miri --component rust-src --component rustfmt --component clippy --component cargo
+
+ # Ensure we have all the necessary targets
+ RUN rustup target add wasm32-unknown-unknown
+ RUN rustup target add wasm32-wasip1
+ # RUN rustup target add wasm32-wasip2 # wasm32-wasip2 not yet available in stable - Try again in 1.82
+ RUN rustup target add x86_64-unknown-linux-gnu
+ RUN rustup target add x86_64-apple-darwin
+ RUN rustup target add x86_64-pc-windows-gnu
+ RUN rustup target add aarch64-unknown-linux-gnu
+ RUN rustup target add aarch64-apple-darwin
+
+ RUN rustup target add wasm32-unknown-unknown --toolchain nightly
+ RUN rustup target add wasm32-wasip1 --toolchain nightly
+ RUN rustup target add wasm32-wasip2 --toolchain nightly
+ RUN rustup target add x86_64-unknown-linux-gnu --toolchain nightly
+ RUN rustup target add x86_64-apple-darwin --toolchain nightly
+ RUN rustup target add x86_64-pc-windows-gnu --toolchain nightly
+ RUN rustup target add aarch64-unknown-linux-gnu --toolchain nightly
+ RUN rustup target add aarch64-apple-darwin --toolchain nightly
+
+# Install necessary tools for Rust.
+INSTALL_TOOLS:
+ FUNCTION
+ # Install cargo-binstall so we can speed up tool installation.
+ DO rust-tools+CARGO_BINSTALL_INSTALL
+
+ DO +COPY_TOOL --tool="cargo-nextest"
+ DO +COPY_TOOL --tool="cargo-machete"
+ DO +COPY_TOOL --tool="refinery"
+ DO +COPY_TOOL --tool="cargo-deny"
+ DO +COPY_TOOL --tool="cargo-modules"
+ DO +COPY_TOOL --tool="cargo-depgraph"
+ DO +COPY_TOOL --tool="cargo-llvm-cov"
+ DO +COPY_TOOL --tool="wasm-tools"
+ DO +COPY_TOOL --tool="cargo-expand"
+ DO +COPY_TOOL --tool="wit-bindgen"
+ DO +COPY_TOOL --tool="cargo-sweep"
+ DO +COPY_TOOL --tool="cargo-component"
+ DO +COPY_TOOL --tool="wasm-pack"
+ DO +COPY_TOOL --tool="flutter-rust-bridge-codegen" --bin="flutter_rust_bridge_codegen"
+
\ No newline at end of file
diff --git a/earthly/rust/tools/Earthfile b/earthly/rust/tools/Earthfile
index 0d072a3f4..a24b19646 100644
--- a/earthly/rust/tools/Earthfile
+++ b/earthly/rust/tools/Earthfile
@@ -82,3 +82,6 @@ tool-cargo-component:
tool-wasm-pack:
DO +CARGO_BINSTALL --package=wasm-pack --version=0.13.0
+
+tool-flutter-rust-bridge-codegen:
+ DO +CARGO_BINSTALL --package=flutter_rust_bridge_codegen --version=2.5.1 --executable="flutter_rust_bridge_codegen"
diff --git a/examples/flutter/example/Earthfile b/examples/flutter/example/Earthfile
index 2b791cd6a..9f87dde07 100644
--- a/examples/flutter/example/Earthfile
+++ b/examples/flutter/example/Earthfile
@@ -1,6 +1,7 @@
VERSION 0.8
IMPORT ../../../earthly/flutter AS flutter-ci
+IMPORT ../../../earthly/flutter_rust_bridge AS flutter_rust_bridge
# To see more available functions, navigate to earthly/flutter/Earthfile.
@@ -39,3 +40,18 @@ build-web:
ARG --required SENTRY_DSN
DO flutter-ci+BUILD_WEB --WORKDIR=$WORKDIR --TARGET=$TARGET --SENTRY_DSN=$SENTRY_DSN
+
+# -----------------flutter_rust_bridge-----------------
+
+builder-frb:
+ FROM flutter_rust_bridge+builder
+ COPY . .
+
+# Generated necessary files for running Flutter web locally and save it locally.
+code-generator-web:
+ FROM +builder-frb
+ DO flutter_rust_bridge+CODE_GENERATOR_WEB
+
+ SAVE ARTIFACT ./assets/js AS LOCAL ./assets/js
+ SAVE ARTIFACT ./rust/src/frb_generated.rs AS LOCAL ./rust/src/frb_generated.rs
+ SAVE ARTIFACT ./lib/src AS LOCAL ./lib/src
\ No newline at end of file
diff --git a/examples/flutter/example/flutter_rust_bridge.yaml b/examples/flutter/example/flutter_rust_bridge.yaml
new file mode 100644
index 000000000..e15ed9167
--- /dev/null
+++ b/examples/flutter/example/flutter_rust_bridge.yaml
@@ -0,0 +1,3 @@
+rust_input: crate::api
+rust_root: rust/
+dart_output: lib/src/rust
\ No newline at end of file
diff --git a/examples/flutter/example/integration_test/simple_test.dart b/examples/flutter/example/integration_test/simple_test.dart
new file mode 100644
index 000000000..730333317
--- /dev/null
+++ b/examples/flutter/example/integration_test/simple_test.dart
@@ -0,0 +1,13 @@
+import 'package:flutter_test/flutter_test.dart';
+import 'package:example/main.dart';
+import 'package:example/src/rust/frb_generated.dart';
+import 'package:integration_test/integration_test.dart';
+
+void main() {
+ IntegrationTestWidgetsFlutterBinding.ensureInitialized();
+ setUpAll(() async => await RustLib.init());
+ testWidgets('Can call rust function', (WidgetTester tester) async {
+ await tester.pumpWidget(const MyApp());
+ expect(find.textContaining('Result: `Hello, Tom!`'), findsOneWidget);
+ });
+}
diff --git a/examples/flutter/example/lib/main.dart b/examples/flutter/example/lib/main.dart
index 8e9408912..0a542e87e 100644
--- a/examples/flutter/example/lib/main.dart
+++ b/examples/flutter/example/lib/main.dart
@@ -1,125 +1,25 @@
import 'package:flutter/material.dart';
+import 'package:example/src/rust/api/simple.dart';
+import 'package:example/src/rust/frb_generated.dart';
-void main() {
+Future main() async {
+ await RustLib.init();
runApp(const MyApp());
}
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 purple 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;
-
- @override
- State createState() => _MyHomePageState();
-}
-
-class _MyHomePageState extends State {
- 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: [
- const Text(
- 'You have pushed the button this many times:',
- ),
- Text(
- '$_counter',
- style: Theme.of(context).textTheme.headlineMedium,
- ),
- ],
+ home: Scaffold(
+ appBar: AppBar(title: const Text('flutter_rust_bridge quickstart')),
+ body: Center(
+ child: Text(
+ 'Action: Call Rust `greet("Tom")`\nResult: `${greet(name: "Tom")}`'),
),
),
- floatingActionButton: FloatingActionButton(
- onPressed: _incrementCounter,
- tooltip: 'Increment',
- child: const Icon(Icons.add),
- ), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
diff --git a/examples/flutter/example/lib/src/rust/api/simple.dart b/examples/flutter/example/lib/src/rust/api/simple.dart
new file mode 100644
index 000000000..7f096a3c9
--- /dev/null
+++ b/examples/flutter/example/lib/src/rust/api/simple.dart
@@ -0,0 +1,10 @@
+// This file is automatically generated, so please do not edit it.
+// @generated by `flutter_rust_bridge`@ 2.5.1.
+
+// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
+
+import '../frb_generated.dart';
+import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
+
+String greet({required String name}) =>
+ RustLib.instance.api.crateApiSimpleGreet(name: name);
diff --git a/examples/flutter/example/lib/src/rust/frb_generated.dart b/examples/flutter/example/lib/src/rust/frb_generated.dart
new file mode 100644
index 000000000..1178b437f
--- /dev/null
+++ b/examples/flutter/example/lib/src/rust/frb_generated.dart
@@ -0,0 +1,238 @@
+// This file is automatically generated, so please do not edit it.
+// @generated by `flutter_rust_bridge`@ 2.5.1.
+
+// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field
+
+import 'api/simple.dart';
+import 'dart:async';
+import 'dart:convert';
+import 'frb_generated.dart';
+import 'frb_generated.io.dart'
+ if (dart.library.js_interop) 'frb_generated.web.dart';
+import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
+
+/// Main entrypoint of the Rust API
+class RustLib extends BaseEntrypoint {
+ @internal
+ static final instance = RustLib._();
+
+ RustLib._();
+
+ /// Initialize flutter_rust_bridge
+ static Future init({
+ RustLibApi? api,
+ BaseHandler? handler,
+ ExternalLibrary? externalLibrary,
+ }) async {
+ await instance.initImpl(
+ api: api,
+ handler: handler,
+ externalLibrary: externalLibrary,
+ );
+ }
+
+ /// Initialize flutter_rust_bridge in mock mode.
+ /// No libraries for FFI are loaded.
+ static void initMock({
+ required RustLibApi api,
+ }) {
+ instance.initMockImpl(
+ api: api,
+ );
+ }
+
+ /// Dispose flutter_rust_bridge
+ ///
+ /// The call to this function is optional, since flutter_rust_bridge (and everything else)
+ /// is automatically disposed when the app stops.
+ static void dispose() => instance.disposeImpl();
+
+ @override
+ ApiImplConstructor get apiImplConstructor =>
+ RustLibApiImpl.new;
+
+ @override
+ WireConstructor get wireConstructor =>
+ RustLibWire.fromExternalLibrary;
+
+ @override
+ Future executeRustInitializers() async {
+ await api.crateApiSimpleInitApp();
+ }
+
+ @override
+ ExternalLibraryLoaderConfig get defaultExternalLibraryLoaderConfig =>
+ kDefaultExternalLibraryLoaderConfig;
+
+ @override
+ String get codegenVersion => '2.5.1';
+
+ @override
+ int get rustContentHash => -1918914929;
+
+ static const kDefaultExternalLibraryLoaderConfig =
+ ExternalLibraryLoaderConfig(
+ stem: 'rust_lib_example',
+ ioDirectory: 'rust/target/release/',
+ webPrefix: '/assets/packages/catalyst_key_derivation/assets/js/',
+ );
+}
+
+abstract class RustLibApi extends BaseApi {
+ String crateApiSimpleGreet({required String name});
+
+ Future crateApiSimpleInitApp();
+}
+
+class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
+ RustLibApiImpl({
+ required super.handler,
+ required super.wire,
+ required super.generalizedFrbRustBinding,
+ required super.portManager,
+ });
+
+ @override
+ String crateApiSimpleGreet({required String name}) {
+ return handler.executeSync(SyncTask(
+ callFfi: () {
+ final serializer = SseSerializer(generalizedFrbRustBinding);
+ sse_encode_String(name, serializer);
+ return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 1)!;
+ },
+ codec: SseCodec(
+ decodeSuccessData: sse_decode_String,
+ decodeErrorData: null,
+ ),
+ constMeta: kCrateApiSimpleGreetConstMeta,
+ argValues: [name],
+ apiImpl: this,
+ ));
+ }
+
+ TaskConstMeta get kCrateApiSimpleGreetConstMeta => const TaskConstMeta(
+ debugName: "greet",
+ argNames: ["name"],
+ );
+
+ @override
+ Future crateApiSimpleInitApp() {
+ return handler.executeNormal(NormalTask(
+ callFfi: (port_) {
+ final serializer = SseSerializer(generalizedFrbRustBinding);
+ pdeCallFfi(generalizedFrbRustBinding, serializer,
+ funcId: 2, port: port_);
+ },
+ codec: SseCodec(
+ decodeSuccessData: sse_decode_unit,
+ decodeErrorData: null,
+ ),
+ constMeta: kCrateApiSimpleInitAppConstMeta,
+ argValues: [],
+ apiImpl: this,
+ ));
+ }
+
+ TaskConstMeta get kCrateApiSimpleInitAppConstMeta => const TaskConstMeta(
+ debugName: "init_app",
+ argNames: [],
+ );
+
+ @protected
+ String dco_decode_String(dynamic raw) {
+ // Codec=Dco (DartCObject based), see doc to use other codecs
+ return raw as String;
+ }
+
+ @protected
+ Uint8List dco_decode_list_prim_u_8_strict(dynamic raw) {
+ // Codec=Dco (DartCObject based), see doc to use other codecs
+ return raw as Uint8List;
+ }
+
+ @protected
+ int dco_decode_u_8(dynamic raw) {
+ // Codec=Dco (DartCObject based), see doc to use other codecs
+ return raw as int;
+ }
+
+ @protected
+ void dco_decode_unit(dynamic raw) {
+ // Codec=Dco (DartCObject based), see doc to use other codecs
+ return;
+ }
+
+ @protected
+ String sse_decode_String(SseDeserializer deserializer) {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ var inner = sse_decode_list_prim_u_8_strict(deserializer);
+ return utf8.decoder.convert(inner);
+ }
+
+ @protected
+ Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer) {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ var len_ = sse_decode_i_32(deserializer);
+ return deserializer.buffer.getUint8List(len_);
+ }
+
+ @protected
+ int sse_decode_u_8(SseDeserializer deserializer) {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ return deserializer.buffer.getUint8();
+ }
+
+ @protected
+ void sse_decode_unit(SseDeserializer deserializer) {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ }
+
+ @protected
+ int sse_decode_i_32(SseDeserializer deserializer) {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ return deserializer.buffer.getInt32();
+ }
+
+ @protected
+ bool sse_decode_bool(SseDeserializer deserializer) {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ return deserializer.buffer.getUint8() != 0;
+ }
+
+ @protected
+ void sse_encode_String(String self, SseSerializer serializer) {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ sse_encode_list_prim_u_8_strict(utf8.encoder.convert(self), serializer);
+ }
+
+ @protected
+ void sse_encode_list_prim_u_8_strict(
+ Uint8List self, SseSerializer serializer) {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ sse_encode_i_32(self.length, serializer);
+ serializer.buffer.putUint8List(self);
+ }
+
+ @protected
+ void sse_encode_u_8(int self, SseSerializer serializer) {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ serializer.buffer.putUint8(self);
+ }
+
+ @protected
+ void sse_encode_unit(void self, SseSerializer serializer) {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ }
+
+ @protected
+ void sse_encode_i_32(int self, SseSerializer serializer) {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ serializer.buffer.putInt32(self);
+ }
+
+ @protected
+ void sse_encode_bool(bool self, SseSerializer serializer) {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ serializer.buffer.putUint8(self ? 1 : 0);
+ }
+}
diff --git a/examples/flutter/example/lib/src/rust/frb_generated.io.dart b/examples/flutter/example/lib/src/rust/frb_generated.io.dart
new file mode 100644
index 000000000..f0efd0130
--- /dev/null
+++ b/examples/flutter/example/lib/src/rust/frb_generated.io.dart
@@ -0,0 +1,84 @@
+// This file is automatically generated, so please do not edit it.
+// @generated by `flutter_rust_bridge`@ 2.5.1.
+
+// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field
+
+import 'api/simple.dart';
+import 'dart:async';
+import 'dart:convert';
+import 'dart:ffi' as ffi;
+import 'frb_generated.dart';
+import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_io.dart';
+
+abstract class RustLibApiImplPlatform extends BaseApiImpl {
+ RustLibApiImplPlatform({
+ required super.handler,
+ required super.wire,
+ required super.generalizedFrbRustBinding,
+ required super.portManager,
+ });
+
+ @protected
+ String dco_decode_String(dynamic raw);
+
+ @protected
+ Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
+
+ @protected
+ int dco_decode_u_8(dynamic raw);
+
+ @protected
+ void dco_decode_unit(dynamic raw);
+
+ @protected
+ String sse_decode_String(SseDeserializer deserializer);
+
+ @protected
+ Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
+
+ @protected
+ int sse_decode_u_8(SseDeserializer deserializer);
+
+ @protected
+ void sse_decode_unit(SseDeserializer deserializer);
+
+ @protected
+ int sse_decode_i_32(SseDeserializer deserializer);
+
+ @protected
+ bool sse_decode_bool(SseDeserializer deserializer);
+
+ @protected
+ void sse_encode_String(String self, SseSerializer serializer);
+
+ @protected
+ void sse_encode_list_prim_u_8_strict(
+ Uint8List self, SseSerializer serializer);
+
+ @protected
+ void sse_encode_u_8(int self, SseSerializer serializer);
+
+ @protected
+ void sse_encode_unit(void self, SseSerializer serializer);
+
+ @protected
+ void sse_encode_i_32(int self, SseSerializer serializer);
+
+ @protected
+ void sse_encode_bool(bool self, SseSerializer serializer);
+}
+
+// Section: wire_class
+
+class RustLibWire implements BaseWire {
+ factory RustLibWire.fromExternalLibrary(ExternalLibrary lib) =>
+ RustLibWire(lib.ffiDynamicLibrary);
+
+ /// Holds the symbol lookup function.
+ final ffi.Pointer Function(String symbolName)
+ _lookup;
+
+ /// The symbols are looked up in [dynamicLibrary].
+ RustLibWire(ffi.DynamicLibrary dynamicLibrary)
+ : _lookup = dynamicLibrary.lookup;
+}
diff --git a/examples/flutter/example/lib/src/rust/frb_generated.web.dart b/examples/flutter/example/lib/src/rust/frb_generated.web.dart
new file mode 100644
index 000000000..2b2640718
--- /dev/null
+++ b/examples/flutter/example/lib/src/rust/frb_generated.web.dart
@@ -0,0 +1,84 @@
+// This file is automatically generated, so please do not edit it.
+// @generated by `flutter_rust_bridge`@ 2.5.1.
+
+// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field
+
+// Static analysis wrongly picks the IO variant, thus ignore this
+// ignore_for_file: argument_type_not_assignable
+
+import 'api/simple.dart';
+import 'dart:async';
+import 'dart:convert';
+import 'frb_generated.dart';
+import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_web.dart';
+
+abstract class RustLibApiImplPlatform extends BaseApiImpl {
+ RustLibApiImplPlatform({
+ required super.handler,
+ required super.wire,
+ required super.generalizedFrbRustBinding,
+ required super.portManager,
+ });
+
+ @protected
+ String dco_decode_String(dynamic raw);
+
+ @protected
+ Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
+
+ @protected
+ int dco_decode_u_8(dynamic raw);
+
+ @protected
+ void dco_decode_unit(dynamic raw);
+
+ @protected
+ String sse_decode_String(SseDeserializer deserializer);
+
+ @protected
+ Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
+
+ @protected
+ int sse_decode_u_8(SseDeserializer deserializer);
+
+ @protected
+ void sse_decode_unit(SseDeserializer deserializer);
+
+ @protected
+ int sse_decode_i_32(SseDeserializer deserializer);
+
+ @protected
+ bool sse_decode_bool(SseDeserializer deserializer);
+
+ @protected
+ void sse_encode_String(String self, SseSerializer serializer);
+
+ @protected
+ void sse_encode_list_prim_u_8_strict(
+ Uint8List self, SseSerializer serializer);
+
+ @protected
+ void sse_encode_u_8(int self, SseSerializer serializer);
+
+ @protected
+ void sse_encode_unit(void self, SseSerializer serializer);
+
+ @protected
+ void sse_encode_i_32(int self, SseSerializer serializer);
+
+ @protected
+ void sse_encode_bool(bool self, SseSerializer serializer);
+}
+
+// Section: wire_class
+
+class RustLibWire implements BaseWire {
+ RustLibWire.fromExternalLibrary(ExternalLibrary lib);
+}
+
+@JS('wasm_bindgen')
+external RustLibWasmModule get wasmModule;
+
+@JS()
+@anonymous
+extension type RustLibWasmModule._(JSObject _) implements JSObject {}
diff --git a/examples/flutter/example/linux/flutter/generated_plugins.cmake b/examples/flutter/example/linux/flutter/generated_plugins.cmake
index 2e1de87a7..65773fc1e 100644
--- a/examples/flutter/example/linux/flutter/generated_plugins.cmake
+++ b/examples/flutter/example/linux/flutter/generated_plugins.cmake
@@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
+ rust_lib_example
)
set(PLUGIN_BUNDLED_LIBRARIES)
diff --git a/examples/flutter/example/pubspec.lock b/examples/flutter/example/pubspec.lock
index 2223dd9f8..6e6c4917a 100644
--- a/examples/flutter/example/pubspec.lock
+++ b/examples/flutter/example/pubspec.lock
@@ -1,6 +1,14 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
+ args:
+ dependency: transitive
+ description:
+ name: args
+ sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.6.0"
async:
dependency: transitive
description:
@@ -17,6 +25,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
+ build_cli_annotations:
+ dependency: transitive
+ description:
+ name: build_cli_annotations
+ sha256: b59d2769769efd6c9ff6d4c4cede0be115a566afc591705c2040b707534b1172
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.0"
characters:
dependency: transitive
description:
@@ -57,11 +73,24 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.1"
+ file:
+ dependency: transitive
+ description:
+ name: file
+ sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.0.0"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
+ flutter_driver:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
flutter_lints:
dependency: "direct dev"
description:
@@ -70,11 +99,29 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.2"
+ flutter_rust_bridge:
+ dependency: "direct main"
+ description:
+ name: flutter_rust_bridge
+ sha256: "5fe868d3cb8cbc4d83091748552e03f00ccfa41b8e44691bc382611f831d5f8b"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.5.1"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
+ fuchsia_remote_debug_protocol:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ integration_test:
+ dependency: "direct dev"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
leak_tracker:
dependency: transitive
description:
@@ -139,6 +186,37 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.9.0"
+ platform:
+ dependency: transitive
+ description:
+ name: platform
+ sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.5"
+ plugin_platform_interface:
+ dependency: transitive
+ description:
+ name: plugin_platform_interface
+ sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.8"
+ process:
+ dependency: transitive
+ description:
+ name: process
+ sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32"
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.0.2"
+ rust_lib_example:
+ dependency: "direct main"
+ description:
+ path: rust_builder
+ relative: true
+ source: path
+ version: "0.0.1"
sky_engine:
dependency: transitive
description: flutter
@@ -176,6 +254,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.0"
+ sync_http:
+ dependency: transitive
+ description:
+ name: sync_http
+ sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.3.1"
term_glyph:
dependency: transitive
description:
@@ -208,6 +294,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "14.2.5"
+ web:
+ dependency: transitive
+ description:
+ name: web
+ sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.0"
+ webdriver:
+ dependency: transitive
+ description:
+ name: webdriver
+ sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.3"
sdks:
dart: ">=3.5.0 <4.0.0"
flutter: ">=3.24.1"
diff --git a/examples/flutter/example/pubspec.yaml b/examples/flutter/example/pubspec.yaml
index 91c8db4b8..1dbbd418e 100644
--- a/examples/flutter/example/pubspec.yaml
+++ b/examples/flutter/example/pubspec.yaml
@@ -12,11 +12,16 @@ dependencies:
sdk: flutter
cupertino_icons: ^1.0.6
+ rust_lib_example:
+ path: rust_builder
+ flutter_rust_bridge: 2.5.1
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.0
+ integration_test:
+ sdk: flutter
flutter:
uses-material-design: true
\ No newline at end of file
diff --git a/examples/flutter/example/rust/.gitignore b/examples/flutter/example/rust/.gitignore
new file mode 100644
index 000000000..ea8c4bf7f
--- /dev/null
+++ b/examples/flutter/example/rust/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/examples/flutter/example/rust/Cargo.toml b/examples/flutter/example/rust/Cargo.toml
new file mode 100644
index 000000000..02114d4e0
--- /dev/null
+++ b/examples/flutter/example/rust/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "rust_lib_example"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib", "staticlib"]
+
+[dependencies]
+flutter_rust_bridge = "=2.5.1"
diff --git a/examples/flutter/example/rust/src/api/mod.rs b/examples/flutter/example/rust/src/api/mod.rs
new file mode 100644
index 000000000..b252f36bf
--- /dev/null
+++ b/examples/flutter/example/rust/src/api/mod.rs
@@ -0,0 +1 @@
+pub mod simple;
diff --git a/examples/flutter/example/rust/src/api/simple.rs b/examples/flutter/example/rust/src/api/simple.rs
new file mode 100644
index 000000000..4360c82ae
--- /dev/null
+++ b/examples/flutter/example/rust/src/api/simple.rs
@@ -0,0 +1,10 @@
+#[flutter_rust_bridge::frb(sync)] // Synchronous mode for simplicity of the demo
+pub fn greet(name: String) -> String {
+ format!("Hello, {name}!")
+}
+
+#[flutter_rust_bridge::frb(init)]
+pub fn init_app() {
+ // Default utilities - feel free to customize
+ flutter_rust_bridge::setup_default_user_utils();
+}
diff --git a/examples/flutter/example/rust/src/frb_generated.rs b/examples/flutter/example/rust/src/frb_generated.rs
new file mode 100644
index 000000000..c84309268
--- /dev/null
+++ b/examples/flutter/example/rust/src/frb_generated.rs
@@ -0,0 +1,276 @@
+// This file is automatically generated, so please do not edit it.
+// @generated by `flutter_rust_bridge`@ 2.5.1.
+
+#![allow(
+ non_camel_case_types,
+ unused,
+ non_snake_case,
+ clippy::needless_return,
+ clippy::redundant_closure_call,
+ clippy::redundant_closure,
+ clippy::useless_conversion,
+ clippy::unit_arg,
+ clippy::unused_unit,
+ clippy::double_parens,
+ clippy::let_and_return,
+ clippy::too_many_arguments,
+ clippy::match_single_binding,
+ clippy::clone_on_copy,
+ clippy::let_unit_value,
+ clippy::deref_addrof,
+ clippy::explicit_auto_deref,
+ clippy::borrow_deref_ref,
+ clippy::needless_borrow
+)]
+
+// Section: imports
+
+use flutter_rust_bridge::for_generated::byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt};
+use flutter_rust_bridge::for_generated::{transform_result_dco, Lifetimeable, Lockable};
+use flutter_rust_bridge::{Handler, IntoIntoDart};
+
+// Section: boilerplate
+
+flutter_rust_bridge::frb_generated_boilerplate!(
+ default_stream_sink_codec = SseCodec,
+ default_rust_opaque = RustOpaqueMoi,
+ default_rust_auto_opaque = RustAutoOpaqueMoi,
+);
+pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.5.1";
+pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -1918914929;
+
+// Section: executor
+
+flutter_rust_bridge::frb_generated_default_handler!();
+
+// Section: wire_funcs
+
+fn wire__crate__api__simple__greet_impl(
+ ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
+ rust_vec_len_: i32,
+ data_len_: i32,
+) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse {
+ FLUTTER_RUST_BRIDGE_HANDLER.wrap_sync::(
+ flutter_rust_bridge::for_generated::TaskInfo {
+ debug_name: "greet",
+ port: None,
+ mode: flutter_rust_bridge::for_generated::FfiCallMode::Sync,
+ },
+ move || {
+ let message = unsafe {
+ flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(
+ ptr_,
+ rust_vec_len_,
+ data_len_,
+ )
+ };
+ let mut deserializer =
+ flutter_rust_bridge::for_generated::SseDeserializer::new(message);
+ let api_name = ::sse_decode(&mut deserializer);
+ deserializer.end();
+ transform_result_sse::<_, ()>((move || {
+ let output_ok = Result::<_, ()>::Ok(crate::api::simple::greet(api_name))?;
+ Ok(output_ok)
+ })())
+ },
+ )
+}
+fn wire__crate__api__simple__init_app_impl(
+ port_: flutter_rust_bridge::for_generated::MessagePort,
+ ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
+ rust_vec_len_: i32,
+ data_len_: i32,
+) {
+ FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::(
+ flutter_rust_bridge::for_generated::TaskInfo {
+ debug_name: "init_app",
+ port: Some(port_),
+ mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
+ },
+ move || {
+ let message = unsafe {
+ flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(
+ ptr_,
+ rust_vec_len_,
+ data_len_,
+ )
+ };
+ let mut deserializer =
+ flutter_rust_bridge::for_generated::SseDeserializer::new(message);
+ deserializer.end();
+ move |context| {
+ transform_result_sse::<_, ()>((move || {
+ let output_ok = Result::<_, ()>::Ok({
+ crate::api::simple::init_app();
+ })?;
+ Ok(output_ok)
+ })())
+ }
+ },
+ )
+}
+
+// Section: dart2rust
+
+impl SseDecode for String {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
+ let mut inner = >::sse_decode(deserializer);
+ return String::from_utf8(inner).unwrap();
+ }
+}
+
+impl SseDecode for Vec {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
+ let mut len_ = ::sse_decode(deserializer);
+ let mut ans_ = vec![];
+ for idx_ in 0..len_ {
+ ans_.push(::sse_decode(deserializer));
+ }
+ return ans_;
+ }
+}
+
+impl SseDecode for u8 {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
+ deserializer.cursor.read_u8().unwrap()
+ }
+}
+
+impl SseDecode for () {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {}
+}
+
+impl SseDecode for i32 {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
+ deserializer.cursor.read_i32::().unwrap()
+ }
+}
+
+impl SseDecode for bool {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
+ deserializer.cursor.read_u8().unwrap() != 0
+ }
+}
+
+fn pde_ffi_dispatcher_primary_impl(
+ func_id: i32,
+ port: flutter_rust_bridge::for_generated::MessagePort,
+ ptr: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
+ rust_vec_len: i32,
+ data_len: i32,
+) {
+ // Codec=Pde (Serialization + dispatch), see doc to use other codecs
+ match func_id {
+ 2 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len),
+ _ => unreachable!(),
+ }
+}
+
+fn pde_ffi_dispatcher_sync_impl(
+ func_id: i32,
+ ptr: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
+ rust_vec_len: i32,
+ data_len: i32,
+) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse {
+ // Codec=Pde (Serialization + dispatch), see doc to use other codecs
+ match func_id {
+ 1 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len),
+ _ => unreachable!(),
+ }
+}
+
+// Section: rust2dart
+
+impl SseEncode for String {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
+ >::sse_encode(self.into_bytes(), serializer);
+ }
+}
+
+impl SseEncode for Vec {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
+ ::sse_encode(self.len() as _, serializer);
+ for item in self {
+ ::sse_encode(item, serializer);
+ }
+ }
+}
+
+impl SseEncode for u8 {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
+ serializer.cursor.write_u8(self).unwrap();
+ }
+}
+
+impl SseEncode for () {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {}
+}
+
+impl SseEncode for i32 {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
+ serializer.cursor.write_i32::(self).unwrap();
+ }
+}
+
+impl SseEncode for bool {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
+ serializer.cursor.write_u8(self as _).unwrap();
+ }
+}
+
+#[cfg(not(target_family = "wasm"))]
+mod io {
+ // This file is automatically generated, so please do not edit it.
+ // @generated by `flutter_rust_bridge`@ 2.5.1.
+
+ // Section: imports
+
+ use super::*;
+ use flutter_rust_bridge::for_generated::byteorder::{
+ NativeEndian, ReadBytesExt, WriteBytesExt,
+ };
+ use flutter_rust_bridge::for_generated::{transform_result_dco, Lifetimeable, Lockable};
+ use flutter_rust_bridge::{Handler, IntoIntoDart};
+
+ // Section: boilerplate
+
+ flutter_rust_bridge::frb_generated_boilerplate_io!();
+}
+#[cfg(not(target_family = "wasm"))]
+pub use io::*;
+
+/// cbindgen:ignore
+#[cfg(target_family = "wasm")]
+mod web {
+ // This file is automatically generated, so please do not edit it.
+ // @generated by `flutter_rust_bridge`@ 2.5.1.
+
+ // Section: imports
+
+ use super::*;
+ use flutter_rust_bridge::for_generated::byteorder::{
+ NativeEndian, ReadBytesExt, WriteBytesExt,
+ };
+ use flutter_rust_bridge::for_generated::wasm_bindgen;
+ use flutter_rust_bridge::for_generated::wasm_bindgen::prelude::*;
+ use flutter_rust_bridge::for_generated::{transform_result_dco, Lifetimeable, Lockable};
+ use flutter_rust_bridge::{Handler, IntoIntoDart};
+
+ // Section: boilerplate
+
+ flutter_rust_bridge::frb_generated_boilerplate_web!();
+}
+#[cfg(target_family = "wasm")]
+pub use web::*;
diff --git a/examples/flutter/example/rust/src/lib.rs b/examples/flutter/example/rust/src/lib.rs
new file mode 100644
index 000000000..cbb071f8b
--- /dev/null
+++ b/examples/flutter/example/rust/src/lib.rs
@@ -0,0 +1,2 @@
+pub mod api;
+mod frb_generated;
diff --git a/examples/flutter/example/rust_builder/.gitignore b/examples/flutter/example/rust_builder/.gitignore
new file mode 100644
index 000000000..ac5aa9893
--- /dev/null
+++ b/examples/flutter/example/rust_builder/.gitignore
@@ -0,0 +1,29 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
+/pubspec.lock
+**/doc/api/
+.dart_tool/
+build/
diff --git a/examples/flutter/example/rust_builder/README.md b/examples/flutter/example/rust_builder/README.md
new file mode 100644
index 000000000..04b641243
--- /dev/null
+++ b/examples/flutter/example/rust_builder/README.md
@@ -0,0 +1,2 @@
+
+Please ignore this folder, which is just glue to build Rust with Flutter.
diff --git a/examples/flutter/example/rust_builder/android/.gitignore b/examples/flutter/example/rust_builder/android/.gitignore
new file mode 100644
index 000000000..161bdcdaf
--- /dev/null
+++ b/examples/flutter/example/rust_builder/android/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.cxx
diff --git a/examples/flutter/example/rust_builder/android/build.gradle b/examples/flutter/example/rust_builder/android/build.gradle
new file mode 100644
index 000000000..f5fb3a072
--- /dev/null
+++ b/examples/flutter/example/rust_builder/android/build.gradle
@@ -0,0 +1,56 @@
+// The Android Gradle Plugin builds the native code with the Android NDK.
+
+group 'com.flutter_rust_bridge.rust_lib_example'
+version '1.0'
+
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ // The Android Gradle Plugin knows how to build native code with the NDK.
+ classpath 'com.android.tools.build:gradle:7.3.0'
+ }
+}
+
+rootProject.allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+apply plugin: 'com.android.library'
+
+android {
+ if (project.android.hasProperty("namespace")) {
+ namespace 'com.flutter_rust_bridge.rust_lib_example'
+ }
+
+ // Bumping the plugin compileSdkVersion requires all clients of this plugin
+ // to bump the version in their app.
+ compileSdkVersion 33
+
+ // Use the NDK version
+ // declared in /android/app/build.gradle file of the Flutter project.
+ // Replace it with a version number if this plugin requires a specfic NDK version.
+ // (e.g. ndkVersion "23.1.7779620")
+ ndkVersion android.ndkVersion
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ defaultConfig {
+ minSdkVersion 19
+ }
+}
+
+apply from: "../cargokit/gradle/plugin.gradle"
+cargokit {
+ manifestDir = "../../rust"
+ libname = "rust_lib_example"
+}
diff --git a/examples/flutter/example/rust_builder/android/settings.gradle b/examples/flutter/example/rust_builder/android/settings.gradle
new file mode 100644
index 000000000..6c763aa7a
--- /dev/null
+++ b/examples/flutter/example/rust_builder/android/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'rust_lib_example'
diff --git a/examples/flutter/example/rust_builder/android/src/main/AndroidManifest.xml b/examples/flutter/example/rust_builder/android/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..b7b7f669b
--- /dev/null
+++ b/examples/flutter/example/rust_builder/android/src/main/AndroidManifest.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/examples/flutter/example/rust_builder/cargokit/.gitignore b/examples/flutter/example/rust_builder/cargokit/.gitignore
new file mode 100644
index 000000000..cf7bb868c
--- /dev/null
+++ b/examples/flutter/example/rust_builder/cargokit/.gitignore
@@ -0,0 +1,4 @@
+target
+.dart_tool
+*.iml
+!pubspec.lock
diff --git a/examples/flutter/example/rust_builder/cargokit/LICENSE b/examples/flutter/example/rust_builder/cargokit/LICENSE
new file mode 100644
index 000000000..d33a5fea5
--- /dev/null
+++ b/examples/flutter/example/rust_builder/cargokit/LICENSE
@@ -0,0 +1,42 @@
+/// This is copied from Cargokit (which is the official way to use it currently)
+/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
+
+Copyright 2022 Matej Knopp
+
+================================================================================
+
+MIT LICENSE
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+================================================================================
+
+APACHE LICENSE, VERSION 2.0
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
diff --git a/examples/flutter/example/rust_builder/cargokit/README b/examples/flutter/example/rust_builder/cargokit/README
new file mode 100644
index 000000000..398474dbc
--- /dev/null
+++ b/examples/flutter/example/rust_builder/cargokit/README
@@ -0,0 +1,11 @@
+/// This is copied from Cargokit (which is the official way to use it currently)
+/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
+
+Experimental repository to provide glue for seamlessly integrating cargo build
+with flutter plugins and packages.
+
+See https://matejknopp.com/post/flutter_plugin_in_rust_with_no_prebuilt_binaries/
+for a tutorial on how to use Cargokit.
+
+Example plugin available at https://github.com/irondash/hello_rust_ffi_plugin.
+
diff --git a/examples/flutter/example/rust_builder/cargokit/build_pod b/examples/flutter/example/rust_builder/cargokit/build_pod
new file mode 100755
index 000000000..98d88d363
--- /dev/null
+++ b/examples/flutter/example/rust_builder/cargokit/build_pod
@@ -0,0 +1,60 @@
+# Delete this line to use the file
+
+!/bin/sh
+set -e
+
+BASEDIR=$(dirname "$0")
+
+# Workaround for https://github.com/dart-lang/pub/issues/4010
+BASEDIR=$(cd "$BASEDIR" ; pwd -P)
+
+# Remove XCode SDK from path. Otherwise this breaks tool compilation when building iOS project
+NEW_PATH=`echo $PATH | tr ":" "\n" | grep -v "Contents/Developer/" | tr "\n" ":"`
+
+export PATH=${NEW_PATH%?} # remove trailing :
+
+env
+
+# Platform name (macosx, iphoneos, iphonesimulator)
+export CARGOKIT_DARWIN_PLATFORM_NAME=$PLATFORM_NAME
+
+# Arctive architectures (arm64, armv7, x86_64), space separated.
+export CARGOKIT_DARWIN_ARCHS=$ARCHS
+
+# Current build configuration (Debug, Release)
+export CARGOKIT_CONFIGURATION=$CONFIGURATION
+
+# Path to directory containing Cargo.toml.
+export CARGOKIT_MANIFEST_DIR=$PODS_TARGET_SRCROOT/$1
+
+# Temporary directory for build artifacts.
+export CARGOKIT_TARGET_TEMP_DIR=$TARGET_TEMP_DIR
+
+# Output directory for final artifacts.
+export CARGOKIT_OUTPUT_DIR=$PODS_CONFIGURATION_BUILD_DIR/$PRODUCT_NAME
+
+# Directory to store built tool artifacts.
+export CARGOKIT_TOOL_TEMP_DIR=$TARGET_TEMP_DIR/build_tool
+
+# Directory inside root project. Not necessarily the top level directory of root project.
+export CARGOKIT_ROOT_PROJECT_DIR=$SRCROOT
+
+FLUTTER_EXPORT_BUILD_ENVIRONMENT=(
+ "$PODS_ROOT/../Flutter/ephemeral/flutter_export_environment.sh" # macOS
+ "$PODS_ROOT/../Flutter/flutter_export_environment.sh" # iOS
+)
+
+for path in "${FLUTTER_EXPORT_BUILD_ENVIRONMENT[@]}"
+do
+ if [[ -f "$path" ]]; then
+ source "$path"
+ fi
+done
+
+sh "$BASEDIR/run_build_tool.sh" build-pod "$@"
+
+# Make a symlink from built framework to phony file, which will be used as input to
+# build script. This should force rebuild (podspec currently doesn't support alwaysOutOfDate
+# attribute on custom build phase)
+ln -fs "$OBJROOT/XCBuildData/build.db" "${BUILT_PRODUCTS_DIR}/cargokit_phony"
+ln -fs "${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}" "${BUILT_PRODUCTS_DIR}/cargokit_phony_out"
diff --git a/examples/flutter/example/rust_builder/cargokit/build_tool/README.md b/examples/flutter/example/rust_builder/cargokit/build_tool/README.md
new file mode 100644
index 000000000..7da957870
--- /dev/null
+++ b/examples/flutter/example/rust_builder/cargokit/build_tool/README.md
@@ -0,0 +1,6 @@
+
+/// This is copied from Cargokit (which is the official way to use it currently)
+/// Details:
+
+A sample command-line application with an entrypoint in `bin/`, library code
+in `lib/`, and example unit test in `test/`.
diff --git a/examples/flutter/example/rust_builder/cargokit/build_tool/analysis_options.yaml b/examples/flutter/example/rust_builder/cargokit/build_tool/analysis_options.yaml
new file mode 100644
index 000000000..0e16a8b09
--- /dev/null
+++ b/examples/flutter/example/rust_builder/cargokit/build_tool/analysis_options.yaml
@@ -0,0 +1,34 @@
+# This is copied from Cargokit (which is the official way to use it currently)
+# Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
+
+# This file configures the static analysis results for your project (errors,
+# warnings, and lints).
+#
+# This enables the 'recommended' set of lints from `package:lints`.
+# This set helps identify many issues that may lead to problems when running
+# or consuming Dart code, and enforces writing Dart using a single, idiomatic
+# style and format.
+#
+# If you want a smaller set of lints you can change this to specify
+# 'package:lints/core.yaml'. These are just the most critical lints
+# (the recommended set includes the core lints).
+# The core lints are also what is used by pub.dev for scoring packages.
+
+include: package:lints/recommended.yaml
+
+# Uncomment the following section to specify additional rules.
+
+linter:
+ rules:
+ - prefer_relative_imports
+ - directives_ordering
+
+# analyzer:
+# exclude:
+# - path/to/excluded/files/**
+
+# For more information about the core and recommended set of lints, see
+# https://dart.dev/go/core-lints
+
+# For additional information about configuring this file, see
+# https://dart.dev/guides/language/analysis-options
diff --git a/examples/flutter/example/rust_builder/cargokit/build_tool/lib/build_tool.dart b/examples/flutter/example/rust_builder/cargokit/build_tool/lib/build_tool.dart
new file mode 100644
index 000000000..7c1bb750a
--- /dev/null
+++ b/examples/flutter/example/rust_builder/cargokit/build_tool/lib/build_tool.dart
@@ -0,0 +1,8 @@
+/// This is copied from Cargokit (which is the official way to use it currently)
+/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
+
+import 'src/build_tool.dart' as build_tool;
+
+Future runMain(List args) async {
+ return build_tool.runMain(args);
+}
diff --git a/examples/flutter/example/rust_builder/cargokit/build_tool/lib/src/android_environment.dart b/examples/flutter/example/rust_builder/cargokit/build_tool/lib/src/android_environment.dart
new file mode 100644
index 000000000..15fc9eeda
--- /dev/null
+++ b/examples/flutter/example/rust_builder/cargokit/build_tool/lib/src/android_environment.dart
@@ -0,0 +1,195 @@
+/// This is copied from Cargokit (which is the official way to use it currently)
+/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
+
+import 'dart:io';
+import 'dart:isolate';
+import 'dart:math' as math;
+
+import 'package:collection/collection.dart';
+import 'package:path/path.dart' as path;
+import 'package:version/version.dart';
+
+import 'target.dart';
+import 'util.dart';
+
+class AndroidEnvironment {
+ AndroidEnvironment({
+ required this.sdkPath,
+ required this.ndkVersion,
+ required this.minSdkVersion,
+ required this.targetTempDir,
+ required this.target,
+ });
+
+ static void clangLinkerWrapper(List args) {
+ final clang = Platform.environment['_CARGOKIT_NDK_LINK_CLANG'];
+ if (clang == null) {
+ throw Exception(
+ "cargo-ndk rustc linker: didn't find _CARGOKIT_NDK_LINK_CLANG env var");
+ }
+ final target = Platform.environment['_CARGOKIT_NDK_LINK_TARGET'];
+ if (target == null) {
+ throw Exception(
+ "cargo-ndk rustc linker: didn't find _CARGOKIT_NDK_LINK_TARGET env var");
+ }
+
+ runCommand(clang, [
+ target,
+ ...args,
+ ]);
+ }
+
+ /// Full path to Android SDK.
+ final String sdkPath;
+
+ /// Full version of Android NDK.
+ final String ndkVersion;
+
+ /// Minimum supported SDK version.
+ final int minSdkVersion;
+
+ /// Target directory for build artifacts.
+ final String targetTempDir;
+
+ /// Target being built.
+ final Target target;
+
+ bool ndkIsInstalled() {
+ final ndkPath = path.join(sdkPath, 'ndk', ndkVersion);
+ final ndkPackageXml = File(path.join(ndkPath, 'package.xml'));
+ return ndkPackageXml.existsSync();
+ }
+
+ void installNdk({
+ required String javaHome,
+ }) {
+ final sdkManagerExtension = Platform.isWindows ? '.bat' : '';
+ final sdkManager = path.join(
+ sdkPath,
+ 'cmdline-tools',
+ 'latest',
+ 'bin',
+ 'sdkmanager$sdkManagerExtension',
+ );
+
+ log.info('Installing NDK $ndkVersion');
+ runCommand(sdkManager, [
+ '--install',
+ 'ndk;$ndkVersion',
+ ], environment: {
+ 'JAVA_HOME': javaHome,
+ });
+ }
+
+ Future