mirror of
				https://github.com/immich-app/immich.git
				synced 2025-10-24 23:39:03 -04:00 
			
		
		
		
	* chore: bump dart sdk to 3.8 * chore: make build * make pigeon * chore: format files --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
		
			
				
	
	
		
			176 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			176 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'package:flutter/material.dart';
 | |
| import 'package:crop_image/crop_image.dart';
 | |
| import 'package:flutter_hooks/flutter_hooks.dart';
 | |
| import 'package:immich_mobile/extensions/build_context_extensions.dart';
 | |
| import 'package:immich_mobile/routing/router.dart';
 | |
| import 'package:immich_mobile/utils/hooks/crop_controller_hook.dart';
 | |
| import 'package:immich_mobile/entities/asset.entity.dart';
 | |
| import 'edit.page.dart';
 | |
| import 'package:easy_localization/easy_localization.dart';
 | |
| import 'package:auto_route/auto_route.dart';
 | |
| 
 | |
| /// A widget for cropping an image.
 | |
| /// This widget uses [HookWidget] to manage its lifecycle and state. It allows
 | |
| /// users to crop an image and then navigate to the [EditImagePage] with the
 | |
| /// cropped image.
 | |
| 
 | |
| @RoutePage()
 | |
| class CropImagePage extends HookWidget {
 | |
|   final Image image;
 | |
|   final Asset asset;
 | |
|   const CropImagePage({super.key, required this.image, required this.asset});
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context) {
 | |
|     final cropController = useCropController();
 | |
|     final aspectRatio = useState<double?>(null);
 | |
| 
 | |
|     return Scaffold(
 | |
|       appBar: AppBar(
 | |
|         backgroundColor: context.scaffoldBackgroundColor,
 | |
|         title: Text("crop".tr()),
 | |
|         leading: CloseButton(color: context.primaryColor),
 | |
|         actions: [
 | |
|           IconButton(
 | |
|             icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24),
 | |
|             onPressed: () async {
 | |
|               final croppedImage = await cropController.croppedImage();
 | |
|               context.pushRoute(EditImageRoute(asset: asset, image: croppedImage, isEdited: true));
 | |
|             },
 | |
|           ),
 | |
|         ],
 | |
|       ),
 | |
|       backgroundColor: context.scaffoldBackgroundColor,
 | |
|       body: SafeArea(
 | |
|         child: LayoutBuilder(
 | |
|           builder: (BuildContext context, BoxConstraints constraints) {
 | |
|             return Column(
 | |
|               children: [
 | |
|                 Container(
 | |
|                   padding: const EdgeInsets.only(top: 20),
 | |
|                   width: constraints.maxWidth * 0.9,
 | |
|                   height: constraints.maxHeight * 0.6,
 | |
|                   child: CropImage(controller: cropController, image: image, gridColor: Colors.white),
 | |
|                 ),
 | |
|                 Expanded(
 | |
|                   child: Container(
 | |
|                     width: double.infinity,
 | |
|                     decoration: BoxDecoration(
 | |
|                       color: context.scaffoldBackgroundColor,
 | |
|                       borderRadius: const BorderRadius.only(
 | |
|                         topLeft: Radius.circular(20),
 | |
|                         topRight: Radius.circular(20),
 | |
|                       ),
 | |
|                     ),
 | |
|                     child: Center(
 | |
|                       child: Column(
 | |
|                         mainAxisAlignment: MainAxisAlignment.center,
 | |
|                         children: [
 | |
|                           Padding(
 | |
|                             padding: const EdgeInsets.only(left: 20, right: 20, bottom: 10),
 | |
|                             child: Row(
 | |
|                               mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | |
|                               children: [
 | |
|                                 IconButton(
 | |
|                                   icon: Icon(Icons.rotate_left, color: context.themeData.iconTheme.color),
 | |
|                                   onPressed: () {
 | |
|                                     cropController.rotateLeft();
 | |
|                                   },
 | |
|                                 ),
 | |
|                                 IconButton(
 | |
|                                   icon: Icon(Icons.rotate_right, color: context.themeData.iconTheme.color),
 | |
|                                   onPressed: () {
 | |
|                                     cropController.rotateRight();
 | |
|                                   },
 | |
|                                 ),
 | |
|                               ],
 | |
|                             ),
 | |
|                           ),
 | |
|                           Row(
 | |
|                             mainAxisAlignment: MainAxisAlignment.spaceEvenly,
 | |
|                             children: <Widget>[
 | |
|                               _AspectRatioButton(
 | |
|                                 cropController: cropController,
 | |
|                                 aspectRatio: aspectRatio,
 | |
|                                 ratio: null,
 | |
|                                 label: 'Free',
 | |
|                               ),
 | |
|                               _AspectRatioButton(
 | |
|                                 cropController: cropController,
 | |
|                                 aspectRatio: aspectRatio,
 | |
|                                 ratio: 1.0,
 | |
|                                 label: '1:1',
 | |
|                               ),
 | |
|                               _AspectRatioButton(
 | |
|                                 cropController: cropController,
 | |
|                                 aspectRatio: aspectRatio,
 | |
|                                 ratio: 16.0 / 9.0,
 | |
|                                 label: '16:9',
 | |
|                               ),
 | |
|                               _AspectRatioButton(
 | |
|                                 cropController: cropController,
 | |
|                                 aspectRatio: aspectRatio,
 | |
|                                 ratio: 3.0 / 2.0,
 | |
|                                 label: '3:2',
 | |
|                               ),
 | |
|                               _AspectRatioButton(
 | |
|                                 cropController: cropController,
 | |
|                                 aspectRatio: aspectRatio,
 | |
|                                 ratio: 7.0 / 5.0,
 | |
|                                 label: '7:5',
 | |
|                               ),
 | |
|                             ],
 | |
|                           ),
 | |
|                         ],
 | |
|                       ),
 | |
|                     ),
 | |
|                   ),
 | |
|                 ),
 | |
|               ],
 | |
|             );
 | |
|           },
 | |
|         ),
 | |
|       ),
 | |
|     );
 | |
