Flutter + Golang : código nativo direto da fonte

Search for a command to run...

No comments yet. Be the first to comment.
Como integrei AWS IoT Core em um app Flutter: desafios e aprendizados

Se o seu projeto Flutter estiver utilizando uma versão do Gradle inferior à 8, é provável que você enfrente problemas ao usar o Android Studio 2024.2. Isso ocorre porque essa versão do Android Studio agora utiliza, por padrão, o JDK 21. Para solucion...

Você já enfrentou o desafio de fazer um aplicativo Flutter usar recursos nativos? Muitas vezes, isso pode ser um tanto traumático. Certa vez, precisei fazer com que um aplicativo usando React Native + Expo utilizasse o GPS em um dispositivo de pagame...

A arquitetura de microapps tem como objetivo proporcionar ao usuário uma experiência unificada, utilizando diversos microapps altamente especializados. Com Flutter/Dart, temos a oportunidade ideal para criar projetos com essa arquitetura, devido à co...

Depois de contribuir para um package da comunidade(por ex. flutter_zxing) tive a curiosidade de me aprofundar mais em como chamar código nativo direto de um projeto Dart, e também estou tendo cada vez mais interesses em linguagens como Rust e Golang, elas me atraem como o mel atraem ursos 🍯.
Então me propus a usar Golang interpolado com Dart e trouxe aqui um pequeno guia para você desenvolvedor e sofredor.
Antes de tudo é bom ter o Golang corretamente configurado em seu ambiente de desenvolvimento, junto também o C.
Escrevi um código Golang básico que calcula a sequência fibonacci. Note para a importação import "C", o cgo é o responsável pela ajuda de chamar código C com Golang.
package main
import "C"
//export fibonacci
func fibonacci(n C.uint) C.uint {
if n <= 1 {
return n
}
return fibonacci(n-1) + fibonacci(n-2)
}
func main() {}
Indicamos a função a ser exportada usando o comentário //export nomefuncao como documentado aqui
Para usar o ffigen devemos gerar o build e ter no minimo o arquivo de cabeçalho C e o arquivo de objeto compartilhado, os famosos *.so compilados. Como vou usar o flutter no Android, usei o comando:
CGO_ENABLED=1 GOOS="android" GOARCH="amd64" GOARM="" go build -buildmode=c-shared -o lib.so fibonacci.go
// para ver as possibilidades rode o comando `$ go tool dist list`
Talvez você precise apontar para para o NDK adicionando
CC=meu caminhoparandk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android21-clang
Criando um projeto Flutter e tendo adicionado o ffigen as dependências de desenvolvimento podemos através do arquivo de cabeçalho gerar nosso código Dart para interpolar tudo isso.
flutter pub add -d ffigen
Para configurar o ffigen basta seguir a documentação aqui. Nesse nosso projeto adicionamos o seguinte ao final do pubspec.yaml:
# Meu sistema é um Linux Fedora
ffigen:
name: "FibonacciGO"
description: "Bindings to FibonacciGO"
output: "lib/generated_bindings.dart"
llvm-path:
- "/usr/local/opt/llvm"
- 'C:\Program Files\llvm'
- "/usr/lib/llvm-11"
- "/usr/lib64/libclang.so"
headers:
entry-points:
- "lib.h"
Para gerar os binds basta executar o comando abaixo e o arquivo indicado no pubspec.yaml será gerado
flutter pub run ffigen
E para facilitar criei um arquivo para facilitar o bind
import 'dart:ffi';
import 'dart:io';
import 'package:fluttergo/generated_bindings.dart';
final FibonacciGO fibonacciGO = FibonacciGO(dylib);
DynamicLibrary _openDynamicLibrary() {
if (Platform.isAndroid) {
return DynamicLibrary.open('lib.so');
} else if (Platform.isLinux) {
return DynamicLibrary.open('/dev/blog/fluttergo/lib.so');
} else if (Platform.isWindows) {
return DynamicLibrary.open('lib.dll');
}
return DynamicLibrary.process();
}
DynamicLibrary dylib = _openDynamicLibrary();
Caso tenha gerado os arquivos
*.sopara Android copie e cole cada um na respectiva pasta da arquitetura
- android/app/src/main/jniLibs/arm64-v8a
- android/app/src/main/jniLibs/armeabi-v7a
- android/app/src/main/jniLibs/x86
- android/app/src/main/jniLibs/x86_64
Criei uma função com a mesma implementação fibonacci em Dart. Usei o benchmark_harness para medir o desempenho Dart x Golang interpolado obtive o mesmo desempenho.
int fibonacci(int n) => n <= 2 ? 1 : fibonacci(n - 2) + fibonacci(n - 1);
Porém otimizando o código Golang o trem decolou
//export fibonacciSequential
func fibonacciSequential(n C.uint) C.uint {
if n <= 1 {
return C.uint(n)
}
var n2, n1 C.uint = 0, 1
for i := C.uint(2); i < n; i++ {
n2, n1 = n1, n1+n2
}
return n2 + n1
}
flutter: Run(RunTime): 388.61892877711654 us. //Dart
flutter: Run(RunTime): 2.5107325 us. //Golang
*µs => microsegundos
O código abaixo alcançou flutter: Run(RunTime): 0.3191476466012018 us.. Em contra partida não tentei mais otimizar o código Golang.
int fibonacciInLoops(int n) {
if (n < 2) return n;
var data = [0, 1];
for (var i = 2; i < n + 1; i++) {
data.add(data[i - 1] + data[i - 2]);
}
return data[data.length - 1];
}
Isso quer dizer que é muito mais provável que somente seja viável usar interpolação caso precise de grande poder computacional, grande volume de dados a ser tratados, algum código nativo especifico ou alguma solução em outra linguagem.
Caso precise de um poder computacional grande talvez seja necessário interpolar com código nativo usando Golang, Rust, Java ou até C puro - tratamento de imagens e vídeos, criptografia, compressão de arquivos, IA, etc.
Repositório para você conferir