Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion lib/data/service/pin_service.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/features/camera/data/camera_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ Future<CameraController> cameraController(Ref ref) async {

final controller = CameraController(
cameras[cameraIndex],
ResolutionPreset.medium,
ResolutionPreset.high,
enableAudio: false,
);

Expand Down
2 changes: 1 addition & 1 deletion lib/features/camera/data/camera_state.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

120 changes: 92 additions & 28 deletions lib/features/camera/presentation/camera.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:geolocator/geolocator.dart';
import 'package:go_router/go_router.dart';
import 'package:image/image.dart' as img;
import 'package:image_cropper/image_cropper.dart';
import 'package:latlong2/latlong.dart';
import 'package:mutex/mutex.dart';
Expand Down Expand Up @@ -76,24 +77,29 @@ class _CameraState extends ConsumerState<Camera> with WidgetsBindingObserver {
children: [
Align(
alignment: Alignment.bottomCenter,
// We handle the AsyncValue of the CONTROLLER here
child: controllerAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (err, stack) => Center(child: Text("Camera Error: $err")),
data: (controller) {
// Once controller is ready, we check the Values state
return cameraStateAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (err, stack) => Text(err.toString()),
data: (cameraState) => GestureDetector(
onDoubleTap: ref.read(cameraIndexProvider.notifier).increment,
onScaleStart: (_) => basScaleFactor = scaleFactor,
onScaleUpdate: (details) => handleZoom(details, controller, cameraState),
child: Padding(
padding: const EdgeInsets.all(5.0),
child: CameraPreview(controller),
),
),
data: (cameraState) {
return GestureDetector(
onDoubleTap: ref.read(cameraIndexProvider.notifier).increment,
onScaleStart: (_) => basScaleFactor = scaleFactor,
onScaleUpdate: (details) => handleZoom(details, controller, cameraState),
child: Padding(
padding: const EdgeInsets.all(5.0),
child: ClipRect(
child: AspectRatio(
aspectRatio: 3/4,
child: CameraPreview(controller),
),
),
),
);
},
);
},
),
Expand Down Expand Up @@ -256,31 +262,89 @@ class _CameraState extends ConsumerState<Camera> with WidgetsBindingObserver {

Future<void> takePicture(String groupId, int index) async {
final indexProvider = ref.read(cameraGroupIndexProvider);
if(index != indexProvider) {
if (index != indexProvider) {
pageController.animateToPage(index, duration: const Duration(milliseconds: 200), curve: Curves.easeIn);
return;
}

final controller = ref.read(cameraControllerProvider).value;
if (_m.isLocked || controller == null) return;

await _m.acquire();
ref.read(cameraCapturingProvider.notifier).setCapturing(true);
try {
final image = await controller.takePicture();
final Uint8List bytes = await image.readAsBytes();
final Position position = await Geolocator.getCurrentPosition();
final pos = LatLng(position.latitude, position.longitude);
// Pause preview while the ImageUpload screen is on top
if (controller.value.isInitialized) {
await controller.pausePreview().catchError((_) {});
}
if (!mounted) return;
context.pushNamed('imageUpload', queryParameters: {"lat": pos.latitude.toString(), "long": pos.longitude.toString()}, extra: bytes);
} catch (e) {
if(kDebugMode) print(e);
} finally {
_m.release();
ref.read(cameraCapturingProvider.notifier).setCapturing(false);

try {
final image = await controller.takePicture();
Uint8List bytes = await image.readAsBytes();

// Hardcode the target ratio to exactly 3:4
const double targetRatio = 3 / 4;

// Crop the image in a background thread using the function from the previous step
bytes = await compute(_cropImageToRatio, (bytes: bytes, targetRatio: targetRatio));

final Position position = await Geolocator.getCurrentPosition();
final pos = LatLng(position.latitude, position.longitude);

if (controller.value.isInitialized) {
await controller.pausePreview().catchError((_) {});
}

if (!mounted) return;
context.pushNamed(
'imageUpload',
queryParameters: {"lat": pos.latitude.toString(), "long": pos.longitude.toString()},
extra: bytes,
);

} catch (e) {
if (kDebugMode) print(e);
} finally {
_m.release();
ref.read(cameraCapturingProvider.notifier).setCapturing(false);
}
}
/// Crops the image center to match the target aspect ratio
Uint8List _cropImageToRatio(({Uint8List bytes, double targetRatio}) data) {
// Decode the image from bytes
final originalImage = img.decodeImage(data.bytes);
if (originalImage == null) return data.bytes; // Fallback if decode fails

final int w = originalImage.width;
final int h = originalImage.height;
final double currentRatio = w / h;

// If the aspect ratios already match, just return the original bytes
if ((currentRatio - data.targetRatio).abs() < 0.05) {
return data.bytes;
}

int cropW = w;
int cropH = h;
int cropX = 0;
int cropY = 0;

if (currentRatio > data.targetRatio) {
// The image is wider than the UI. Crop the left and right sides.
cropW = (h * data.targetRatio).round();
cropX = ((w - cropW) / 2).round();
} else {
// The image is taller than the UI. Crop the top and bottom.
cropH = (w / data.targetRatio).round();
cropY = ((h - cropH) / 2).round();
}

// Perform the crop
final croppedImage = img.copyCrop(
originalImage,
x: cropX,
y: cropY,
width: cropW,
height: cropH,
);

// Encode back to JPG and return
return img.encodeJpg(croppedImage);
}

}
2 changes: 1 addition & 1 deletion pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1046,7 +1046,7 @@ packages:
source: hosted
version: "4.1.2"
image:
dependency: transitive
dependency: "direct main"
description:
name: image
sha256: f9881ff4998044947ec38d098bc7c8316ae1186fa786eddffdb867b9bc94dfce
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ dependencies:
isar_community_flutter_libs:
rxdart: ^0.28.0
go_router: ^17.1.0
image: ^4.8.0

dev_dependencies:
build_runner:
Expand Down
Binary file modified web/icons/Icon-192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified web/icons/Icon-512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
91 changes: 72 additions & 19 deletions web/index.html
Original file line number Diff line number Diff line change
@@ -1,38 +1,91 @@
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.

The path provided below has to start and end with a slash "/" in order for
it to work correctly.

For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base

This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">

<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<meta name="description" content="A Sticker App.">

<!-- iOS meta tags & icons -->
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="buff_lisa">
<meta name="apple-mobile-web-app-title" content="Stick-It">
<link rel="apple-touch-icon" href="icons/Icon-192.png">

<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>

<title>buff_lisa</title>
<title>Stick-It</title>
<link rel="manifest" href="manifest.json">

<link rel="preload" href="main.dart.js" as="script">
<link rel="preload" href="splash/img/light-1x.png" as="image">
<link rel="preload" href="assets/icon/logo-rounded-corners.png" as="image">

<style>
body {
background-color: #e1a064; /* Match this to your app's background color */
margin: 0;
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}

#splash-screen {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 9999;
}

.splash-logo {
width: 120px; /* Adjust size as needed */
height: auto;
animation: pulse 2s infinite ease-in-out;
}

.splash-loader {
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db; /* Match this to your primary brand color */
border-radius: 50%;
width: 30px;
height: 30px;
margin-top: 24px;
animation: spin 1s linear infinite;
}

@keyframes pulse {
0% { transform: scale(0.95); opacity: 0.8; }
50% { transform: scale(1.05); opacity: 1; }
100% { transform: scale(0.95); opacity: 0.8; }
}

@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>

<div id="splash-screen">
<img class="splash-logo" src="assets/icon/logo-rounded-corners.png" alt="Stick-It Logo">
<div class="splash-loader"></div>
</div>

<script>
window.addEventListener('flutter-first-frame', function () {
var splashScreen = document.getElementById('splash-screen');
if (splashScreen) {
splashScreen.remove();
}
});
</script>

<script src="flutter_bootstrap.js" async></script>

</body>
</html>
</html>
4 changes: 2 additions & 2 deletions web/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"short_name": "Stick-It",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"background_color": "#e1a064",
"theme_color": "#e1a064",
"description": "A sticker app.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
Expand Down
Loading