Reuse existing Go libraries in Flutter by calling Go from Dart using auto generated bindings.
int
's), string
's and nested structures.error
to Exception
mapping.Install fgb
go get -u github.com/csnewman/flutter-go-bridge
Create a wrapper around your Go code.
Generate bindings
go generate
in the directory containing your wrapper.Use the generated bindings from your Flutter/Dart application
Automate library building by integrating into flutter build.
exampleapp
folder for a full example.When modifying the Go code, you may need to call flutter clean
to trigger a rebuild, dependent upon your Go source
location and configured source directories.
The bridge does not support all Go constructs and instead relies on a subset. This allows for a more ergonomic wrapper.
Therefore, you should create a Go package containing a wrapper around more complex Go libraries. The generator will scan
this package and generate a bridge
package containing the necessary FFI bridging logic.
See example for a complete example.
The process is as follows:
mkdir go
(inside the Flutter project)go/example.go
, containing the following:
//go:generate go run github.com/csnewman/flutter-go-bridge/cmd/flutter-go-bridge generate --src example.go --go bridge/bridge.gen.go --dart ../lib/bridge.gen.dart
package example
Replace the --dart
path, with a suitable location inside your flutter applications lib
directory.
go generate
to automatically regenerate the bridge.gen.go
and the corresponding bridge.gen.dart
file.From Dart:
import 'bridge.gen.dart';
var bridge = Bridge.open();
bridge.example();
await bridge.exampleAsync();
The following patterns are supported:
func Simple(a int, b int) int {
return a + b
}
Will produce:
int simple(int a, int b);
Future<int> simpleAsync(int a, int b);
func SimpleError(a int, b int) (int, error) {
return 0, errors.New("an example")
}
Will produce:
int simpleError(int a, int b);
Future<int> simpleErrorAsync(int a, int b);
If the Go function returns an error, the simpleError
function will throw a BridgeException
.
type ExampleStruct struct {
A int
B string
}
func StructPassing(val ExampleStruct) {
}
Will produce:
final class ExampleStruct {
int a;
String b;
ExampleStruct(this.a, this.b);
}
and
void structPassing(ExampleStruct val);
Future<void> structPassingAsync(ExampleStruct val);
Structs passed in this manner will be passed by value
, meaning the contents will be copied.
Value structs can not contain private fields.
TODO: Document
The platforms supported by flutter
use various build tooling, which complicates integrating Go into the build
pipeline. Originally this project had hooks into the build systems for Windows, Linux and Android, however this had
high maintenance and was not trivial to integrate into the Mac ecosystem.
Flutter (& Dart) currently have an experimental feature called
Native Assets which greatly simplifies the setup. This does however
mean that for now, this project requires using the flutter master
channel.
A complete example can be seen in the exampleapp
folder.
master
flutter channel
flutter channel master
flutter config --enable-native-assets
pubspec.yaml
logging: ^1.2.0
native_assets_cli: ^0.5.4
native_toolchain_go: ^0.3.0
ffi: ^2.1.2
flutter pub get
Create a build.dart
file
import 'package:native_toolchain_go/go_native_toolchain.dart';
import 'package:logging/logging.dart';
import 'package:native_assets_cli/native_assets_cli.dart';
void main(List<String> args) async {
await build(args, (config, output) async {
final packageName = config.packageName;
final goBuilder = GoBuilder(
name: packageName,
assetName: 'bridge.gen.dart',
bridgePath: 'go/bridge',
sources: ['go/'],
dartBuildFiles: ['hook/build.dart'],
);
await goBuilder.run(
buildConfig: config,
buildOutput: output,
logger: Logger('')
..level = Level.ALL
..onRecord.listen((record) => print(record.message)),
);
});
}
The assetId
path needs to match the location of the autogenerated bridge.gen.dart
file, as flutter uses this
internally to automate library resolution. You may need to specify a list of source directories to the GoBuilder
to allow automatic rebuilding as necessary.
You should now be able to use your IDE and other tooling as usual.
If you do not want to use the master
channel or wish to customise the build process, you can manually build the Go
library and bundle with your application as necessary:
CGO_ENABLED=1 go build -buildmode=c-shared -o libexample.so example/bridge/bridge.gen.go
You can specify GOOS
and GOARCH
as necessary.