|   }
 | |
| }
 | |
| 
 | |
| class _AspectRatioButton extends StatelessWidget {
 | |
|   final CropController cropController;
 | |
|   final ValueNotifier<double?> aspectRatio;
 | |
|   final double? ratio;
 | |
|   final String label;
 | |
| 
 | |
|   const _AspectRatioButton({
 | |
|     required this.cropController,
 | |
|     required this.aspectRatio,
 | |
|     required this.ratio,
 | |
|     required this.label,
 | |
|   });
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context) {
 | |
|     return Column(
 | |
|       mainAxisSize: MainAxisSize.min,
 | |
|       children: [
 | |
|         IconButton(
 | |
|           icon: Icon(switch (label) {
 | |
|             'Free' => Icons.crop_free_rounded,
 | |
|             '1:1' => Icons.crop_square_rounded,
 | |
|             '16:9' => Icons.crop_16_9_rounded,
 | |
|             '3:2' => Icons.crop_3_2_rounded,
 | |
|             '7:5' => Icons.crop_7_5_rounded,
 | |
|             _ => Icons.crop_free_rounded,
 | |
|           }, color: aspectRatio.value == ratio ? context.primaryColor : context.themeData.iconTheme.color),
 | |
|           onPressed: () {
 | |
|             cropController.crop = const Rect.fromLTRB(0.1, 0.1, 0.9, 0.9);
 | |
|             aspectRatio.value = ratio;
 | |
|             cropController.aspectRatio = ratio;
 | |
|           },
 | |
|         ),
 | |
|         Text(label, style: context.textTheme.displayMedium),
 | |
|       ],
 | |
|     );
 | |
|   }
 | |
| }
 |