import 'dart:typed_data'; import 'package:crop_your_image/crop_your_image.dart'; import 'package:flutter/material.dart'; import 'theme.dart'; /// Lets the user square-crop the picked [bytes] before it's saved as an avatar. /// Returns the cropped image bytes, or null if cancelled. Pure Flutter (works on /// every platform, desktop included) — no native cropper, no plaintext on disk. Future cropToSquare(BuildContext context, Uint8List bytes) { return Navigator.of(context).push( MaterialPageRoute( fullscreenDialog: true, builder: (context) => _CropPage(bytes: bytes), ), ); } class _CropPage extends StatefulWidget { const _CropPage({required this.bytes}); final Uint8List bytes; @override State<_CropPage> createState() => _CropPageState(); } class _CropPageState extends State<_CropPage> { final _controller = CropController(); var _cropping = false; @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.black, appBar: AppBar( backgroundColor: Colors.black, foregroundColor: Colors.white, leading: IconButton( key: const Key('crop.cancel'), icon: const Icon(Icons.close), onPressed: () => Navigator.of(context).pop(), ), actions: [ if (_cropping) const Padding( padding: EdgeInsets.all(14), child: SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ), ) else IconButton( key: const Key('crop.confirm'), icon: const Icon(Icons.check), onPressed: () { setState(() => _cropping = true); _controller.crop(); }, ), ], ), body: Crop( image: widget.bytes, controller: _controller, aspectRatio: 1, withCircleUi: true, baseColor: Colors.black, maskColor: Colors.black.withValues(alpha: 0.6), cornerDotBuilder: (size, edgeAlignment) => const DotControl(color: seedGreen), onCropped: (result) { if (!mounted) return; switch (result) { case CropSuccess(:final croppedImage): Navigator.of(context).pop(croppedImage); case CropFailure(): setState(() => _cropping = false); } }, ), ); } }