upload, edit postcard image with filter
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
class ApiUrls {
|
class ApiUrls {
|
||||||
|
static const baseUrl =
|
||||||
// static const baseUrl = "https://devapi.citycards.betadelivery.com";//Normal API
|
"https://devapi.citycards.betadelivery.com"; //Normal API
|
||||||
// static const baseUrl = "https://testingapi.citycards.betadelivery.com";// Test API
|
//static const baseUrl =
|
||||||
static const baseUrl = "https://uatapi.citycard.betadelivery.com";// Production Lvl API
|
// "https://testingapi.citycards.betadelivery.com"; // Test API
|
||||||
|
// static const baseUrl = "https://uatapi.citycard.betadelivery.com";// Production Lvl API
|
||||||
|
|
||||||
static const refreshToken = "$baseUrl/auth/refresh";
|
static const refreshToken = "$baseUrl/auth/refresh";
|
||||||
|
|
||||||
|
|||||||
150
lib/postcard/blocs/edit_image_filter/edit_image_filter_bloc.dart
Normal file
150
lib/postcard/blocs/edit_image_filter/edit_image_filter_bloc.dart
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:image/image.dart' as img;
|
||||||
|
|
||||||
|
part 'edit_image_filter_event.dart';
|
||||||
|
part 'edit_image_filter_state.dart';
|
||||||
|
|
||||||
|
enum EditImageType { network, file }
|
||||||
|
|
||||||
|
class EditImageFilterBloc
|
||||||
|
extends Bloc<EditImageFilterEvent, EditImageFilterState> {
|
||||||
|
EditImageFilterBloc() : super(EditImageFilterInitial()) {
|
||||||
|
on<DownloadImage>((event, emit) async {
|
||||||
|
try {
|
||||||
|
emit(DownloadImageLoading());
|
||||||
|
if (event.type == EditImageType.network) {
|
||||||
|
final Dio dio = Dio();
|
||||||
|
|
||||||
|
final directory = await getTemporaryDirectory();
|
||||||
|
|
||||||
|
final String fileName =
|
||||||
|
'${DateTime.now().millisecondsSinceEpoch}.png';
|
||||||
|
|
||||||
|
final String filePath = '${directory.path}/$fileName';
|
||||||
|
|
||||||
|
await dio.download(
|
||||||
|
event.url,
|
||||||
|
filePath,
|
||||||
|
options: Options(responseType: ResponseType.bytes),
|
||||||
|
);
|
||||||
|
|
||||||
|
emit(
|
||||||
|
DownloadImageSuccessfully(
|
||||||
|
filePath: filePath,
|
||||||
|
filteredImagePath: filePath,
|
||||||
|
filter: 'original',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
emit(
|
||||||
|
DownloadImageSuccessfully(
|
||||||
|
filePath: event.url,
|
||||||
|
filteredImagePath: event.url,
|
||||||
|
filter: 'original',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
emit(DownloadImageFailed());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
on<SelectFilter>((event, emit) async {
|
||||||
|
if (state is! DownloadImageSuccessfully) return;
|
||||||
|
|
||||||
|
final currentState = state as DownloadImageSuccessfully;
|
||||||
|
|
||||||
|
try {
|
||||||
|
log("Selected Filter ${event.filterName}");
|
||||||
|
emit(currentState.copyWith(processing: true));
|
||||||
|
|
||||||
|
if (event.filterName == "none" || event.filterName == "original") {
|
||||||
|
emit(
|
||||||
|
currentState.copyWith(
|
||||||
|
filteredImagePath: currentState.filePath,
|
||||||
|
processing: false,
|
||||||
|
filter: "original",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final originalFile = File(currentState.filePath);
|
||||||
|
final bytes = await originalFile.readAsBytes();
|
||||||
|
img.Image? image = img.decodeImage(bytes);
|
||||||
|
|
||||||
|
if (image == null) {
|
||||||
|
emit(currentState.copyWith(processing: false));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (event.filterName) {
|
||||||
|
case "vintage":
|
||||||
|
image = img.adjustColor(
|
||||||
|
image,
|
||||||
|
saturation: 0.8,
|
||||||
|
gamma: 1.1,
|
||||||
|
contrast: 0.9,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "bw":
|
||||||
|
image = img.grayscale(image);
|
||||||
|
break;
|
||||||
|
case "sepia":
|
||||||
|
image = img.sepia(image);
|
||||||
|
break;
|
||||||
|
case "cool":
|
||||||
|
// hue is normalized 0.0–1.0; -15 degrees ≈ -15/360 ≈ -0.042
|
||||||
|
image = img.adjustColor(image, hue: -0.042, contrast: 1.05);
|
||||||
|
break;
|
||||||
|
case "contrast":
|
||||||
|
image = img.adjustColor(image, contrast: 1.4);
|
||||||
|
break;
|
||||||
|
case "soft":
|
||||||
|
image = img.adjustColor(
|
||||||
|
image,
|
||||||
|
brightness: 0.1,
|
||||||
|
gamma: 0.9,
|
||||||
|
saturation: 1.1,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
emit(currentState.copyWith(filter: "none", processing: false));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final filteredPath =
|
||||||
|
"${originalFile.parent.path}/filtered_${event.filterName}_${DateTime.now().millisecondsSinceEpoch}.jpg";
|
||||||
|
|
||||||
|
final filteredFile = File(filteredPath)
|
||||||
|
..writeAsBytesSync(img.encodeJpg(image, quality: 95));
|
||||||
|
|
||||||
|
if (currentState.filteredImagePath != currentState.filePath) {
|
||||||
|
final oldFile = File(currentState.filteredImagePath);
|
||||||
|
if (await oldFile.exists()) await oldFile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
log(
|
||||||
|
"Filter applied: ${filteredFile.path} | filter: ${event.filterName}",
|
||||||
|
);
|
||||||
|
|
||||||
|
emit(
|
||||||
|
currentState.copyWith(
|
||||||
|
filteredImagePath: filteredFile.path,
|
||||||
|
filter: event.filterName,
|
||||||
|
processing: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
} catch (e) {
|
||||||
|
log("SelectFilter error: ${e.toString()}");
|
||||||
|
emit(currentState.copyWith(processing: false)); // don't leave UI stuck
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
part of 'edit_image_filter_bloc.dart';
|
||||||
|
|
||||||
|
class EditImageFilterEvent extends Equatable {
|
||||||
|
const EditImageFilterEvent();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class DownloadImage extends EditImageFilterEvent {
|
||||||
|
final String url;
|
||||||
|
final EditImageType type;
|
||||||
|
const DownloadImage({required this.url, required this.type});
|
||||||
|
}
|
||||||
|
|
||||||
|
class SelectFilter extends EditImageFilterEvent {
|
||||||
|
final String filterName;
|
||||||
|
const SelectFilter({required this.filterName});
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
part of 'edit_image_filter_bloc.dart';
|
||||||
|
|
||||||
|
class EditImageFilterState extends Equatable {
|
||||||
|
const EditImageFilterState();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class EditImageFilterInitial extends EditImageFilterState {}
|
||||||
|
|
||||||
|
class DownloadImageLoading extends EditImageFilterState {}
|
||||||
|
|
||||||
|
class DownloadImageSuccessfully extends EditImageFilterState {
|
||||||
|
final String filePath;
|
||||||
|
final String filteredImagePath;
|
||||||
|
final bool processing;
|
||||||
|
final String filter;
|
||||||
|
|
||||||
|
const DownloadImageSuccessfully({
|
||||||
|
required this.filePath,
|
||||||
|
required this.filteredImagePath,
|
||||||
|
this.processing = false,
|
||||||
|
required this.filter,
|
||||||
|
});
|
||||||
|
@override
|
||||||
|
List<Object> get props => [filePath, filteredImagePath, processing, filter];
|
||||||
|
|
||||||
|
DownloadImageSuccessfully copyWith({
|
||||||
|
String? filePath,
|
||||||
|
String? filteredImagePath,
|
||||||
|
bool? processing,
|
||||||
|
String? filter,
|
||||||
|
}) {
|
||||||
|
return DownloadImageSuccessfully(
|
||||||
|
filePath: filePath ?? this.filePath,
|
||||||
|
filteredImagePath: filteredImagePath ?? this.filteredImagePath,
|
||||||
|
processing: processing ?? this.processing,
|
||||||
|
filter: filter ?? this.filter,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DownloadImageFailed extends EditImageFilterState {}
|
||||||
@@ -15,6 +15,7 @@ class EditPostcardBloc extends Bloc<EditPostcardEvent, EditPostcardState> {
|
|||||||
emit(EditPostcardLoading());
|
emit(EditPostcardLoading());
|
||||||
await MyPostCardsRepository().editMyPostCards(
|
await MyPostCardsRepository().editMyPostCards(
|
||||||
postcard: event.myPostCard,
|
postcard: event.myPostCard,
|
||||||
|
image: event.editImage,
|
||||||
);
|
);
|
||||||
log("Edit PostCard Successfully");
|
log("Edit PostCard Successfully");
|
||||||
emit(EditPostcardSuccessfull());
|
emit(EditPostcardSuccessfull());
|
||||||
|
|||||||
@@ -9,5 +9,6 @@ class EditPostcardEvent extends Equatable {
|
|||||||
|
|
||||||
class EditPostCard extends EditPostcardEvent {
|
class EditPostCard extends EditPostcardEvent {
|
||||||
final MyPostCard myPostCard;
|
final MyPostCard myPostCard;
|
||||||
const EditPostCard({required this.myPostCard});
|
final String? editImage;
|
||||||
|
const EditPostCard({required this.myPostCard, this.editImage});
|
||||||
}
|
}
|
||||||
|
|||||||
80
lib/postcard/blocs/pick_images/pick_images_bloc.dart
Normal file
80
lib/postcard/blocs/pick_images/pick_images_bloc.dart
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
|
||||||
|
part 'pick_images_event.dart';
|
||||||
|
part 'pick_images_state.dart';
|
||||||
|
|
||||||
|
class PickImagesBloc extends Bloc<PickImagesEvent, PickImagesState> {
|
||||||
|
PickImagesBloc() : super(PickImagesState()) {
|
||||||
|
final ImagePicker imagePicker = ImagePicker();
|
||||||
|
on<TakePhoto>((event, emit) async {
|
||||||
|
PickImagesState currentState = state;
|
||||||
|
try {
|
||||||
|
emit(currentState.copyWith(loading: true));
|
||||||
|
final XFile? pickedFile = await imagePicker.pickImage(
|
||||||
|
source: ImageSource.camera,
|
||||||
|
imageQuality: 80,
|
||||||
|
maxWidth: 1920,
|
||||||
|
maxHeight: 1920,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (pickedFile != null) {
|
||||||
|
emit(
|
||||||
|
currentState.copyWith(
|
||||||
|
loading: false,
|
||||||
|
file: pickedFile.path,
|
||||||
|
filteredFile: pickedFile.path,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
emit(currentState.copyWith(loading: false));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
emit(currentState.copyWith(loading: false));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
on<PickPhoto>((event, emit) async {
|
||||||
|
PickImagesState currentState = state;
|
||||||
|
try {
|
||||||
|
emit(currentState.copyWith(loading: true));
|
||||||
|
final XFile? pickedFile = await imagePicker.pickImage(
|
||||||
|
source: ImageSource.gallery,
|
||||||
|
imageQuality: 80,
|
||||||
|
maxWidth: 1920,
|
||||||
|
maxHeight: 1920,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (pickedFile != null) {
|
||||||
|
emit(
|
||||||
|
currentState.copyWith(
|
||||||
|
loading: false,
|
||||||
|
file: pickedFile.path,
|
||||||
|
filteredFile: pickedFile.path,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
emit(currentState.copyWith(loading: false));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
emit(currentState.copyWith(loading: false));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
on<SelectedFilter>((event, emit) async {
|
||||||
|
PickImagesState currentState = state;
|
||||||
|
try {
|
||||||
|
emit(
|
||||||
|
currentState.copyWith(
|
||||||
|
loading: false,
|
||||||
|
file: currentState.file ?? event.imagePath,
|
||||||
|
filteredFile: event.imagePath,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
emit(currentState.copyWith(loading: false));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
19
lib/postcard/blocs/pick_images/pick_images_event.dart
Normal file
19
lib/postcard/blocs/pick_images/pick_images_event.dart
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
part of 'pick_images_bloc.dart';
|
||||||
|
|
||||||
|
class PickImagesEvent extends Equatable {
|
||||||
|
const PickImagesEvent();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class TakePhoto extends PickImagesEvent {}
|
||||||
|
|
||||||
|
class PickPhoto extends PickImagesEvent {}
|
||||||
|
|
||||||
|
class RemovePhoto extends PickImagesEvent {}
|
||||||
|
|
||||||
|
class SelectedFilter extends PickImagesEvent {
|
||||||
|
final String imagePath;
|
||||||
|
const SelectedFilter({required this.imagePath});
|
||||||
|
}
|
||||||
23
lib/postcard/blocs/pick_images/pick_images_state.dart
Normal file
23
lib/postcard/blocs/pick_images/pick_images_state.dart
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
part of 'pick_images_bloc.dart';
|
||||||
|
|
||||||
|
class PickImagesState extends Equatable {
|
||||||
|
final String? file;
|
||||||
|
final String? filteredFile;
|
||||||
|
final bool? loading;
|
||||||
|
const PickImagesState({this.file, this.loading = false, this.filteredFile});
|
||||||
|
|
||||||
|
PickImagesState copyWith({
|
||||||
|
String? file,
|
||||||
|
bool? loading,
|
||||||
|
String? filteredFile,
|
||||||
|
}) {
|
||||||
|
return PickImagesState(
|
||||||
|
file: file ?? this.file,
|
||||||
|
loading: loading ?? this.loading,
|
||||||
|
filteredFile: filteredFile ?? this.filteredFile,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<dynamic> get props => [file, filteredFile, loading];
|
||||||
|
}
|
||||||
@@ -20,7 +20,10 @@ class MyPostCardsRepository {
|
|||||||
return (response.data as List).map((e) => MyPostCard.fromJson(e)).toList();
|
return (response.data as List).map((e) => MyPostCard.fromJson(e)).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> editMyPostCards({required MyPostCard postcard}) async {
|
Future<void> editMyPostCards({
|
||||||
|
required MyPostCard postcard,
|
||||||
|
String? image,
|
||||||
|
}) async {
|
||||||
try {
|
try {
|
||||||
final formData = FormData();
|
final formData = FormData();
|
||||||
|
|
||||||
@@ -44,16 +47,15 @@ class MyPostCardsRepository {
|
|||||||
if (postcard.address2.isNotEmpty) {
|
if (postcard.address2.isNotEmpty) {
|
||||||
formData.fields.add(MapEntry('address2', postcard.address2));
|
formData.fields.add(MapEntry('address2', postcard.address2));
|
||||||
}
|
}
|
||||||
// final fileName = postcard.pcImagePath.split('/').last;
|
if (image != null && image.isNotEmpty) {
|
||||||
// formData.files.add(
|
final fileName = image.split('/').last;
|
||||||
// MapEntry(
|
formData.files.add(
|
||||||
// 'pcImage',
|
MapEntry(
|
||||||
// await MultipartFile.fromFile(
|
'pcImage',
|
||||||
// postcard.pcImagePath,
|
await MultipartFile.fromFile(image, filename: fileName),
|
||||||
// filename: fileName,
|
),
|
||||||
// ),
|
);
|
||||||
// ),
|
}
|
||||||
// );
|
|
||||||
await _apiService.putApi(
|
await _apiService.putApi(
|
||||||
url: '${ApiUrls.editPostcard}/${postcard.id}',
|
url: '${ApiUrls.editPostcard}/${postcard.id}',
|
||||||
data: formData,
|
data: formData,
|
||||||
|
|||||||
424
lib/postcard/views/edit_image_filter.dart
Normal file
424
lib/postcard/views/edit_image_filter.dart
Normal file
@@ -0,0 +1,424 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:citycards_customer/common_packages/app_bar.dart';
|
||||||
|
import 'package:citycards_customer/postcard/blocs/edit_image_filter/edit_image_filter_bloc.dart';
|
||||||
|
import 'package:citycards_customer/postcard/blocs/pick_images/pick_images_bloc.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
|
||||||
|
import '../widgets/dotted_border_holder.dart';
|
||||||
|
|
||||||
|
class EditImageFilter extends StatefulWidget {
|
||||||
|
final EditImageType type;
|
||||||
|
final String url;
|
||||||
|
final PickImagesBloc pickImagesBloc;
|
||||||
|
const EditImageFilter({
|
||||||
|
super.key,
|
||||||
|
required this.type,
|
||||||
|
required this.url,
|
||||||
|
required this.pickImagesBloc,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<EditImageFilter> createState() => _EditImageFilterState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _EditImageFilterState extends State<EditImageFilter> {
|
||||||
|
final EditImageFilterBloc editImageFilterBloc = EditImageFilterBloc();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
editImageFilterBloc.add(DownloadImage(url: widget.url, type: widget.type));
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocConsumer<EditImageFilterBloc, EditImageFilterState>(
|
||||||
|
bloc: editImageFilterBloc,
|
||||||
|
listener: (ctx, state) {
|
||||||
|
if (state is DownloadImageFailed) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text("Failed to fetch edit details")),
|
||||||
|
);
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is DownloadImageLoading) {
|
||||||
|
return const Scaffold(
|
||||||
|
body: SafeArea(child: Center(child: CircularProgressIndicator())),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state is DownloadImageSuccessfully) {
|
||||||
|
return Scaffold(
|
||||||
|
body: SafeArea(
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
CommonAppBar(
|
||||||
|
isWhiteLogo: false,
|
||||||
|
isProfilePage: false,
|
||||||
|
showDivider: true,
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () => Navigator.pop(context),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.arrow_back, size: 20),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
const Text(
|
||||||
|
"Back",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
"Add a Filter",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
Text(
|
||||||
|
"Choose your favorite filter and enhance your postcard.",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14.sp,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: const Color(0xff2D3134),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
DottedBorderContainerHolder(
|
||||||
|
imagePath: state.filteredImagePath,
|
||||||
|
filter: state.filter,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
buildFilterOption(
|
||||||
|
context,
|
||||||
|
"Original",
|
||||||
|
File(state.filePath),
|
||||||
|
"original",
|
||||||
|
state.filter == "original",
|
||||||
|
),
|
||||||
|
buildFilterOption(
|
||||||
|
context,
|
||||||
|
"Black & White",
|
||||||
|
File(state.filePath),
|
||||||
|
"bw",
|
||||||
|
state.filter == "bw",
|
||||||
|
),
|
||||||
|
buildFilterOption(
|
||||||
|
context,
|
||||||
|
"Sepia",
|
||||||
|
File(state.filePath),
|
||||||
|
"sepia",
|
||||||
|
state.filter == "sepia",
|
||||||
|
),
|
||||||
|
buildFilterOption(
|
||||||
|
context,
|
||||||
|
"Vintage",
|
||||||
|
File(state.filePath),
|
||||||
|
"vintage",
|
||||||
|
state.filter == "vintage",
|
||||||
|
),
|
||||||
|
buildFilterOption(
|
||||||
|
context,
|
||||||
|
"Cool Tone",
|
||||||
|
File(state.filePath),
|
||||||
|
"cool",
|
||||||
|
state.filter == "cool",
|
||||||
|
),
|
||||||
|
buildFilterOption(
|
||||||
|
context,
|
||||||
|
"Contrast",
|
||||||
|
File(state.filePath),
|
||||||
|
"contrast",
|
||||||
|
state.filter == "contrast",
|
||||||
|
),
|
||||||
|
buildFilterOption(
|
||||||
|
context,
|
||||||
|
"Soft Glow",
|
||||||
|
File(state.filePath),
|
||||||
|
"soft",
|
||||||
|
state.filter == "soft",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20.h),
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: ElevatedButton(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: const Color(0xffF95F62),
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 16.h),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(40),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
widget.pickImagesBloc.add(
|
||||||
|
SelectedFilter(
|
||||||
|
imagePath: state.filteredImagePath,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
"Save Changes",
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 14.sp,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Processing overlay
|
||||||
|
if (state.processing == true)
|
||||||
|
Container(
|
||||||
|
color: Colors.black.withValues(alpha: .4),
|
||||||
|
child: const Center(
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: Color(0xffF95F62),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return const Scaffold(
|
||||||
|
body: SafeArea(child: Center(child: CircularProgressIndicator())),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds a single filter preview thumbnail
|
||||||
|
Widget buildFilterOption(
|
||||||
|
BuildContext context,
|
||||||
|
String label,
|
||||||
|
File imageFile,
|
||||||
|
String filter,
|
||||||
|
bool isSelected,
|
||||||
|
) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () => editImageFilterBloc.add(SelectFilter(filterName: filter)),
|
||||||
|
child: Container(
|
||||||
|
margin: const EdgeInsets.only(right: 12),
|
||||||
|
width: 90,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
child: ColorFiltered(
|
||||||
|
colorFilter: getColorFilter(filter),
|
||||||
|
child: Image.file(
|
||||||
|
imageFile,
|
||||||
|
height: 70,
|
||||||
|
width: 90,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: isSelected
|
||||||
|
? const Color(0xffF95F62)
|
||||||
|
: const Color(0xff2D3134),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorFilter getColorFilter(String? filter) {
|
||||||
|
switch (filter) {
|
||||||
|
case "vintage":
|
||||||
|
// Muted, warm tones without overflow
|
||||||
|
return const ColorFilter.matrix([
|
||||||
|
0.9,
|
||||||
|
0.3,
|
||||||
|
0.1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0.2,
|
||||||
|
0.8,
|
||||||
|
0.1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0.1,
|
||||||
|
0.3,
|
||||||
|
0.7,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
case "bw":
|
||||||
|
// Grayscale
|
||||||
|
return const ColorFilter.matrix([
|
||||||
|
0.2126,
|
||||||
|
0.7152,
|
||||||
|
0.0722,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0.2126,
|
||||||
|
0.7152,
|
||||||
|
0.0722,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0.2126,
|
||||||
|
0.7152,
|
||||||
|
0.0722,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
case "sepia":
|
||||||
|
// Classic soft brown
|
||||||
|
return const ColorFilter.matrix([
|
||||||
|
0.393,
|
||||||
|
0.769,
|
||||||
|
0.189,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0.349,
|
||||||
|
0.686,
|
||||||
|
0.168,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0.272,
|
||||||
|
0.534,
|
||||||
|
0.131,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
case "cool":
|
||||||
|
// Gentle blue tone — no gamma boost to avoid clipping
|
||||||
|
return const ColorFilter.matrix([
|
||||||
|
1.0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1.0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1.1,
|
||||||
|
0,
|
||||||
|
10,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
case "contrast":
|
||||||
|
// Slight contrast increase, safe range
|
||||||
|
return const ColorFilter.matrix([
|
||||||
|
1.1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
-10,
|
||||||
|
0,
|
||||||
|
1.1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
-10,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1.1,
|
||||||
|
0,
|
||||||
|
-10,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
case "soft":
|
||||||
|
// Gentle brightness and warmth — fixed to avoid pixelation
|
||||||
|
return const ColorFilter.matrix([
|
||||||
|
1.02,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
5,
|
||||||
|
0,
|
||||||
|
1.02,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
5,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1.02,
|
||||||
|
0,
|
||||||
|
5,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return const ColorFilter.mode(Colors.transparent, BlendMode.srcOver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:citycards_customer/postcard/blocs/edit_image_filter/edit_image_filter_bloc.dart';
|
||||||
import 'package:citycards_customer/postcard/blocs/edit_postcard/edit_postcard_bloc.dart';
|
import 'package:citycards_customer/postcard/blocs/edit_postcard/edit_postcard_bloc.dart';
|
||||||
|
import 'package:citycards_customer/postcard/blocs/pick_images/pick_images_bloc.dart';
|
||||||
import 'package:citycards_customer/postcard/models/my_postcard_model.dart';
|
import 'package:citycards_customer/postcard/models/my_postcard_model.dart';
|
||||||
import 'package:citycards_customer/postcard/widgets/dotted_border_container.dart';
|
import 'package:citycards_customer/postcard/widgets/dotted_border_container.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -12,6 +16,7 @@ import '../../common_packages/custom_text.dart';
|
|||||||
import '../../networkApiServices/api_urls.dart';
|
import '../../networkApiServices/api_urls.dart';
|
||||||
import '../widgets/edit_post_card/edit_message.dart';
|
import '../widgets/edit_post_card/edit_message.dart';
|
||||||
import '../widgets/edit_post_card/your_details.dart';
|
import '../widgets/edit_post_card/your_details.dart';
|
||||||
|
import 'edit_image_filter.dart';
|
||||||
|
|
||||||
class EditPostcardView extends StatefulWidget {
|
class EditPostcardView extends StatefulWidget {
|
||||||
final MyPostCard myPostCard;
|
final MyPostCard myPostCard;
|
||||||
@@ -58,6 +63,8 @@ class _EditPostcardViewState extends State<EditPostcardView> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String? selectedImage;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final size = MediaQuery.of(context).size;
|
final size = MediaQuery.of(context).size;
|
||||||
@@ -122,90 +129,213 @@ class _EditPostcardViewState extends State<EditPostcardView> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 10.h),
|
SizedBox(height: 10.h),
|
||||||
Row(
|
BlocConsumer<PickImagesBloc, PickImagesState>(
|
||||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
listener: (ctx, state) {
|
||||||
children: [
|
if (state.file != null && state.file!.isNotEmpty) {
|
||||||
Expanded(
|
setState(() {
|
||||||
child: CustomPaint(
|
selectedImage =
|
||||||
painter: DottedBorderPainter(),
|
state.filteredFile ?? state.file!;
|
||||||
child: Container(
|
});
|
||||||
padding: EdgeInsets.all(10),
|
}
|
||||||
height: size.width * 0.45,
|
},
|
||||||
width: size.width,
|
builder: (context, state) {
|
||||||
constraints: BoxConstraints(minHeight: 150),
|
return Row(
|
||||||
decoration: BoxDecoration(
|
children: [
|
||||||
borderRadius: BorderRadius.circular(5),
|
Expanded(
|
||||||
),
|
child: CustomPaint(
|
||||||
child: ClipRRect(
|
painter: DottedBorderPainter(),
|
||||||
borderRadius: BorderRadius.circular(10),
|
child: Container(
|
||||||
child: Image.network(
|
padding: EdgeInsets.all(10),
|
||||||
'${ApiUrls.baseUrl}${postCard!.pcImagePath}',
|
|
||||||
height: size.width * 0.45,
|
height: size.width * 0.45,
|
||||||
width: size.width,
|
width: size.width,
|
||||||
fit: BoxFit.cover,
|
constraints: BoxConstraints(
|
||||||
loadingBuilder:
|
minHeight: 150,
|
||||||
(context, child, loadingProgress) {
|
),
|
||||||
if (loadingProgress == null)
|
decoration: BoxDecoration(
|
||||||
return child;
|
borderRadius: BorderRadius.circular(5),
|
||||||
return Container(
|
),
|
||||||
height: size.width * 0.45,
|
child: ClipRRect(
|
||||||
width: size.width,
|
borderRadius: BorderRadius.circular(10),
|
||||||
color: Colors.grey[300],
|
child:
|
||||||
child: const Center(
|
state.file != null &&
|
||||||
child:
|
state.file!.isNotEmpty
|
||||||
CircularProgressIndicator(
|
? Image.file(
|
||||||
color: Color(0xffF95F62),
|
height: size.width * 0.45,
|
||||||
strokeWidth: 2,
|
width: size.width,
|
||||||
),
|
fit: BoxFit.cover,
|
||||||
|
File(
|
||||||
|
state.filteredFile ??
|
||||||
|
state.file!,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Stack(
|
||||||
|
children: [
|
||||||
|
Image.network(
|
||||||
|
'${ApiUrls.baseUrl}${postCard!.pcImagePath}',
|
||||||
|
height: size.width * 0.45,
|
||||||
|
width: size.width,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
loadingBuilder:
|
||||||
|
(
|
||||||
|
context,
|
||||||
|
child,
|
||||||
|
loadingProgress,
|
||||||
|
) {
|
||||||
|
if (loadingProgress ==
|
||||||
|
null) {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
return Container(
|
||||||
|
height:
|
||||||
|
size.width *
|
||||||
|
0.45,
|
||||||
|
width: size.width,
|
||||||
|
color: Colors
|
||||||
|
.grey[300],
|
||||||
|
child: const Center(
|
||||||
|
child:
|
||||||
|
CircularProgressIndicator(
|
||||||
|
color: Color(
|
||||||
|
0xffF95F62,
|
||||||
|
),
|
||||||
|
strokeWidth:
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
errorBuilder:
|
||||||
|
(
|
||||||
|
context,
|
||||||
|
error,
|
||||||
|
stackTrace,
|
||||||
|
) {
|
||||||
|
return Container(
|
||||||
|
height:
|
||||||
|
size.width *
|
||||||
|
0.45,
|
||||||
|
width: size.width,
|
||||||
|
color: Colors
|
||||||
|
.grey[300],
|
||||||
|
child: const Icon(
|
||||||
|
Icons
|
||||||
|
.image_not_supported,
|
||||||
|
color:
|
||||||
|
Colors.grey,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
Positioned(
|
||||||
|
child: state.loading == true
|
||||||
|
? Container(
|
||||||
|
height:
|
||||||
|
size.width *
|
||||||
|
0.45,
|
||||||
|
width: size.width,
|
||||||
|
decoration:
|
||||||
|
BoxDecoration(
|
||||||
|
color: Colors
|
||||||
|
.black
|
||||||
|
.withValues(
|
||||||
|
alpha:
|
||||||
|
0.25,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: SizedBox(
|
||||||
|
height: 25,
|
||||||
|
width: 25,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: Colors
|
||||||
|
.white,
|
||||||
|
strokeWidth:
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: SizedBox(),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
|
||||||
errorBuilder:
|
|
||||||
(context, error, stackTrace) {
|
|
||||||
return Container(
|
|
||||||
height: size.width * 0.45,
|
|
||||||
width: size.width,
|
|
||||||
color: Colors.grey[300],
|
|
||||||
child: const Icon(
|
|
||||||
Icons.image_not_supported,
|
|
||||||
color: Colors.grey,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Expanded(
|
||||||
),
|
child: Container(
|
||||||
Expanded(
|
height: size.width * 0.5,
|
||||||
child: Container(
|
width: size.width,
|
||||||
height: size.width * 0.5,
|
constraints: BoxConstraints(minHeight: 150),
|
||||||
width: size.width,
|
padding: EdgeInsets.all(10),
|
||||||
constraints: BoxConstraints(minHeight: 150),
|
child: Column(
|
||||||
padding: EdgeInsets.all(10),
|
mainAxisAlignment:
|
||||||
child: Column(
|
MainAxisAlignment.spaceBetween,
|
||||||
mainAxisAlignment:
|
children: [
|
||||||
MainAxisAlignment.spaceBetween,
|
imageButton(
|
||||||
children: [
|
title: 'Take a photo',
|
||||||
imageButton(
|
icon: Icons.camera_alt_outlined,
|
||||||
title: 'Take a photo',
|
width: size.width,
|
||||||
icon: Icons.camera_alt_outlined,
|
onPressed: () {
|
||||||
width: size.width,
|
context.read<PickImagesBloc>().add(
|
||||||
|
TakePhoto(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
imageButton(
|
||||||
|
title: 'Upload Again',
|
||||||
|
icon: Icons.refresh,
|
||||||
|
width: size.width,
|
||||||
|
onPressed: () {
|
||||||
|
context.read<PickImagesBloc>().add(
|
||||||
|
PickPhoto(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
imageButton(
|
||||||
|
title: 'Edit Filters',
|
||||||
|
width: size.width,
|
||||||
|
onPressed: () {
|
||||||
|
final pickImagesBloc = context
|
||||||
|
.read<PickImagesBloc>();
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => BlocProvider(
|
||||||
|
create: (context) =>
|
||||||
|
EditImageFilterBloc(),
|
||||||
|
child: EditImageFilter(
|
||||||
|
type:
|
||||||
|
state.file != null &&
|
||||||
|
state
|
||||||
|
.file!
|
||||||
|
.isNotEmpty
|
||||||
|
? EditImageType.file
|
||||||
|
: EditImageType.network,
|
||||||
|
url:
|
||||||
|
state.file != null &&
|
||||||
|
state
|
||||||
|
.file!
|
||||||
|
.isNotEmpty
|
||||||
|
? state.file!
|
||||||
|
: '${ApiUrls.baseUrl}${postCard!.pcImagePath}',
|
||||||
|
pickImagesBloc:
|
||||||
|
pickImagesBloc,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
imageButton(
|
),
|
||||||
title: 'Upload Again',
|
|
||||||
icon: Icons.refresh,
|
|
||||||
width: size.width,
|
|
||||||
),
|
|
||||||
imageButton(
|
|
||||||
title: 'Edit Filters',
|
|
||||||
width: size.width,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
);
|
||||||
],
|
},
|
||||||
),
|
),
|
||||||
SizedBox(height: 10.h),
|
SizedBox(height: 10.h),
|
||||||
Text(
|
Text(
|
||||||
@@ -275,7 +405,10 @@ class _EditPostcardViewState extends State<EditPostcardView> {
|
|||||||
countryName: _selectedCountry,
|
countryName: _selectedCountry,
|
||||||
);
|
);
|
||||||
editPostcardBloc.add(
|
editPostcardBloc.add(
|
||||||
EditPostCard(myPostCard: postCard!),
|
EditPostCard(
|
||||||
|
myPostCard: postCard!,
|
||||||
|
editImage: selectedImage,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -335,7 +468,7 @@ class _EditPostcardViewState extends State<EditPostcardView> {
|
|||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: width,
|
width: width,
|
||||||
child: OutlinedButton(
|
child: OutlinedButton(
|
||||||
onPressed: () {},
|
onPressed: onPressed,
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 16),
|
padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 16),
|
||||||
side: const BorderSide(color: Color(0xffF95F62)),
|
side: const BorderSide(color: Color(0xffF95F62)),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:citycards_customer/postcard/blocs/edit_postcard/edit_postcard_bloc.dart';
|
import 'package:citycards_customer/postcard/blocs/edit_postcard/edit_postcard_bloc.dart';
|
||||||
|
import 'package:citycards_customer/postcard/blocs/pick_images/pick_images_bloc.dart';
|
||||||
import 'package:citycards_customer/postcard/views/edit_postcard_view.dart';
|
import 'package:citycards_customer/postcard/views/edit_postcard_view.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
@@ -108,18 +109,18 @@ class _MyPostCardDraftViewState extends State<MyPostCardDraftView> {
|
|||||||
),
|
),
|
||||||
suffixIcon: _searchController.text.isNotEmpty
|
suffixIcon: _searchController.text.isNotEmpty
|
||||||
? IconButton(
|
? IconButton(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.clear,
|
Icons.clear,
|
||||||
color: Colors.black38,
|
color: Colors.black38,
|
||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_searchController.clear();
|
_searchController.clear();
|
||||||
context.read<MyPostCardBloc>().add(
|
context.read<MyPostCardBloc>().add(
|
||||||
const ClearDraftSearch(),
|
const ClearDraftSearch(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
filled: true,
|
filled: true,
|
||||||
fillColor: Colors.white,
|
fillColor: Colors.white,
|
||||||
@@ -150,7 +151,7 @@ class _MyPostCardDraftViewState extends State<MyPostCardDraftView> {
|
|||||||
onChanged: (query) {
|
onChanged: (query) {
|
||||||
context.read<MyPostCardBloc>().add(
|
context.read<MyPostCardBloc>().add(
|
||||||
SearchDraftPostCards(query: query),
|
SearchDraftPostCards(query: query),
|
||||||
);// To update clear button visibility
|
); // To update clear button visibility
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -185,55 +186,58 @@ class _MyPostCardDraftViewState extends State<MyPostCardDraftView> {
|
|||||||
color: const Color(0xffF95F62),
|
color: const Color(0xffF95F62),
|
||||||
child: state.draftPostCards.isEmpty
|
child: state.draftPostCards.isEmpty
|
||||||
? ListView(
|
? ListView(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
children: [
|
|
||||||
SizedBox(height: 100.h),
|
|
||||||
Center(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
SizedBox(height: 100.h),
|
||||||
Icons.search_off,
|
Center(
|
||||||
size: 48,
|
child: Column(
|
||||||
color: Colors.black26,
|
children: [
|
||||||
),
|
Icon(
|
||||||
SizedBox(height: 16.h),
|
Icons.search_off,
|
||||||
Text(
|
size: 48,
|
||||||
'No search available',
|
color: Colors.black26,
|
||||||
style: GoogleFonts.poppins(
|
|
||||||
fontSize: 16.sp,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Colors.black54,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 8.h),
|
|
||||||
if (state.draftSearchQuery.isNotEmpty)
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
horizontal: 32.w,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
'Try searching with different keywords',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: GoogleFonts.poppins(
|
|
||||||
fontSize: 14.sp,
|
|
||||||
color: Colors.black38,
|
|
||||||
),
|
),
|
||||||
),
|
SizedBox(height: 16.h),
|
||||||
|
Text(
|
||||||
|
'No search available',
|
||||||
|
style: GoogleFonts.poppins(
|
||||||
|
fontSize: 16.sp,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.black54,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 8.h),
|
||||||
|
if (state.draftSearchQuery.isNotEmpty)
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 32.w,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'Try searching with different keywords',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: GoogleFonts.poppins(
|
||||||
|
fontSize: 14.sp,
|
||||||
|
color: Colors.black38,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
)
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: ListView.builder(
|
: ListView.builder(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
padding: EdgeInsets.symmetric(horizontal: 12.w,vertical: 12.h),
|
padding: EdgeInsets.symmetric(
|
||||||
itemCount: state.draftPostCards.length,
|
horizontal: 12.w,
|
||||||
itemBuilder: (context, index) {
|
vertical: 12.h,
|
||||||
final postcard = state.draftPostCards[index];
|
),
|
||||||
return _buildDraftCard(context, postcard);
|
itemCount: state.draftPostCards.length,
|
||||||
},
|
itemBuilder: (context, index) {
|
||||||
),
|
final postcard = state.draftPostCards[index];
|
||||||
|
return _buildDraftCard(context, postcard);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
Positioned(
|
Positioned(
|
||||||
@@ -243,14 +247,14 @@ class _MyPostCardDraftViewState extends State<MyPostCardDraftView> {
|
|||||||
bottom: 0,
|
bottom: 0,
|
||||||
child: state.isDeleteLoading == true
|
child: state.isDeleteLoading == true
|
||||||
? Center(
|
? Center(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 25,
|
width: 25,
|
||||||
height: 25,
|
height: 25,
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
color: Color(0XFFF95F62),
|
color: Color(0XFFF95F62),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: SizedBox(),
|
: SizedBox(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -445,8 +449,16 @@ class _MyPostCardDraftViewState extends State<MyPostCardDraftView> {
|
|||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final result = await Navigator.of(context).push(
|
final result = await Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => BlocProvider(
|
builder: (_) => MultiBlocProvider(
|
||||||
create: (context) => EditPostcardBloc(),
|
providers: [
|
||||||
|
BlocProvider(
|
||||||
|
create: (context) => EditPostcardBloc(),
|
||||||
|
),
|
||||||
|
BlocProvider(
|
||||||
|
create: (context) => PickImagesBloc(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
|
||||||
child: EditPostcardView(myPostCard: postcard),
|
child: EditPostcardView(myPostCard: postcard),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -539,4 +551,4 @@ class _MyPostCardDraftViewState extends State<MyPostCardDraftView> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,9 +33,7 @@ class _EditYourdetailsState extends State<EditYourdetails> {
|
|||||||
String? _selectedState;
|
String? _selectedState;
|
||||||
String? _selectedCountry;
|
String? _selectedCountry;
|
||||||
|
|
||||||
final List<String> countries = [
|
final List<String> countries = ['Australia'];
|
||||||
'Australia',
|
|
||||||
];
|
|
||||||
|
|
||||||
final List<String> states = [
|
final List<String> states = [
|
||||||
'New South Wales',
|
'New South Wales',
|
||||||
@@ -51,8 +49,12 @@ class _EditYourdetailsState extends State<EditYourdetails> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedState = widget.selectedState;
|
_selectedState = states.contains(widget.selectedState)
|
||||||
_selectedCountry = widget.selectedCountry;
|
? widget.selectedState
|
||||||
|
: null;
|
||||||
|
_selectedCountry = countries.contains(widget.selectedCountry)
|
||||||
|
? widget.selectedCountry
|
||||||
|
: null;
|
||||||
});
|
});
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
@@ -257,10 +259,7 @@ class _EditYourdetailsState extends State<EditYourdetails> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
items: items.map((String item) {
|
items: items.map((String item) {
|
||||||
return DropdownMenuItem<String>(
|
return DropdownMenuItem<String>(value: item, child: Text(item));
|
||||||
value: item,
|
|
||||||
child: Text(item),
|
|
||||||
);
|
|
||||||
}).toList(),
|
}).toList(),
|
||||||
onChanged: onChanged,
|
onChanged: onChanged,
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
@@ -274,4 +273,4 @@ class _EditYourdetailsState extends State<EditYourdetails> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -830,7 +830,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.1"
|
version: "1.9.1"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ dependencies:
|
|||||||
bloc: ^9.2.0
|
bloc: ^9.2.0
|
||||||
csc_picker_plus: ^0.0.3
|
csc_picker_plus: ^0.0.3
|
||||||
flutter_slidable: ^4.0.3
|
flutter_slidable: ^4.0.3
|
||||||
|
path_provider: ^2.1.5
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user