-
Notifications
You must be signed in to change notification settings - Fork 283
Implement a plugin
The source code of this example is available in the example repo.
Flutter uses a flexible system that allows you to call platform-specific APIs, make sure to be familiar with the way Flutter handles platform calls on Android or iOS before going further. A great tutorial is available on flutter.dev.
The following code demonstrates the matching Go implementation described in the official Flutter platform-channels docs
The goal is to retrieve the current battery level of the device via a single platform message, getBatteryLevel
.
Matching step in the official Flutter docs
We use a MethodChannel with a single platform method that returns the battery level.
// File: lib/main_desktop.dart
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
// The client and host sides of a channel are connected through
// a channel name passed in the channel constructor.
const platform = const MethodChannel('samples.flutter.dev/battery');
void main() {
test('Test Battery Plugin result', () async {
// Invoke a method on the method channel, specifying the concrete
// method to call via the String identifier.
final int result = await platform.invokeMethod('getBatteryLevel');
expect(result, 55);
final String batteryLevel = 'Battery level at $result % .';
print(batteryLevel);
SystemNavigator.pop(); // close the app
});
}
The following code implementation the Golang platform channels.
The package name chosen is battery
, making the Plugin available by accessing
&battery.MyBatteryPlugin{}
// File: main.go
package battery
import (
flutter "github.com/go-flutter-desktop/go-flutter"
"github.com/go-flutter-desktop/go-flutter/plugin"
)
// Make sure to use the same channel name as was used on the Flutter client side.
const channelName = "samples.flutter.dev/battery"
// MyBatteryPlugin demonstrates how to call a platform-specific API to retrieve
// the current battery level.
//
// This example matches the guide example available on:
// https://flutter.dev/docs/development/platform-integration/platform-channels
type MyBatteryPlugin struct{}
var _ flutter.Plugin = &MyBatteryPlugin{} // compile-time type check
// InitPlugin creates a MethodChannel and set a HandleFunc to the
// shared 'getBatteryLevel' method.
// https://godoc.org/github.com/go-flutter-desktop/go-flutter/plugin#MethodChannel
//
// The plugin is using a MethodChannel through the StandardMethodCodec.
//
// You can also use the more basic BasicMessageChannel, which supports basic,
// asynchronous message passing using a custom message codec.
// You can also use the specialized BinaryCodec, StringCodec, and JSONMessageCodec
// struct, or create your own codec.
//
// The JSONMessageCodec was deliberately left out because in Go its better to
// decode directly to known structs.
func (p *MyBatteryPlugin) InitPlugin(messenger plugin.BinaryMessenger) error {
channel := plugin.NewMethodChannel(messenger, channelName, plugin.StandardMethodCodec{})
channel.HandleFunc("getBatteryLevel", handleGetBatteryLevel)
return nil // no error
}
// handleGetBatteryLevel is called when the method getBatteryLevel is invoked by
// the dart code.
// The function returns a fake battery level, without raising an error.
//
// Supported return types of StandardMethodCodec codec are described in a table:
// https://godoc.org/github.com/go-flutter-desktop/go-flutter/plugin#StandardMessageCodec
func handleGetBatteryLevel(arguments interface{}) (reply interface{}, err error) {
batteryLevel := int32(55) // Your platform-specific API
return batteryLevel, nil
}
In case you aren't using a hover compatible plugin, click here 👇
Before adding this plugin to go-flutter, we need to setup few requirements:
- go-flutter uses Go modules. Make sure your go version is updated.
- A
go.mod
andgo.sum
file.
Next to the above main.go
file, generate the go.mod
and go.sum
files
using:
# enable go modules
export GO111MODULE=on
# create the go.mod
go mod init github.com/go-flutter-desktop/plugins/go-plugin-example/battery
# add missing modules
go mod tidy
Outputs:
// File: Generated go.mod
module github.com/go-flutter-desktop/plugins/go-plugin-example/battery
go 1.12
require github.com/go-flutter-desktop/go-flutter vX.XX.X
Your plugin is DONE!
First, read the Plugin info wiki section.
Assuming you are using hover
, and you have initialized hover
in your Flutter project, edit the option.go file and add your local plugin.
Our option.go file is looking like this:
// File: go/cmd/options.go
package main
import (
"github.com/go-flutter-desktop/go-flutter"
// url set in `go mod init 'url'`
"github.com/go-flutter-desktop/plugins/go-plugin-example/battery"
)
var options = []flutter.Option{
flutter.WindowInitialDimensions(200, 200),
flutter.PopBehavior(flutter.PopBehaviorClose), // on SystemNavigator.pop() closes the app
flutter.AddPlugin(&battery.MyBatteryPlugin{}), // our plugin
}
To fetch the dependency, hover
uses the go.mod
located under the go/
directory of your Flutter project.
Since there is no go module available at the github.com/go-flutter-desktop/plugins/go-plugin-example/battery url.
Our build will fail.
$ hover run
go: github.com/go-flutter-desktop/plugins/go-plugin-example/[email protected]: unknown revision go-plugin-example/v0.1.0
go: error loading module requirements
To test before publishing, we can use the replace
directive in /go/go.mod
.
Our go.mod
file now looks something like this:
// File ./go/go.mod
module github.com/go-flutter-desktop/examples/plugin_tutorial/desktop
go 1.12
require (
github.com/go-flutter-desktop/go-flutter vX.XX.X
github.com/go-flutter-desktop/plugins/go-plugin-example/battery v0.1.0
)
replace github.com/go-flutter-desktop/plugins/go-plugin-example/battery => ../go-plugin-example/battery
Go wont try to fetch MyBatteryPlugin at the github.com/go-flutter-desktop/plugins/go-plugin-example/battery VCS url, instead it will use one located on your file filesystem.
The new import path from the replace directive is used without needing to update the import paths in the actual source code.
$ hover run
flutter: Observatory listening on http://127.0.0.1:50300/GAkYtsSwdYU=/
flutter: 00:00 +0: Test Battery Plugin result
flutter: Battery level at 55 % .
flutter: 00:00 +1: All tests passed!
go-flutter: closing application
app 'plugin_tutorial' exited.
🎊 YaY 🎊
We can now publish the platform-specific Go code as a remote Go module.
- You can read existing implementations, path_provider is a good example.
- For more informations, read the our docs about Plugin: here and here.
The previous article explained how to expose a platform (Golang) service in your host app code and have it invoked from the Dart side.
The Flutter plugin API also support calling Dart handler from Golang.
Using InvokeMethod
on a MethodChannel triggers the corresponding Dart MethodCallHandler
.
Example:
// Golang
channel := plugin.NewMethodChannel(messenger, "samples/demo", plugin.StandardMethodCodec{})
err := p.channel.InvokeMethod("test", nil)
// error handling..
// Dart
const platform_channel = MethodChannel("samples/demo");
platform_channel.setMethodCallHandler((MethodCall methodCall) async {
print(methodCall.method); // prints: test
});
if you wish to have a reply use InvokeMethodWithReply
, example:
// Golang
channel := plugin.NewMethodChannel(messenger, "samples/demo", plugin.StandardMethodCodec{})
reply, err := channel.InvokeMethodWithReply("test", nil) // blocks the goroutine until reply is avaiable
// error handling..
spew.Dump(reply) // prints 5 seconds later: (string) (len=21) "reply from dart: test"
// Dart
const platform_channel = MethodChannel("samples/demo");
platform_channel.setMethodCallHandler((MethodCall methodCall) async {
print(methodCall.method); // prints: test
await Future.delayed(Duration(seconds: 5));
return "reply from dart: " + methodCall.method;
});
When the platform (Golang) needs to send data every time a particular event happens (e.g. wifi connection changes),
the EventChannel named channel can be used.
Example of a go-flutter plugin using the EventChannel is available in the xbox_controller example.