图片选择与裁剪通常需要统一封装,避免业务层重复实现。下面以 image_picker + crop_your_image 为例。
0. 依赖
dependencies:
image_picker: ^0.8.5
crop_your_image: ^0.7.5
0.1 平台配置(关键)
Android:AndroidManifest.xml 添加相机/相册权限。
iOS:Info.plist 添加 NSPhotoLibraryUsageDescription、NSCameraUsageDescription。
1. 选择图片
final picker = ImagePicker();
Future<XFile?> pickImage() async {
return picker.pickImage(source: ImageSource.gallery);
}
1.1 相机拍照
await picker.pickImage(source: ImageSource.camera);
1.2 限制尺寸
await picker.pickImage(
source: ImageSource.gallery,
maxWidth: 1024,
maxHeight: 1024,
);
2. 裁剪组件
class CropPage extends StatefulWidget {
final Uint8List image;
const CropPage({super.key, required this.image});
@override
State<CropPage> createState() => _CropPageState();
}
class _CropPageState extends State<CropPage> {
final controller = CropController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(actions: [
IconButton(
icon: const Icon(Icons.check),
onPressed: () => controller.crop(),
)
]),
body: Crop(
controller: controller,
image: widget.image,
onCropped: (data) {
Navigator.pop(context, data);
},
),
);
}
}
3. 完整流程
final file = await pickImage();
if (file == null) return;
final bytes = await file.readAsBytes();
final cropped = await Navigator.push<Uint8List>(
context,
MaterialPageRoute(builder: (_) => CropPage(image: bytes)),
);
4. 常见坑点
- 权限未配置:iOS/Android 会直接崩溃
- 大图内存:建议限制尺寸或压缩
5. 实践清单
- 选择图片统一入口
- 裁剪页独立封装
- 返回 Uint8List 供上传或保存