Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: #1538 - new crop tool (cf. dev mode) #2872

Merged
merged 6 commits into from
Sep 7, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions packages/smooth_app/lib/pages/crop_helper.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:image_cropper/image_cropper.dart';
import 'package:provider/provider.dart';
import 'package:smooth_app/data_models/user_preferences.dart';
import 'package:smooth_app/pages/preferences/user_preferences_dev_mode.dart';
import 'package:smooth_app/tmp_crop_image/new_crop_page.dart';

/// Crop Helper - which crop tool do we use, and the method to use it.
abstract class CropHelper {
/// Returns the crop tool selected in the dev mode preferences.
static CropHelper getCurrent(final BuildContext context) => context
.read<UserPreferences>()
.getFlag(UserPreferencesDevMode.userPreferencesFlagNewCropTool) ??
false
? _NewCropHelper()
: _OldCropHelper();

/// Returns the path of the image file after the crop operation.
Future<String?> getCroppedPath(
final BuildContext context,
final String inputPath,
);
}

/// New version of the image cropper.
class _NewCropHelper extends CropHelper {
@override
Future<String?> getCroppedPath(
final BuildContext context,
final String inputPath,
) async =>
Navigator.push<String>(
context,
MaterialPageRoute<String>(
builder: (BuildContext context) => CropPage(File(inputPath)),
fullscreenDialog: true,
),
);
}

/// Image cropper based on image_cropper. To be forgotten.
class _OldCropHelper extends CropHelper {
@override
Future<String?> getCroppedPath(
final BuildContext context,
final String inputPath,
) async =>
(await ImageCropper().cropImage(
sourcePath: inputPath,
aspectRatioPresets: <CropAspectRatioPreset>[
CropAspectRatioPreset.square,
CropAspectRatioPreset.ratio3x2,
CropAspectRatioPreset.original,
CropAspectRatioPreset.ratio4x3,
CropAspectRatioPreset.ratio16x9
],
uiSettings: <PlatformUiSettings>[
AndroidUiSettings(
initAspectRatio: CropAspectRatioPreset.original,
lockAspectRatio: false,
toolbarTitle: AppLocalizations.of(context).product_edit_photo_title,
// They all need to be the same for dark/light mode as we can't change
// the background color and the action bar color
statusBarColor: Colors.black,
toolbarWidgetColor: Colors.black,
backgroundColor: Colors.black,
activeControlsWidgetColor: const Color(0xFF85746C),
),
],
))
?.path;
}
40 changes: 5 additions & 35 deletions packages/smooth_app/lib/pages/image_crop_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,21 @@ import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';
import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart';

/// Returns the file path of an image after it's been cropped.
///
/// This is the "old" problematic version; to be rapidly changed.
Future<String?> getCroppedPath(
final BuildContext context,
final String inputPath,
) async =>
(await ImageCropper().cropImage(
sourcePath: inputPath,
aspectRatioPresets: <CropAspectRatioPreset>[
CropAspectRatioPreset.square,
CropAspectRatioPreset.ratio3x2,
CropAspectRatioPreset.original,
CropAspectRatioPreset.ratio4x3,
CropAspectRatioPreset.ratio16x9
],
uiSettings: <PlatformUiSettings>[
AndroidUiSettings(
initAspectRatio: CropAspectRatioPreset.original,
lockAspectRatio: false,
toolbarTitle: AppLocalizations.of(context).product_edit_photo_title,
// They all need to be the same for dark/light mode as we can't change
// the background color and the action bar color
statusBarColor: Colors.black,
toolbarWidgetColor: Colors.black,
backgroundColor: Colors.black,
activeControlsWidgetColor: const Color(0xFF85746C),
),
],
))
?.path;
import 'package:smooth_app/pages/crop_helper.dart';

