elysia/lib/plugin/AvatarPicker.dart
2025-11-04 09:53:47 +08:00

100 lines
3.1 KiB
Dart

import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:image_editor_plus/image_editor_plus.dart';
import 'package:image_editor_plus/options.dart' as o;
import 'RouteAnimation.dart';
class AvatarPicker extends StatefulWidget {
final void Function(File file)? onImageSelected;
const AvatarPicker({super.key, this.onImageSelected});
@override
State<AvatarPicker> createState() => _AvatarPickerState();
}
class _AvatarPickerState extends State<AvatarPicker> {
File? _avatar;
Future<void> _pickAndCropImage() async {
final picker = ImagePicker();
final picked = await picker.pickImage(source: ImageSource.gallery);
if (picked == null) return;
final bytes = await picked.readAsBytes();
final edited = await Navigator.push(
context,
RouteAnimation(
ImageCropper(
image: bytes,
availableRatios: [o.AspectRatio(title: '1:1', ratio: 1)],
),
Offset(0, -1),
),
);
if (edited == null) return;
final tempPath =
'${Directory.systemTemp.path}/${DateTime.now().millisecondsSinceEpoch}.jpg';
final file = await File(tempPath).writeAsBytes(edited as Uint8List);
setState(() => _avatar = file);
widget.onImageSelected?.call(file);
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
// ✅ 取最小边作为正方形边长,防止拉伸成椭圆
final side = constraints.maxWidth < constraints.maxHeight
? constraints.maxWidth
: constraints.maxHeight;
return Center(
child: GestureDetector(
onTap: _pickAndCropImage,
child: Stack(
children: [
ClipOval(
child: Container(
width: side,
height: side,
color: Colors.grey.shade200, // 可选:加载前背景
child: _avatar != null
? FittedBox(
fit: BoxFit.cover,
clipBehavior: Clip.hardEdge,
child: SizedBox(
width: side,
height: side,
child: Image.file(_avatar!, fit: BoxFit.cover),
),
)
: FittedBox(
fit: BoxFit.cover,
clipBehavior: Clip.hardEdge,
child: SizedBox(
width: side,
height: side,
child: Image.asset(
"assets/icon/default_avatar.png",
fit: BoxFit.cover,
),
),
),
),
)
],
),
),
);
},
);
}
}