Turn Go service APIs into type-safe Dart and Flutter interfaces with generated bindings, a stable FFI ABI, and no Flutter runtime dependency in the generated Dart code.
It covers installation, configuration, generated output, serialization, type mapping, directives,
structs, interfaces, streams, callbacks, return values, and error handling.
What it does
Write ordinary Go code and let flutter_go_bridge_codegen produce the bridge:
Go package
│
│ go/packages + go/types
▼
flutter_go_bridge_codegen
├── bridge_generated.go
└── mirrored Dart API tree
└── bridge_generated.dart keeps every FFI detail
package api
import "errors"
type User struct {
ID int64
Name string
}
func Add(a, b int) int {
return a + b
}
//fgb:async
func LoadUser(id int64) (User, error) {
if id <= 0 {
return User{}, errors.New("id must be positive")
}
return User{ID: id, Name: "Gopher"}, nil
}
The generated Dart API uses named parameters and normal Dart types:
final total = add(a: 20, b: 22);
final user = await loadUser(id: 1);
print(user.name);
Unmarked functions are synchronous. Add //fgb:async only when the Dart API should return a
Future.
Why flutter_go_bridge
Ordinary Go source — APIs are parsed with the official go/packages, go/ast, and go/types
toolchain. No custom Go syntax or interface definition language is required.
Dart-first generated API — every Go source file gets a matching Dart file, parameters are named,
and structs become typed Dart classes.
FFI stays internal — dynamic library loading, native memory, codecs, handles, and Dart API DL are
isolated in bridge_generated.dart.
No Flutter SDK dependency in generated bindings — generated APIs use Dart SDK libraries and do
not import package:flutter/services.dart or depend on Flutter Native Assets.
Per-call codec selection — fast CST/DCO paths are used where possible; maps, dynamic values, and
interfaces fall back to the built-in standard codec when needed.
Paired custom codecs - return a concrete generated fgb.CustomCodec[GoType, DartType] from a
zero-argument //fgb:customCodec factory; choose cached lazy (default), cached eager, or uncached
prototype construction while both directions stay together and use the Standard codec transport.
Ordered startup hooks - mark top-level Go functions with //fgb:init or an int32 priority;
GoLibrary.initialize(...) runs them once in deterministic order and generates typed args wrappers
for parameterized hooks. See the directive reference.
Stable native ABI — adding a Go function does not add a new exported C symbol. Calls use a fixed
dispatcher ABI, so Gokit and CMake integration remains stable.
Stateful Go objects — serializable structs become Dart value classes; stateful or unsupported
structs become GoOpaque handles released by NativeFinalizer.
Streams and callbacks — expose Go producers as Dart Stream<T> and pass synchronous or async
Dart closures into Go functions.
Install
Install flutter_go_bridge_codegen from a GitHub Release archive. Release binaries contain the
embedded Gokit templates required by create and integrate.
The getting-started guide
provides installers for Windows, Linux, and macOS. They select the current OS and architecture,
require an installed Go development environment, and install the latest stable release by default.
When no stable release exists yet, they fall back to the newest pre-release; callers can also select
the pre-release channel explicitly.
When building the code generator from a source checkout, initialize the Gokit submodule first:
create runs flutter create and applies the Go/Gokit bridge template, leaving a runnable project.
Integrate an existing project
Run the command anywhere inside the Flutter project:
flutter_go_bridge_codegen integrate
For an existing FFI plugin:
flutter_go_bridge_codegen integrate -t plugin
The command finds the nearest pubspec.yaml, adds the Go module and Gokit build files, generates the
initial bridge, and preserves existing files whenever possible.
Generate bindings
flutter_go_bridge_codegen generate
During development, regenerate automatically:
flutter_go_bridge_codegen generate --watch
Or let the CLI run Flutter and coordinate both source trees:
flutter_go_bridge_codegen run -d emulator-5554
Dart changes use hot reload. Go changes regenerate the bridge and restart the application process so
the rebuilt platform artifact is loaded. When the device is Web, run also invokes Gokit
build-web before startup and after Go changes.
To prepare only the WebAssembly assets for a direct Flutter command, use:
flutter_go_bridge_codegen build-web
flutter run -d chrome
# or: flutter build web
build-web generates the shared bridge and runs Gokit build-web; it does not start or build
Flutter itself.
For a one-shot platform artifact, generate and build together:
flutter_go_bridge_codegen build web -- --release
flutter_go_bridge_codegen build windows -- --release
Configuration
The CLI discovers these files automatically:
.flutter_go_bridge.yml, .flutter_go_bridge.yaml, or .flutter_go_bridge.json;
flutter_go_bridge.yml, flutter_go_bridge.yaml, or flutter_go_bridge.json;
bridge_generated.go contains the Native cgo exports, dispatchers, and Go codecs.
bridge_generated_web.go contains the pure-Go js/wasm dispatcher and standard codec.
bridge_generated.dart contains shared types/codecs and conditionally selects the Native or Web
wire.
Mirrored Dart files contain the public classes, functions, methods, interfaces, and constants.
Serialization model
Each call selects a transport from all types reachable through its parameters, receiver, and return
values:
Direction
Preferred path
Purpose
Dart → Go
CST
Real C wire structs with inline scalars and short-lived arenas for nested values
Go → Dart
DCO
Dart_CObject values posted or returned directly to Dart
Either direction
Standard codec
Fallback for maps, any, named interfaces, and other dynamic shapes
This selection is generated per call. Application code works only with the public Dart API and does
not choose codecs manually.
Supported API shapes
Go
Generated Dart
bool, string
bool, String
int8 through int64, int
int
uint8, uint16, uint32
int with range checks
uint64, uint, uintptr
BigInt
float32, float64
double
CGo scalars such as C.char, C.int, C.size_t, typedefs, and enums
Underlying int, BigInt, or double
[]byte, []int32, []int64, []float64
Dart typed lists
[]T, [N]T, map[K]V
List<T>, List<T>, Map<K, V>
time.Time, time.Duration, math/big.Int
DateTime, Duration, BigInt
net/netip.Addr, net/netip.Prefix, net/url.URL
InternetAddress, String, Uri
github.com/gofrs/uuid/v5.UUID
UuidValue
type XXX struct { ... }
class XXX or class XXX extends GoOpaque
type XXX interface { ... }
abstract interface class XXX
error
FgbPlatformException
chan<- T, fgb.StreamSink[T]
Stream<T> or StreamSink<T>
func(A) R parameter
FutureOr<R> Function(A)
See the complete type mapping,
including pointer, nullable, collection, interface, and unsupported-type rules.
Core bridge features
Structs and interfaces
Serializable Go structs become Dart value classes. Anonymous embedded structs become Dart
inheritance, and promoted fields are flattened on the wire. Named Go interfaces become Dart
abstract interface class declarations. Interfaces from dependencies discover exported concrete
types across the loaded package graph and use a GoOpaque fallback for unnameable runtime implementations.
Structs with state that cannot be serialized can be marked explicitly:
//fgb:opaque
type Counter struct {
total int
}
They become GoOpaque handle classes and retain Go-side identity across calls.
Streams
A send-only channel is enough to expose a Go-owned Dart stream:
//fgb:async
func Count(out chan<- int) {
for value := range 5 {
out <- value
}
}
await for (final value in count()) {
print(value);
}
Use fgb.StreamSink[T] when Go also needs to add error events or close the stream explicitly.
final value = await transform(
input: 'go',
mapper: (text) => text.toUpperCase(),
);
The generated callback type uses FutureOr, so both synchronous and asynchronous Dart closures are
accepted.
Return values and errors
One non-error Go result stays a normal Dart value.
Multiple non-error results become a Dart record.
Named Go results become named record fields.
error may appear anywhere in the Go result list.
Exported named Go types implementing Error() string automatically become generated Dart
exception subclasses; no extra annotation is required, and wrapped errors keep their type.
Other non-nil errors throw FgbPlatformException; several error results are available through
FgbPlatformException.goErrors with both messages and per-error exceptions.
CLI overview
Command
Purpose
generate
Generate the Go bridge and mirrored Dart API
generate --watch
Regenerate when Go source changes
run
Run Flutter, hot reload Dart, and restart after Go changes
build
Generate once and build a Flutter platform through the signing boundary
build-web
Generate once and prepare the Go WebAssembly assets for direct Flutter Web commands
create
Create a new Flutter app or FFI plugin with Go integration
Package devrun drives a `flutter run` process from code generation: it regenerates the bridge when Go sources change and then restarts the app, because a native dynamic library cannot be swapped into a live process.
Package devrun drives a `flutter run` process from code generation: it regenerates the bridge when Go sources change and then restarts the app, because a native dynamic library cannot be swapped into a live process.