/// Crops an image from an existing file.
Future<File?> startImageCroppingNoPick(
final BuildContext context, {
required final File existingImage,
}) async {
final NavigatorState navigator = Navigator.of(context);
final CropHelper cropHelper = CropHelper.getCurrent(context);
await _showScreenBetween(navigator);

// ignore: use_build_context_synchronously
final String? croppedPath = await getCroppedPath(
final String? croppedPath = await cropHelper.getCroppedPath(
context,
existingImage.path,
);
Expand Down Expand Up @@ -105,6 +74,7 @@ Future<File?> startImageCropping(
}) async {
// Show a loading page on the Flutter side
final NavigatorState navigator = Navigator.of(context);
final CropHelper cropHelper = CropHelper.getCurrent(context);
await _showScreenBetween(navigator);

// ignore: use_build_context_synchronously
Expand All @@ -119,7 +89,7 @@ Future<File?> startImageCropping(
}

// ignore: use_build_context_synchronously
final String? croppedPath = await getCroppedPath(
final String? croppedPath = await cropHelper.getCroppedPath(
context,
pickedXFile.path,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class UserPreferencesDevMode extends AbstractUserPreferences {
static const String userPreferencesTestEnvHost = '__testEnvHost';
static const String userPreferencesFlagAdditionalButton =
'__additionalButtonOnProductPage';
static const String userPreferencesFlagNewCropTool = '__newCropTool';
static const String userPreferencesFlagEditIngredients = '__editIngredients';
static const String userPreferencesEnumScanMode = '__scanMode';
static const String userPreferencesAppLanguageCode = '__appLanguage';
Expand Down Expand Up @@ -359,6 +360,16 @@ class UserPreferencesDevMode extends AbstractUserPreferences {
setState(() {});
},
),
SwitchListTile(
title: const Text('Use new crop tool'),
value:
userPreferences.getFlag(userPreferencesFlagNewCropTool) ?? false,
onChanged: (bool value) async {
await userPreferences.setFlag(
userPreferencesFlagNewCropTool, value);
setState(() {});
},
),
ListTile(
// Do not translate
title: const Text('Reset App Language'),
Expand Down
149 changes: 149 additions & 0 deletions packages/smooth_app/lib/tmp_crop_image/crop_grid.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import 'dart:ui';

import 'package:flutter/material.dart';

class CropGrid extends StatelessWidget {
const CropGrid({
Key? key,
required this.crop,
required this.gridColor,
required this.cornerSize,
required this.thinWidth,
required this.thickWidth,
required this.scrimColor,
required this.alwaysShowThirdLines,
required this.isMoving,
required this.onSize,
}) : super(key: key);

final Rect crop;
final Color gridColor;
final double cornerSize;
final double thinWidth;
final double thickWidth;
final Color scrimColor;
final bool alwaysShowThirdLines;
final bool isMoving;
final ValueChanged<Size> onSize;

@override
Widget build(BuildContext context) => RepaintBoundary(
child: CustomPaint(foregroundPainter: _CropGridPainter(this)),
);
}

class _CropGridPainter extends CustomPainter {
_CropGridPainter(this.grid);

final CropGrid grid;

@override
void paint(Canvas canvas, Size size) {
final Rect full = Offset.zero & size;
final Rect bounds = Rect.fromLTRB(
grid.crop.left * full.width,
grid.crop.top * full.height,
grid.crop.right * full.width,
grid.crop.bottom * full.height,
);
grid.onSize(size);

canvas.save();
canvas.clipRect(bounds, clipOp: ClipOp.difference);
canvas.drawRect(
full,
Paint() //
..color = grid.scrimColor
..style = PaintingStyle.fill
..isAntiAlias = true);
canvas.restore();

canvas.drawPath(
Path()
..addPolygon(<Offset>[
bounds.topLeft.translate(0, grid.cornerSize),
bounds.topLeft,
bounds.topLeft.translate(grid.cornerSize, 0)
], false)
..addPolygon(<Offset>[
bounds.topRight.translate(0, grid.cornerSize),
bounds.topRight,
bounds.topRight.translate(-grid.cornerSize, 0)
], false)
..addPolygon(<Offset>[
bounds.bottomLeft.translate(0, -grid.cornerSize),
bounds.bottomLeft,
bounds.bottomLeft.translate(grid.cornerSize, 0)
], false)
..addPolygon(<Offset>[
bounds.bottomRight.translate(0, -grid.cornerSize),
bounds.bottomRight,
bounds.bottomRight.translate(-grid.cornerSize, 0)
], false),
Paint()
..color = grid.gridColor
..style = PaintingStyle.stroke
..strokeWidth = grid.thickWidth
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.miter
..isAntiAlias = true);

final Path path = Path()
..addPolygon(<Offset>[
bounds.topLeft.translate(grid.cornerSize, 0),
bounds.topRight.translate(-grid.cornerSize, 0)
], false)
..addPolygon(<Offset>[
bounds.bottomLeft.translate(grid.cornerSize, 0),
bounds.bottomRight.translate(-grid.cornerSize, 0)
], false)
..addPolygon(<Offset>[
bounds.topLeft.translate(0, grid.cornerSize),
bounds.bottomLeft.translate(0, -grid.cornerSize)
], false)
..addPolygon(<Offset>[
bounds.topRight.translate(0, grid.cornerSize),
bounds.bottomRight.translate(0, -grid.cornerSize)
], false);

if (grid.isMoving || grid.alwaysShowThirdLines) {
final double thirdHeight = bounds.height / 3.0;
path.addPolygon(<Offset>[
bounds.topLeft.translate(0, thirdHeight),
bounds.topRight.translate(0, thirdHeight)
], false);
path.addPolygon(<Offset>[
bounds.bottomLeft.translate(0, -thirdHeight),
bounds.bottomRight.translate(0, -thirdHeight)
], false);

final double thirdWidth = bounds.width / 3.0;
path.addPolygon(<Offset>[
bounds.topLeft.translate(thirdWidth, 0),
bounds.bottomLeft.translate(thirdWidth, 0)
], false);
path.addPolygon(<Offset>[
bounds.topRight.translate(-thirdWidth, 0),
bounds.bottomRight.translate(-thirdWidth, 0)
], false);
}

canvas.drawPath(
path,
Paint()
..color = grid.gridColor
..style = PaintingStyle.stroke
..strokeWidth = grid.thinWidth
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.miter
..isAntiAlias = true);
}

@override
bool shouldRepaint(_CropGridPainter oldDelegate) =>
oldDelegate.grid.crop != grid.crop || //
oldDelegate.grid.isMoving != grid.isMoving;

@override
bool hitTest(Offset position) => true;
}
Loading