Closed chengxuncc closed 4 years ago
I have an idea on that subject, I'll describe it when I'm back from vacation.
Currently there no easy way to address this use-case.
I'd be interested in this as well. I've just moved to go-flutter-desktop and so far I'm really happy with the results. Being able to use go-flutter for ios/android would be an amazing endgame.
Use gomobile is what I do.
@gedw99 are you using gomobile together with go-flutter-desktop? If so could you please share how you are doing so?
My requirements are different from others but happy to share and get critical feedback.
All I do is delineate between the flutter process and the golang process. The many flutter apps are pure flutter running in the standard foreground process and the singleton golang process is running on a background process.
A couple of nasty aspects depending on what your tradeoffs are.
However for me the tradeoffs work for what my goals are:
The negatives ?
Your exposures to the shifting winds of what apple and Google allow for background services. But the advanced apps like Skype ( poor choice of advanced ) and many others use this technique and so it can never be blocked in all practicality
flutter apps are in charge of their domain model. Golang singleton is a dumb pub sub topic based message queue. This forces me to keep the domain model and the message queue decoupled architecturally. As well as less experienced Devs tempted to break this contract. The flutters app can update to different versions and cope with it fine due to the schema evolution of flatbuffers just like protobuffs
the flutter apps can store their data how they like and offers a cache to avoid hitting the background process. The background service just pushed updates to the subscribed flutter apps as messages come in.
many background apps can be used. Rust is much better at some things like search indexing then golang. You can be flexible and just rely on grpc.
it requires more effort but in other ways less effort.
flat buffers support is a bit shaky and it's a really getting good eyeballs from Devs more and more. I use flatbuffers for everything P2P, C2S and S2S and am happy to have a bit higher complexity in return for much lower cost in running infrastructure and better UX.
Protobufs are a great IDL to work from. You can use that IDL for flat buffers too these days. For flutter web from the IDL can be reused for that codegen. There is very little codegen though because it's just message queue semantics
I had seen go mobile or go bind, it seems I need to write code on Java/Kotlin or Swift, which is called by Dart and pass to Go API. I think there must be some ways simpler to call Go API in flutter.
Projet like https://github.com/adieu/flutter go aims to help you in this task.
I would like to have similar features in go-flutter, here is 2 possibilities implementation:
MethodChannel
-> a thin java/swift (like what adieu has done) -> a small golang dispatcher -> your Golang plugin (exact same plug-in interface).MethodChannel
-> Dart FFI -> a small golang dispatcher -> your Golang plugin.I like the second approach the most, but FFI on Android/iOS is still a open question for me. Ref: https://github.com/go-flutter-desktop/go-flutter/issues/58
Here is a working example of using GRPC between golang / gomobile and flutter.
The go code that is using grpc and exposes a GRPC client to flutter https://github.com/textileio/go-threads/blob/master/api/pb/dart/pubspec.yaml
The flutter code that consumes the GRPC exposed by the golang code https://github.com/textileio/dart-threads-client/blob/master/pubspec.yaml
The golang code is cross compiled using gomobile.
Here is an example: https://github.com/textileio/grpc-ipfs-lite/blob/master/Makefile
They use a golang lib to search for a freeport on the mobile when it starts up.
Thats basically everything.
So once that "plumbing" is in place you can do do things like: https://github.com/textileio/go-threads#libraries
Here is a working example of using GRPC between golang / gomobile and flutter.
It seem GRPC requires more work to be done, does it require local network? local network need to be concerned sometime and it's quite boring to define protobuf, not that directly calling function. I think Dart FFI is a better choice, I will try it first on mobile platforms.
I tried Dart FFI but I don't known how to pass String type via Dart FFI. I have also taken a look at https://github.com/adieu/flutter_go/, it's great but won't work with latest flutter.
I think jsonrpc 2.0 is a good start, comparing to grpc, jsonrpc is simpler. https://github.com/adieu/flutter_go/ is using golang standard library net/rpc
, which is not support mutual rpc. I consider reimplementing it by using jsonrpc 2.0 library like http://github.com/ethereum/go-ethereum/rpc.
@crossle Thanks, but I don't understand why it didn't work without parameter.
main.go: go build -i -buildmode=c-shared -o ffi.dll
package main
import (
"C"
"fmt"
)
func main() {
}
//export Hello
func Hello() string {
fmt.Println("called from ffi")
return "Hello from golang via ffi"
}
ffi.dart: dart ffi.dart
import "dart:convert";
import "dart:ffi";
import 'package:ffi/ffi.dart';
class GoString extends Struct {
Pointer<Uint8> string;
@IntPtr()
int length;
@override
String toString() {
List<int> units = [];
for (int i = 0; i < length; ++i) {
units.add(string.elementAt(i).value);
}
return Utf8Decoder().convert(units);
}
static Pointer<GoString> fromString(String string) {
List<int> units = Utf8Encoder().convert(string);
final ptr = allocate<Uint8>(count: units.length);
for (int i = 0; i < units.length; ++i) {
ptr.elementAt(i).value = units[i];
}
final GoString str = allocate<GoString>().ref;
str.length = units.length;
str.string = ptr;
return str.addressOf;
}
}
typedef StringPtr = Pointer<GoString> Function(Pointer<GoString>);
void main() {
final lib = DynamicLibrary.open('ffi.dll');
final StringPtr Hello = lib.lookupFunction<StringPtr, StringPtr>("Hello");
print(Hello(GoString.fromString("here is dart")).ref.toString());
}
It worked:
called from ffi
Hello from golang via ffi
Process finished with exit code 0
Then I modified ffi.dart:
typedef StringPtr = Pointer<GoString> Function();
void main() {
final lib = DynamicLibrary.open('ffi.dll');
final StringPtr Hello = lib.lookupFunction<StringPtr, StringPtr>("Hello");
print(Hello().ref.toString());
}
It didn't work:
called from ffi
Process finished with exit code -1073741819 (0xC0000005)
environment:
dependencies:
ffi: ^0.1.3
Dart VM version: 2.7.0
go version:1.13.6
ffi only support param struct pointer, not support return ...
But it did returned.
It worked:
called from ffi Hello from golang via ffi Process finished with exit code 0
I figured it out by decompiling c-shared library, the first parameter is a return value when Hello
function doesn't have parameter but a return string:
// 1 return values, 0 parameter
GoString *__cdecl Hello(GoString *retstr)
There're decompiled functions with different numbers of parameters and return values:
// 0 return value, 2 parameters
void __fastcall Hello(GoString *p0, GoString *p1)
// 1 return value, 1 parameter
GoString *__fastcall Hello(GoString *retstr, GoString *p0, __int64 a3, GoString *a4)
// 1 return value, 2 parameters
GoString *__fastcall Hello(GoString *retstr, GoString *p0, GoString *p1, GoString *a4)
// 2 return values, 2 parameters
struct Hello_return {
GoString r0;
GoString r1;
};
Hello_return *__fastcall Hello(Hello_return *retstr, GoString *p0, GoString *p1, _QWORD *a4)
I did a test with 1 parameter and 1 return value situation: go:
//export Hello
func Hello(p1 string) string {
fmt.Println("called from dart ffi", p1)
return "Hello from golang via ffi"
}
dart calls with 1 parameter:
typedef StringPtr = Pointer<GoString> Function(Pointer<GoString>);
void main() {
final lib = DynamicLibrary.open('ffi.dll');
final StringPtr Hello = lib.lookupFunction<StringPtr, StringPtr>("Hello");
var p1 = GoString.fromString('p1');
print(Hello(p1).ref.toString());
print(p1.ref);
}
Output:
called from dart ffi p1
Hello from golang via ffi
Hello from golang via ffi
dart calls with 2 parameters:
typedef StringPtr = Pointer<GoString> Function(
Pointer<GoString>, Pointer<GoString>);
void main() {
final lib = DynamicLibrary.open('ffi.dll');
final StringPtr Hello = lib.lookupFunction<StringPtr, StringPtr>("Hello");
var p1 = GoString.fromString('p1');
var p2 = GoString.fromString('p2');
print(Hello(p1, p2).ref.toString());
print(p1.ref);
print(p2.ref);
}
Output:
called from dart ffi p2
Hello from golang via ffi
Hello from golang via ffi
p2
It seems work with passing 1 parameter on dart, but actually sharing memory with return value. Normally calling Go function expecting returning GoString should pass a GoString parameter to receive return value.
Check out https://github.com/deromask/deromask. It work well with desktop and mobile. Basically, we passing data around via protobuf.
I have given up trying Dart FFI:
Thank you, I accepted @gedw99's advice. Using go mobile
with grpc
is a good idea for whom want to integrate Flutter
and Golang
on mobile.
- the golang process is a singleton and provides all data to the foreground flutter apps. So any data shared between two flutter apps stays in sync automatically. There is only one version of the truth on the device.
grpc and gomobile is what I did as well in my app over a year ago.
only platform binding code I needed was to pass a user password and return a address:port so the flutter app could communicate to the golang service. ~200 lines of code for ios and android.
@james-lawrence / @chengxuncc Did you manage to get the gomobile code running as a background service on mobiles ? I have not. On Desktops it easy.
I'd be interested in this as well. I've just moved to go-flutter-desktop and so far I'm really happy with the results. Being able to use go-flutter for ios/android would be an amazing endgame.
Excuse me, how did you use go as a flutter backend to run on iosandroid?
I want to write an application on all platforms, it's a perfect solution working with
go-flutter
on desktop platforms. But I wonder how can I write apps efficiently on Android or iOS, I had seengo mobile
orgo bind
, it seems I need to write code on Java/Kotlin or Swift, which is called by Dart and pass to Go API. I think there must be some ways simpler to call Go API in flutter.