1 Commits

Author SHA1 Message Date
Shreeyash Thorat
cdfb9c74ca upload, edit postcard image with filter 2026-02-17 15:15:21 +05:30
16 changed files with 1082 additions and 173 deletions

View File

@@ -1,8 +1,9 @@
class ApiUrls {
// static const baseUrl = "https://devapi.citycards.betadelivery.com";//Normal API
// static const baseUrl = "https://testingapi.citycards.betadelivery.com";// Test API
static const baseUrl = "https://uatapi.citycard.betadelivery.com";// Production Lvl API
static const baseUrl =
"https://devapi.citycards.betadelivery.com"; //Normal API
//static const baseUrl =
// "https://testingapi.citycards.betadelivery.com"; // Test API
// static const baseUrl = "https://uatapi.citycard.betadelivery.com";// Production Lvl API
static const refreshToken = "$baseUrl/auth/refresh";

View 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.01.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
}
});
}
}

View File

@@ -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});
}

View File

@@ -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 {}

View File

@@ -15,6 +15,7 @@ class EditPostcardBloc extends Bloc<EditPostcardEvent, EditPostcardState> {
emit(EditPostcardLoading());
await MyPostCardsRepository().editMyPostCards(
postcard: event.myPostCard,
image: event.editImage,
);
log("Edit PostCard Successfully");
emit(EditPostcardSuccessfull());

View File

@@ -9,5 +9,6 @@ class EditPostcardEvent extends Equatable {
class EditPostCard extends EditPostcardEvent {
final MyPostCard myPostCard;
const EditPostCard({required this.myPostCard});
final String? editImage;
const EditPostCard({required this.myPostCard, this.editImage});
}

View 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));
}
});
}
}

View 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});
}

View 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];
}

View File

@@ -20,7 +20,10 @@ class MyPostCardsRepository {
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 {
final formData = FormData();
@@ -44,16 +47,15 @@ class MyPostCardsRepository {
if (postcard.address2.isNotEmpty) {
formData.fields.add(MapEntry('address2', postcard.address2));
}
// final fileName = postcard.pcImagePath.split('/').last;
// formData.files.add(
// MapEntry(
// 'pcImage',
// await MultipartFile.fromFile(
// postcard.pcImagePath,
// filename: fileName,
// ),
// ),
// );
if (image != null && image.isNotEmpty) {
final fileName = image.split('/').last;
formData.files.add(
MapEntry(
'pcImage',
await MultipartFile.fromFile(image, filename: fileName),
),
);
}
await _apiService.putApi(
url: '${ApiUrls.editPostcard}/${postcard.id}',
data: formData,

View 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);
}
}
}

View File

