Conceitos Fundamentais da Rotação
A operação de rotação em um editor de imagens envolve girar a imagem ao redor do seu ponto central. Para simplificar a implementação e garantir performance, a rotação é realizada em incrementos de 90 graus. Esta abordagem é suficiente para converter uma imagem de paisagem para retrato ou corrigir sua orientação, e é computacionalmente mais eficiente do que suportar ângulos arbitrários.
Gerenciamento de Estado com Mixin
O estado da rotação é controlado por um valor inteiro que indica quantos quartos de volta (90°) a imagem sofreu. Um mixin é utilizado para encapsular essa lógica de estado e os métodos associados.
mixin ControleRotacaoMixin<T extends StatefulWidget> on State<T> {
int _quantidadeGiros = 0;
void aplicarRotacao() {
setState(() {
_quantidadeGiros++;
});
}
void zerarRotacao() {
setState(() {
_quantidadeGiros = 0;
});
}
}
O campo _quantidadeGiros é incrementado a cada rotação. O componente RotatedBox da Flutter automaticamente aplica a operação módulo 4 ao valor fornecido, portanto, um valor de 4 equivale a 0 (sem rotação), evitando overflow.
Componente RotatedBox para Rotação
O widget RotatedBox é projetado para rotações em múltiplos de 90 graus. Sua principal vantagem é que ele recalcula o layout após a rotação. Por exemplo, um container de 300x200 pixels se tornará 200x300 pixels após uma rotação de 90 graus. Isso é crucial para o editor, pois garante que todas as camadas (imagem, camada de desenho e texto) sejam tratadas como uma unidade no layout.
RotatedBox(
quarterTurns: _quantidadeGiros,
child: Stack(
alignment: Alignment.center,
children: [
ImagemFundo(),
CanvasDesenho(),
CanvasTexto(),
],
),
)
Envolver o Stack inteiro garante a coesão das camadas. Se apenas a imagem fosse rotacionada, as anotações do usuário perderiam o posicionamento relativo.
Comparação: RotatedBox vs Transform.rotate
O Transform.rotate permite ângulos arbitários, mas não interfere no layout. Isso pode causar conteúdo transbordando do container pai. Para um editor de imagens com camadas, o RotatedBox é preferível pois seu comportamento de layout é previsível e o desempenho é superior, pois evita cálculos de matriz complexos para transformações livres.
Adicionando Animação de Rotação
Para uma rotação suave, é necessário substituir o RotatedBox por um Transform animado. Um AnimationController controla a transição.
late final AnimationController _controladorAnimacao;
late Animation<double> _animacaoAngulo;
double _alvoAngulo = 0.0;
@override
void initState() {
super.initState();
_controladorAnimacao = AnimationController(
duration: const Duration(milliseconds: 350),
vsync: this,
);
}
void iniciarRotacaoAnimada() {
_alvoAngulo += math.pi / 2;
_animacaoAngulo = Tween<double>(
begin: _alvoAngulo - math.pi / 2,
end: _alvoAngulo,
).animate(CurvedAnimation(
parent: _controladorAnimacao,
curve: Curves.easeInOutCubic,
));
_controladorAnimacao.forward(from: 0);
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animacaoAngulo,
builder: (_, child) {
return Transform.rotate(
angle: _animacaoAngulo.value,
child: child,
);
},
child: ConteudoEditor(),
);
}
Ao salvar a imagem, o RepaintBoundary deve envolver o widget já rotacionado para capturar o resultado correto.
Adaptação para OpenHarmony: Obtendo Orientação do Dispositivo
Embora a rotação em si seja puramente Flutter, obter a orientação física do dispositivo requer comunicação com a plataforma nativa.
Definição no Dart (via MethodChannel):
import 'package:flutter/services.dart';
const _plataforma = MethodChannel('canal_editor_imagens');
Future<int> buscarOrientacaoAparelho() async {
final resultado = await _plataforma.invokeMethod('orientacaoAtual');
return resultado as int;
}
Implementação no lado OpenHarmony (ArkTS):
import { MethodChannel, MethodCall, MethodResult } from '@ohos/flutter_ohos';
import { window } from '@kit.ArkUI';
class ManipuladorPlugin {
private canal: MethodChannel | null = null;
configurarCanal(binding: any) {
this.canal = new MethodChannel(binding.getBinaryMessenger(), 'canal_editor_imagens');
this.canal.setMethodCallHandler(this.tratarChamada.bind(this));
}
async tratarChamada(chamada: MethodCall, resultado: MethodResult): Promise<void> {
if (chamada.method === 'orientacaoAtual') {
const janela = await window.getLastWindow(getContext(this));
const propriedades = janela.getWindowProperties();
const orientacao = propriedades.displayOrientation;
resultado.success(orientacao);
} else {
resultado.notImplemented();
}
}
}
A orientação retornada pode ser usada para ajustar automaticamente a rotação inicial da imagem no editor.
Considerações de Implementação
- Garantir que o
RotatedBoxsempre envolva todas as camadas interativas do editor. - Utilizar
Transform.rotatepara rotações suaves, reservandoRotatedBoxpara rotações discretas instantâneas. - Para grandes volumes de dados, isolar a região de desenho com
RepaintBoundarypode melhorar o desempenho das animações.