# Flutter + Golang : código nativo direto da fonte

# Introdução

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.

# Golang com Dart 🤝

[Escrevi um código Golang](https://github.com/lucasnsa/flutterandgolang) básico que calcula a sequência fibonacci. Note para a importação `import "C"`, o [cgo](https://pkg.go.dev/cmd/cgo) é o responsável pela ajuda de chamar código C com Golang.

```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](https://pkg.go.dev/cmd/cgo#hdr-C_references_to_Go)

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:

```bash
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](https://github.com/lucasnsa/flutterandgolang) 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.

```bash
flutter pub add -d ffigen
```

Para configurar o ffigen basta seguir a [documentação aqui](https://pub.dev/packages/ffigen). Nesse nosso projeto adicionamos o seguinte ao final do pubspec.yaml:

```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
```bash
flutter pub run ffigen
```
E para facilitar criei um arquivo para facilitar o `bind`
```dart
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 `*.so` para 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

# Performance 🏇

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.

```dart
int fibonacci(int n) => n <= 2 ? 1 : fibonacci(n - 2) + fibonacci(n - 1);
```

Porém otimizando o código Golang o `trem decolou`

```golang
//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.

```dart
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.

# Considerações ✍️

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](https://github.com/lucasnsa/flutterandgolang)

## Fontes
- [High-performance Fibonacci numbers generator in Go](https://blog.abelotech.com/posts/fibonacci-numbers-golang/)
- [Coding Problem: Nth Fibonacci Number in Golang by Israel Miles](https://levelup.gitconnected.com/coding-problem-nth-fibonacci-number-in-golang-68db104e318)
- [Faster Fibonacci by Mustafa Naser](https://sof9816.medium.com/faster-fibonacci-efde16a7d312)