@@ -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/pick_images/pick_images_bloc.dart';
import 'package:citycards_customer/postcard/models/my_postcard_model.dart';
import 'package:citycards_customer/postcard/widgets/dotted_border_container.dart';
import 'package:flutter/material.dart';
@@ -12,6 +16,7 @@ import '../../common_packages/custom_text.dart';
import '../../networkApiServices/api_urls.dart';
import '../widgets/edit_post_card/edit_message.dart';
import '../widgets/edit_post_card/your_details.dart';
import 'edit_image_filter.dart';
class EditPostcardView extends StatefulWidget {
final MyPostCard myPostCard;
@@ -58,6 +63,8 @@ class _EditPostcardViewState extends State<EditPostcardView> {
super.initState();
}
String? selectedImage;
@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
@@ -122,90 +129,213 @@ class _EditPostcardViewState extends State<EditPostcardView> {
),
),
SizedBox(height: 10.h),
Row(
// crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: CustomPaint(
painter: DottedBorderPainter(),
child: Container(
padding: EdgeInsets.all(10),
height: size.width * 0.45,
width: size.width,
constraints: BoxConstraints(minHeight: 150),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.network(
'${ApiUrls.baseUrl}${postCard!.pcImagePath}',
BlocConsumer<PickImagesBloc, PickImagesState>(
listener: (ctx, state) {
if (state.file != null && state.file!.isNotEmpty) {
setState(() {
selectedImage =
state.filteredFile ?? state.file!;
});
}
},
builder: (context, state) {
return Row(
children: [
Expanded(
child: CustomPaint(
painter: DottedBorderPainter(),
child: Container(
padding: EdgeInsets.all(10),
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,
),
constraints: BoxConstraints(
minHeight: 150,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child:
state.file != null &&
state.file!.isNotEmpty
? Image.file(
height: size.width * 0.45,
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(
height: size.width * 0.5,
width: size.width,
constraints: BoxConstraints(minHeight: 150),
padding: EdgeInsets.all(10),
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
imageButton(
title: 'Take a photo',
icon: Icons.camera_alt_outlined,
width: size.width,
Expanded(
child: Container(
height: size.width * 0.5,
width: size.width,
constraints: BoxConstraints(minHeight: 150),
padding: EdgeInsets.all(10),
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
imageButton(
title: 'Take a photo',
icon: Icons.camera_alt_outlined,
width: size.width,
onPressed: () {
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),
Text(
@@ -275,7 +405,10 @@ class _EditPostcardViewState extends State<EditPostcardView> {
countryName: _selectedCountry,
);
editPostcardBloc.add(
EditPostCard(myPostCard: postCard!),
EditPostCard(
myPostCard: postCard!,
editImage: selectedImage,
),
);
}
},
@@ -335,7 +468,7 @@ class _EditPostcardViewState extends State<EditPostcardView> {
return SizedBox(
width: width,
child: OutlinedButton(
onPressed: () {},
onPressed: onPressed,
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 16),
side: const BorderSide(color: Color(0xffF95F62)),

View File

@@ -1,6 +1,7 @@
import 'dart:developer';
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:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -108,18 +109,18 @@ class _MyPostCardDraftViewState extends State<MyPostCardDraftView> {
),
suffixIcon: _searchController.text.isNotEmpty
? IconButton(
icon: Icon(
Icons.clear,
color: Colors.black38,
size: 20,
),
onPressed: () {
_searchController.clear();
context.read<MyPostCardBloc>().add(
const ClearDraftSearch(),
);
},
)
icon: Icon(
Icons.clear,
color: Colors.black38,
size: 20,
),
onPressed: () {
_searchController.clear();
context.read<MyPostCardBloc>().add(
const ClearDraftSearch(),
);
},
)
: null,
filled: true,
fillColor: Colors.white,
@@ -150,7 +151,7 @@ class _MyPostCardDraftViewState extends State<MyPostCardDraftView> {
onChanged: (query) {
context.read<MyPostCardBloc>().add(
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),
child: state.draftPostCards.isEmpty
? ListView(
physics: const AlwaysScrollableScrollPhysics(),
children: [
SizedBox(height: 100.h),
Center(
child: Column(
physics: const AlwaysScrollableScrollPhysics(),
children: [
Icon(
Icons.search_off,
size: 48,
color: Colors.black26,
),
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,
SizedBox(height: 100.h),
Center(
child: Column(
children: [
Icon(
Icons.search_off,
size: 48,
color: Colors.black26,
),
),
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(
physics: const AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.symmetric(horizontal: 12.w,vertical: 12.h),
itemCount: state.draftPostCards.length,
itemBuilder: (context, index) {
final postcard = state.draftPostCards[index];
return _buildDraftCard(context, postcard);
},
),
physics: const AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.symmetric(
horizontal: 12.w,
vertical: 12.h,
),
itemCount: state.draftPostCards.length,
itemBuilder: (context, index) {
final postcard = state.draftPostCards[index];
return _buildDraftCard(context, postcard);
},
),
),
Positioned(
@@ -243,14 +247,14 @@ class _MyPostCardDraftViewState extends State<MyPostCardDraftView> {
bottom: 0,
child: state.isDeleteLoading == true
? Center(
child: SizedBox(
width: 25,
height: 25,
child: CircularProgressIndicator(
color: Color(0XFFF95F62),
),
),
)
child: SizedBox(
width: 25,
height: 25,
child: CircularProgressIndicator(
color: Color(0XFFF95F62),
),
),
)
: SizedBox(),
),
],
@@ -445,8 +449,16 @@ class _MyPostCardDraftViewState extends State<MyPostCardDraftView> {
onPressed: () async {
final result = await Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => BlocProvider(
create: (context) => EditPostcardBloc(),
builder: (_) => MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => EditPostcardBloc(),
),
BlocProvider(
create: (context) => PickImagesBloc(),
),
],
child: EditPostcardView(myPostCard: postcard),
),
),
@@ -539,4 +551,4 @@ class _MyPostCardDraftViewState extends State<MyPostCardDraftView> {
),
);
}
}
}

View File

@@ -33,9 +33,7 @@ class _EditYourdetailsState extends State<EditYourdetails> {
String? _selectedState;
String? _selectedCountry;
final List<String> countries = [
'Australia',
];
final List<String> countries = ['Australia'];
final List<String> states = [
'New South Wales',
@@ -51,8 +49,12 @@ class _EditYourdetailsState extends State<EditYourdetails> {
@override
void initState() {
setState(() {
_selectedState = widget.selectedState;
_selectedCountry = widget.selectedCountry;
_selectedState = states.contains(widget.selectedState)
? widget.selectedState
: null;
_selectedCountry = countries.contains(widget.selectedCountry)
? widget.selectedCountry
: null;
});
super.initState();
}
@@ -257,10 +259,7 @@ class _EditYourdetailsState extends State<EditYourdetails> {
),
),
items: items.map((String item) {
return DropdownMenuItem<String>(
value: item,
child: Text(item),
);
return DropdownMenuItem<String>(value: item, child: Text(item));
}).toList(),
onChanged: onChanged,
validator: (value) {
@@ -274,4 +273,4 @@ class _EditYourdetailsState extends State<EditYourdetails> {
),
);
}
}
}

View File

@@ -830,7 +830,7 @@ packages:
source: hosted
version: "1.9.1"
path_provider:
dependency: transitive
dependency: "direct main"
description:
name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"

View File

@@ -62,6 +62,7 @@ dependencies:
bloc: ^9.2.0
csc_picker_plus: ^0.0.3
flutter_slidable: ^4.0.3
path_provider: ^2.1.5
dev_dependencies:
flutter_test: