flutter-go-bridge
flutter-go-bridge copied to clipboard
Flutter/Dart to Go FFI generator
flutter-go-bridge
Reuse existing Go libraries in Flutter by calling Go from Dart using auto generated bindings.
Features
- MacOS, iOS, Windows, Linux and Android support.
- Sync and Async calls from Dart.
- Passing primitives (various
int's),string's and nested structures. - Automatic
errortoExceptionmapping. - Basic object passing.
Getting Started
-
Install fgb
go get -u github.com/csnewman/flutter-go-bridge -
Create a wrapper around your Go code.
- See Go wrapper below.
- See example for an example.
-
Generate bindings
- Run
go generatein the directory containing your wrapper.
- Run
-
Use the generated bindings from your Flutter/Dart application
-
Automate library building by integrating into flutter build.
- See Platform building below.
- See the
exampleappfolder for a full example.
-
When modifying the Go code, you may need to call
flutter cleanto trigger a rebuild, dependent upon your Go source location and configured source directories.
Go wrapper
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)- Create
go/example.go, containing the following:
Replace the//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--dartpath, with a suitable location inside your flutter applicationslibdirectory. - Write a simplified wrapper, using the template mappings below.
- Invoke
go generateto automatically regenerate thebridge.gen.goand the correspondingbridge.gen.dartfile.
Dart setup
From Dart:
- Import generated bridge
import 'bridge.gen.dart'; - Load library
var bridge = Bridge.open(); - Invoke functions
bridge.example(); await bridge.exampleAsync();
Mappings
The following patterns are supported:
Function calls
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);
Function calls, with errors
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.
Struct passing
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.
Objects
TODO: Document
Platform building
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.
Native Assets approach
A complete example can be seen in the exampleapp folder.
- Switch to the
masterflutter channelflutter channel master - Enable the Native Assets experiment
flutter config --enable-native-assets - Add the required dependencies to
pubspec.yamlcli_config: ^0.1.2 logging: ^1.2.0 native_assets_cli: ^0.3.2 go_native_toolchain: ^0.0.1 ffi: ^2.1.0 - Fetch dependencies
flutter pub get - Create a
build.dartfile
Theimport 'package:go_native_toolchain/go_native_toolchain.dart'; import 'package:logging/logging.dart'; import 'package:native_assets_cli/native_assets_cli.dart'; const packageName = 'exampleapp'; void main(List<String> args) async { final buildConfig = await BuildConfig.fromArgs(args); final buildOutput = BuildOutput(); final gobuilder = GoBuilder( name: packageName, assetId: 'package:$packageName/bridge.gen.dart', bridgePath: 'go/bridge' ); await gobuilder.run( buildConfig: buildConfig, buildOutput: buildOutput, logger: Logger('')..onRecord.listen((record) => print(record.message)), ); await buildOutput.writeToFile(outDir: buildConfig.outDir); }assetIdpath needs to match the location of the autogeneratedbridge.gen.dartfile, as flutter uses this internally to automate library resolution. You may need to specify a list of source directories to theGoBuilderto allow automatic rebuilding as necessary.
You should now be able to use your IDE and other tooling as usual.
Manual building
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.