flutter_go_bridge
A code-generation bridge between Go and Dart.
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.
English |
简体中文
Documentation
The complete English documentation is available at:
https://star4277.github.io/flutter_go_bridge
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.
- 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
go install github.com/star4277/flutter_go_bridge/cmd/flutter_go_bridge_codegen@latest
This installs the flutter_go_bridge_codegen command.
When building the code generator from a source checkout, initialize the Gokit submodule first:
git submodule update --init --recursive
Quick start
Create a new project
flutter_go_bridge_codegen create my_app
For an FFI plugin:
flutter_go_bridge_codegen create my_plugin -t plugin
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 native library is loaded.
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;
- the
flutter_go_bridge section in pubspec.yaml.
A minimal configuration looks like this:
go_input: go/api
go_output: go/bridge_generated.go
dart_output: lib/src/bridge_generated.dart
dart_entrypoint_class_name: FlutterGoBridge
dart_format: true
library_name is optional and defaults to go_lib_<pubspec package name>. Command-line flags
override configuration files.
See the configuration reference
for every option.
Generated output
Given this Go module:
go/
├── go.mod
└── api/
├── api.go
└── account.go
Code generation produces one Go bridge and a mirrored Dart tree:
go/
├── bridge_generated.go
├── internal/fgb/fgb_generated.go
└── api/
├── api.go
└── account.go
lib/src/
├── bridge_generated.dart
└── api/
├── api.dart
└── account.dart
bridge_generated.go contains the cgo exports, dispatchers, and Go codecs.
bridge_generated.dart contains the FFI runtime, dynamic library bindings, codecs, and handle
management.
- 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 |
[]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 with a generated, closed set of Go 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.
Dart closure callbacks
//fgb:async
func Transform(input string, mapper func(string) string) string {
return mapper(input)
}
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.
- Non-nil errors throw
FgbPlatformException; several error results are available through
FgbPlatformException.goErrors.
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 |
create |
Create a new Flutter app or FFI plugin with Go integration |
integrate |
Add the bridge to an existing Flutter project |
The full command and flag reference is in the
CLI documentation.
Release workflow and version management are documented in the
releasing guide.
Development
Clone with submodules, then run the Go test suite:
git clone --recurse-submodules https://github.com/star4277/flutter_go_bridge.git
cd flutter_go_bridge
go test ./...
Build the CLI locally:
go build ./cmd/flutter_go_bridge_codegen
Build release archives with the Makefile:
make windows-amd64
make linux-amd64
make macos-arm64
The documentation site uses Bun:
cd docs
bun install
bun run typecheck
bun run build
License
flutter_go_bridge is available under the MIT License.
Generated codec code also incorporates or follows third-party components under their respective
licenses. See THIRD_PARTY_NOTICES.md.