feat: Implement local image preview and explicit submission for attire capture.
This commit is contained in:
@@ -28,6 +28,8 @@ class AttireCapturePage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _AttireCapturePageState extends State<AttireCapturePage> {
|
||||
String? _selectedLocalPath;
|
||||
|
||||
/// On gallery button press
|
||||
Future<void> _onGallery(BuildContext context) async {
|
||||
final AttireCaptureCubit cubit = BlocProvider.of<AttireCaptureCubit>(
|
||||
@@ -43,7 +45,9 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
|
||||
final GalleryService service = Modular.get<GalleryService>();
|
||||
final String? path = await service.pickImage();
|
||||
if (path != null && context.mounted) {
|
||||
await cubit.uploadPhoto(widget.item.id, path);
|
||||
setState(() {
|
||||
_selectedLocalPath = path;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
if (context.mounted) {
|
||||
@@ -67,7 +71,9 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
|
||||
final CameraService service = Modular.get<CameraService>();
|
||||
final String? path = await service.takePhoto();
|
||||
if (path != null && context.mounted) {
|
||||
await cubit.uploadPhoto(widget.item.id, path);
|
||||
setState(() {
|
||||
_selectedLocalPath = path;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
if (context.mounted) {
|
||||
@@ -95,6 +101,20 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onSubmit(BuildContext context) async {
|
||||
final AttireCaptureCubit cubit = BlocProvider.of<AttireCaptureCubit>(
|
||||
context,
|
||||
);
|
||||
if (_selectedLocalPath == null) return;
|
||||
|
||||
await cubit.uploadPhoto(widget.item.id, _selectedLocalPath!);
|
||||
if (context.mounted && cubit.state.status == AttireCaptureStatus.success) {
|
||||
setState(() {
|
||||
_selectedLocalPath = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider<AttireCaptureCubit>(
|
||||
@@ -153,8 +173,35 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
|
||||
padding: const EdgeInsets.all(UiConstants.space5),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
// Image Preview (Toggle between example and uploaded)
|
||||
if (hasUploadedPhoto) ...<Widget>[
|
||||
// Image Preview (Toggle between example, review, and uploaded)
|
||||
if (_selectedLocalPath != null) ...<Widget>[
|
||||
Text(
|
||||
'Review the attire item',
|
||||
style: UiTypography.body1b.textPrimary,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space2),
|
||||
AttireImagePreview(localPath: _selectedLocalPath),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
Text(
|
||||
'Reference Example',
|
||||
style: UiTypography.body2b.textSecondary,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space1),
|
||||
Center(
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
UiConstants.radiusBase,
|
||||
),
|
||||
child: Image.network(
|
||||
widget.item.imageUrl ?? '',
|
||||
height: 120,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, ___) =>
|
||||
const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
),
|
||||
] else if (hasUploadedPhoto) ...<Widget>[
|
||||
Text(
|
||||
'Your Uploaded Photo',
|
||||
style: UiTypography.body1b.textPrimary,
|
||||
@@ -216,38 +263,50 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
|
||||
},
|
||||
),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
|
||||
if (isUploading)
|
||||
const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(UiConstants.space8),
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
)
|
||||
else
|
||||
AttireUploadButtons(
|
||||
onGallery: () => _onGallery(context),
|
||||
onCamera: () => _onCamera(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (hasUploadedPhoto)
|
||||
SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(UiConstants.space5),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: UiButton.primary(
|
||||
text: 'Submit Image',
|
||||
onPressed: () {
|
||||
Modular.to.pop(currentPhotoUrl);
|
||||
},
|
||||
),
|
||||
),
|
||||
SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(UiConstants.space5),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
if (isUploading)
|
||||
const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(UiConstants.space4),
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
)
|
||||
else ...<Widget>[
|
||||
AttireUploadButtons(
|
||||
onGallery: () => _onGallery(context),
|
||||
onCamera: () => _onCamera(context),
|
||||
),
|
||||
if (_selectedLocalPath != null) ...<Widget>[
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
UiButton.primary(
|
||||
fullWidth: true,
|
||||
text: 'Submit Image',
|
||||
onPressed: () => _onSubmit(context),
|
||||
),
|
||||
] else if (hasUploadedPhoto) ...<Widget>[
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
UiButton.primary(
|
||||
fullWidth: true,
|
||||
text: 'Submit Image',
|
||||
onPressed: () {
|
||||
Modular.to.pop(currentPhotoUrl);
|
||||
},
|
||||
),
|
||||
],
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,10 +1,23 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AttireImagePreview extends StatelessWidget {
|
||||
const AttireImagePreview({super.key, required this.imageUrl});
|
||||
const AttireImagePreview({super.key, this.imageUrl, this.localPath});
|
||||
|
||||
final String? imageUrl;
|
||||
final String? localPath;
|
||||
|
||||
ImageProvider get _imageProvider {
|
||||
if (localPath != null) {
|
||||
return FileImage(File(localPath!));
|
||||
}
|
||||
return NetworkImage(
|
||||
imageUrl ??
|
||||
'https://images.unsplash.com/photo-1549298916-b41d501d3772?auto=format&fit=crop&q=80&w=400&h=400',
|
||||
);
|
||||
}
|
||||
|
||||
void _viewEnlargedImage(BuildContext context) {
|
||||
showDialog<void>(
|
||||
@@ -17,10 +30,7 @@ class AttireImagePreview extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||
image: DecorationImage(
|
||||
image: NetworkImage(
|
||||
imageUrl ??
|
||||
'https://images.unsplash.com/photo-1549298916-b41d501d3772?auto=format&fit=crop&q=80&w=400&h=400',
|
||||
),
|
||||
image: _imageProvider,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
@@ -47,13 +57,7 @@ class AttireImagePreview extends StatelessWidget {
|
||||
offset: Offset(0, 2),
|
||||
),
|
||||
],
|
||||
image: DecorationImage(
|
||||
image: NetworkImage(
|
||||
imageUrl ??
|
||||
'https://images.unsplash.com/photo-1549298916-b41d501d3772?auto=format&fit=crop&q=80&w=400&h=400',
|
||||
),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
image: DecorationImage(image: _imageProvider, fit: BoxFit.cover),
|
||||
),
|
||||
child: const Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
|
||||
Reference in New Issue
Block a user