Completed Itinerary Creation Model

This commit is contained in:
Vinayakkadge04
2025-10-15 20:30:49 +05:30
parent f0cde5d827
commit 255556597d
44 changed files with 1287 additions and 2 deletions

BIN
assets/icons/active.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
assets/icons/adventure.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
assets/icons/balanced.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
assets/icons/calender.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
assets/icons/halal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
assets/icons/hi_rate1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
assets/icons/hi_rate2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
assets/icons/hi_rate3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
assets/icons/hi_rate4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
assets/icons/kosher.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
assets/icons/location.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
assets/icons/pesc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
assets/icons/relaxed.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
assets/icons/tr_rate1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
assets/icons/tr_rate2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
assets/icons/tr_rate3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
assets/icons/tr_rate4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
assets/icons/veg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
assets/icons/vegan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
assets/icons/wi_rate1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
assets/icons/wi_rate2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
assets/icons/wi_rate3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
assets/icons/wi_rate4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -0,0 +1,51 @@
import 'package:citycards_customer/common_packages/custom_text.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class CustomFilledButton extends StatelessWidget {
final double? width;
final String label;
final bool? showArrow;
final GestureTapCallback onTap;
CustomFilledButton({
super.key,
this.width = 266,
required this.onTap,
required this.label,
this.showArrow = false,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
height: 42.h,
width: width,
decoration: BoxDecoration(
color: Color(0xFFF95F62),
borderRadius: BorderRadius.circular(38.r),
),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CustomText(
text: label,
color: Colors.white,
size: 16.sp ,
weight: FontWeight.w500,
),
if(showArrow!)
SizedBox(width: 8,),
if(showArrow!)
Icon(Icons.arrow_forward_ios_rounded,size: 18.sp, color: Colors.white,)
],
),
),
),
);
}
}

View File

@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class CustomText extends StatelessWidget {
final FontWeight? weight;

View File

@@ -3,6 +3,9 @@ import 'package:citycards_customer/common_bloc/language_selection_bloc.dart';
import 'package:citycards_customer/contact_us/contact_us_view.dart';
import 'package:citycards_customer/edit_profile/edit_profile_view.dart';
import 'package:citycards_customer/faq/faq_view.dart';
import 'package:citycards_customer/itinerary_creation/bloc/itinerary_steps_selection_bloc.dart';
import 'package:citycards_customer/itinerary_creation/itinerary_creation_start_view.dart';
import 'package:citycards_customer/itinerary_creation/itinerary_creation_view.dart';
import 'package:citycards_customer/privacy/privacy_view.dart';
import 'package:citycards_customer/terms_and_condition/terms_and_condition_view.dart';
import 'package:flutter/material.dart';
@@ -65,6 +68,21 @@ class AppRouter {
},
);
case RouteConstants.itineraryCreationStart:
return MaterialPageRoute(
builder: (_) {
return ItineraryCreationStartPage();
},
);
case RouteConstants.itineraryCreation:
return MaterialPageRoute(
builder: (_) {
return BlocProvider(create: (_) => ItineraryStepNavigationBloc(),
child: ItineraryCreationPage(),
);
},
);
default:
return MaterialPageRoute(
builder: (_) =>

View File

@@ -9,4 +9,9 @@ class RouteConstants {
static const String termsAndCondition = '/termsAndCondition';
static const String privacyPolicy = '/privacyPolicy';
static const String faq = '/faq';
/****************************** ITINERARY CREATION ************************************/
static const String itineraryCreationStart = '/itineraryCreationStart';
static const String itineraryCreation = '/itineraryCreation';
}

View File

@@ -0,0 +1,36 @@
import 'package:flutter_bloc/flutter_bloc.dart';
abstract class ItineraryStepNavigationEvent {}
class ItineraryStepNavigationNextEvent extends ItineraryStepNavigationEvent {}
class ItineraryStepNavigationPreviousEvent extends ItineraryStepNavigationEvent {}
class ItineraryStepNavigationState {
final int selectedIndex;
const ItineraryStepNavigationState(this.selectedIndex);
}
class ItineraryStepNavigationBloc
extends Bloc<ItineraryStepNavigationEvent, ItineraryStepNavigationState> {
final int maxIndex; // maximum index allowed
ItineraryStepNavigationBloc({this.maxIndex = 2})
: super(const ItineraryStepNavigationState(0)) {
on<ItineraryStepNavigationNextEvent>((event, emit) {
final nextIndex = state.selectedIndex + 1;
if (nextIndex <= 10) {
emit(ItineraryStepNavigationState(nextIndex));
}
});
on<ItineraryStepNavigationPreviousEvent>((event, emit) {
final prevIndex = state.selectedIndex - 1;
if (prevIndex >= 0) {
emit(ItineraryStepNavigationState(prevIndex));
}
});
}
}

View File

@@ -0,0 +1,90 @@
import 'package:citycards_customer/common_packages/custom_filled_button.dart';
import 'package:citycards_customer/common_packages/custom_text.dart';
import 'package:citycards_customer/core/route_constants.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class ItineraryCreationStartPage extends StatelessWidget {
const ItineraryCreationStartPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFFFFF5F5),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 103.h,
width: 103.w,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(24.r),
boxShadow: [
BoxShadow(
color: Colors.black12,
offset: Offset(0, 4),
blurRadius: 5,
),
],
),
child: Center(
child: Image.asset("assets/icons/magic_creation.png", scale: 4),
),
),
SizedBox(height: 34.h),
Text.rich(
TextSpan(
children: [
TextSpan(
text: "Create your",
style: TextStyle(
color: Color(0xFF101828),
fontSize: 24.sp,
fontWeight: FontWeight.w500,
),
),
TextSpan(
text: " magic itinerary",
style: TextStyle(
color: Color(0xFFF95F62),
fontSize: 24.sp,
fontWeight: FontWeight.w500,
),
),
],
),
),
SizedBox(height: 13.h),
Padding(
padding: EdgeInsets.symmetric(horizontal: 25.w),
child: Text(
"Answer a few quick questions and we'll craft a personalized travel experience just for you ✨",
style: TextStyle(fontSize: 14, color: Color(0xFF4A5565)),
textAlign: TextAlign.center,
),
),
SizedBox(height: 47.h),
CustomFilledButton(
onTap: () {
Navigator.pushNamed(context, RouteConstants.itineraryCreation);
},
showArrow: true,
label: "Lets Get Started",
),
SizedBox(height: 38.h),
CustomText(
text: "Takes only 2 minutes ⏱️",
color: Color(0xFF6A7282),
size: 14.sp,
),
],
),
),
);
}
}

View File

@@ -0,0 +1,74 @@
import 'package:citycards_customer/common_packages/custom_text.dart';
import 'package:citycards_customer/itinerary_creation/bloc/itinerary_steps_selection_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class ArtGallerySelectionView extends StatelessWidget {
ArtGallerySelectionView({super.key});
final List<Map<String, String>> options = [
{"icon": "😴", "name": "Not interested"},
{"icon": "🤔", "name": "Maybe one or two"},
{"icon": "😊", "name": "Yes, sounds good!"},
{"icon": "🤩", "name": "Absolutely love them!"},
];
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"👋 Hello! We'd love to know more about you. Do you enjoy visiting museums and art galleries?",
style: TextStyle(
color: Color(0xFF101828),
fontSize: 24.sp,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
SizedBox(height: 32.h),
...List.generate(options.length, (index) {
final item = options[index];
return Padding(
padding: EdgeInsets.only(bottom: 12.h),
child: GestureDetector(
onTap: () {
context.read<ItineraryStepNavigationBloc>().add(
ItineraryStepNavigationNextEvent(),
);
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 24.w),
height: 83.h,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(28.r),
),
alignment: Alignment.center,
child: Row(
children: [
CustomText(
text: item['icon'] ?? "",
size: 36.sp,
color: const Color(0xFF101828),
weight: FontWeight.w500,
),
SizedBox(width: 16.w,),
CustomText(
text: item['name'] ?? "",
size: 16.sp,
color: const Color(0xFF101828),
weight: FontWeight.w500,
),
],
),
),
),
);
}),
],
);
}
}

View File

@@ -0,0 +1,116 @@
import 'package:citycards_customer/common_packages/custom_filled_button.dart';
import 'package:citycards_customer/common_packages/custom_text.dart';
import 'package:citycards_customer/itinerary_creation/bloc/itinerary_steps_selection_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class CitySelectionView extends StatelessWidget {
CitySelectionView({super.key});
final List<Map<String, String>> cityList = [
{"flag": "🇫🇷", "city": "Paris"},
{"flag": "🇯🇵", "city": "Tokyo"},
{"flag": "🇺🇸", "city": "New York"},
{"flag": "🇬🇧", "city": "London"},
{"flag": "🇪🇸", "city": "Barcelona"},
{"flag": "🇦🇪", "city": "Dubai"},
{"flag": "🇮🇹", "city": "Rome"},
{"flag": "🇹🇭", "city": "Bangkok"},
];
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"👋 Hello! We'd love to know more about you. Which city are you visiting?",
style: TextStyle(
color: Color(0xFF101828),
fontSize: 24.sp,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
SizedBox(height: 32.h),
Container(
height: 56.h,
width: double.infinity,
padding: EdgeInsets.only(left: 20.w),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Color(0xFFF95F62)),
borderRadius: BorderRadius.circular(28.r),
),
child: Row(
children: [
Image.asset("assets/icons/location.png", scale: 4),
SizedBox(width: 12.w),
CustomText(
text: "Tokyo",
color: Color(0xFF737373),
size: 14.sp,
),
],
),
),
SizedBox(height: 16.h),
Align(
alignment: Alignment.topLeft,
child: CustomText(
text: "Popular destinations",
size: 16.sp,
weight: FontWeight.w500,
color: Color(0xFF6A7282),
),
),
SizedBox(height: 10.h),
SizedBox(
height: 175.h,
child: GridView.builder(
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
mainAxisSpacing: 16.h,
crossAxisSpacing: 16.w,
),
itemCount: cityList.length,
itemBuilder: (context, index) {
final item = cityList[index];
return Container(
height: 78.h,
width: 76.w,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CustomText(text: item['flag'] ?? ""),
SizedBox(height: 4.h),
CustomText(
text: item['city'] ?? "",
size: 12.sp,
color: Color(0xFF364153),
),
],
),
);
},
),
),
SizedBox(height: 40.h),
CustomFilledButton(onTap: () {
context.read<ItineraryStepNavigationBloc>().add(
ItineraryStepNavigationNextEvent(),
);
}, label: "Continue", showArrow: true),
],
),
);
}
}

View File

@@ -0,0 +1,65 @@
import 'package:citycards_customer/common_packages/custom_filled_button.dart';
import 'package:citycards_customer/common_packages/custom_text.dart';
import 'package:citycards_customer/itinerary_creation/bloc/itinerary_steps_selection_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class DateSelectionView extends StatelessWidget {
const DateSelectionView({super.key});
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"👋 Hello! We'd love to know more about you. When are you visiting?",
style: TextStyle(
color: Color(0xFF101828),
fontSize: 24.sp,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
SizedBox(height: 32.h),
Container(
height: 90.h,
padding: EdgeInsets.symmetric(horizontal: 20.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(28),
border: Border.all(color: Color(0xFFF95F62), width: 1.1.w),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Image.asset("assets/icons/calender.png", scale: 4),
CustomText(
text: "Wednesday, October 15, 2025",
size: 14.sp,
color: Color(0xFF101828),
),
Icon(Icons.check_circle, color: Color(0xFFF95F62)),
],
),
),
SizedBox(height: 32.h),
CustomFilledButton(
onTap: () {
context.read<ItineraryStepNavigationBloc>().add(
ItineraryStepNavigationNextEvent(),
);
},
label: "Continue",
showArrow: true,
),
],
),
);
}
}

View File

@@ -0,0 +1,107 @@
import 'package:citycards_customer/common_packages/custom_filled_button.dart';
import 'package:citycards_customer/common_packages/custom_text.dart';
import 'package:citycards_customer/itinerary_creation/bloc/itinerary_steps_selection_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class DietarySelectionView extends StatefulWidget {
const DietarySelectionView({super.key});
@override
State<DietarySelectionView> createState() => _DietarySelectionViewState();
}
class _DietarySelectionViewState extends State<DietarySelectionView> {
int selectedIndex = -1;
final List<Map<String, String>> options = [
{
"icon": "assets/icons/no_restrictions_food.png",
"name": "No Restrictions",
},
{"icon": "assets/icons/veg.png", "name": "Vegetarian"},
{"icon": "assets/icons/vegan.png", "name": "Vegan"},
{"icon": "assets/icons/pesc.png", "name": "Pescatarian"},
{"icon": "assets/icons/halal.png", "name": "Halal"},
{"icon": "assets/icons/kosher.png", "name": "Kosher"},
];
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"👋 Hello! We'd love to know more about you. Do you follow any dietary preferences?",
style: TextStyle(
color: Color(0xFF101828),
fontSize: 24.sp,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
SizedBox(height: 12.h),
CustomText(
text: "Select all that apply",
size: 14.sp,
color: const Color(0xFF6A7282),
),
SizedBox(height: 38.h),
SizedBox(
height: 350.h,
child: GridView.builder(
physics: NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
mainAxisSpacing: 12,
crossAxisSpacing: 16,
crossAxisCount: 2,
childAspectRatio: 1.7,
),
itemCount: options.length,
itemBuilder: (BuildContext context, int index) {
final item = options[index];
return GestureDetector(
onTap: () {},
child: Container(
width: 168.w,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(24),
),
alignment: Alignment.center,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(item["icon"] ?? "", scale: 4),
SizedBox(height: 8),
CustomText(
text: item["name"] ?? "",
size: 16.sp,
weight: FontWeight.w500,
color: const Color(0xFF364153),
),
],
),
),
);
},
),
),
SizedBox(height: 41.h),
CustomFilledButton(
onTap: () {
context.read<ItineraryStepNavigationBloc>().add(
ItineraryStepNavigationNextEvent(),
);
},
label: "Continue",
showArrow: true,
),
],
);
}
}

View File

@@ -0,0 +1,72 @@
import 'package:citycards_customer/common_packages/custom_filled_button.dart';
import 'package:citycards_customer/common_packages/custom_text.dart';
import 'package:citycards_customer/itinerary_creation/bloc/itinerary_steps_selection_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class EnergySelectionView extends StatelessWidget {
EnergySelectionView({super.key});
final List<Map<String, String>> options = [
{"img": "assets/icons/relaxed.png", "name": "Relaxed & Chill"},
{"img": "assets/icons/balanced.png", "name": "Balanced Mix"},
{"img": "assets/icons/active.png", "name": "Active & Energetic"},
{"img": "assets/icons/adventure.png", "name": "Full Adventure"},
];
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"👋 Hello! We'd love to know more about you. What kind of energy are you after on this trip?",
style: TextStyle(
color: Color(0xFF101828),
fontSize: 24.sp,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
SizedBox(height: 32.h),
...List.generate(options.length, (index) {
final item = options[index];
return Padding(
padding: EdgeInsets.only(bottom: 12.h),
child: GestureDetector(
onTap: (){
context.read<ItineraryStepNavigationBloc>().add(
ItineraryStepNavigationNextEvent(),
);
},
child: Container(
height: 86.h,
padding: EdgeInsets.symmetric(horizontal: 24.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(28.r),
),
alignment: Alignment.center,
child: Row(
children: [
Image.asset(item['img'] ?? "",scale: 4,),
SizedBox(width: 15,),
CustomText(
text: item['name'] ?? "",
size: 14.sp,
color: const Color(0xFF101828),
weight: FontWeight.w500,
),
],
),
),
),
);
}),
],
);
}
}

View File

@@ -0,0 +1,82 @@
import 'package:citycards_customer/common_packages/custom_text.dart';
import 'package:citycards_customer/itinerary_creation/bloc/itinerary_steps_selection_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class HistoricalSiteRatingView extends StatelessWidget {
HistoricalSiteRatingView({super.key});
final List<Map<String, String>> historicalRatingOption = [
{"icon": "assets/icons/hi_rate1.png", "option": "Not interested"},
{"icon": "assets/icons/hi_rate2.png", "option": "A few key sites"},
{"icon": "assets/icons/hi_rate3.png", "option": "Yes, I enjoy them!"},
{"icon": "assets/icons/hi_rate4.png", "option": "Can't miss them!"},
];
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CustomText(text: "🏛️", size: 36.sp),
SizedBox(height: 9.h),
Text(
"Do you enjoy cultural landmarks and historical sites?",
style: TextStyle(
color: Color(0xFF101828),
fontSize: 24.sp,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
SizedBox(height: 30.h),
...List.generate(historicalRatingOption.length, (index) {
final item = historicalRatingOption[index];
return Padding(
padding: EdgeInsets.only(bottom: 12.h),
child: GestureDetector(
onTap: () {
context.read<ItineraryStepNavigationBloc>().add(
ItineraryStepNavigationNextEvent(),
);
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 24.w),
height: 82.h,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16.r),
border: Border.all(
color: Color(0xFFE5E7EB),
width: 1.1
),
boxShadow: [
BoxShadow(
color: Colors.black12,
offset: Offset(1,2),
blurRadius: 1,
)
]
),
alignment: Alignment.center,
child: Row(
children: [
Image.asset(item['icon']?? "", scale: 4,),
SizedBox(width: 16.w,),
CustomText(
text: item['option'] ?? "",
size: 16.sp,
color: const Color(0xFF101828),
weight: FontWeight.w500,
),
],
),
),
),
);
}),
],
);
}
}

View File

@@ -0,0 +1,135 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:citycards_customer/common_packages/custom_text.dart';
import 'package:citycards_customer/common_packages/custom_filled_button.dart';
class ItineraryCompletionView extends StatelessWidget {
const ItineraryCompletionView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFFFF5F5),
body: SingleChildScrollView(
child: Column(
children: [
Column(
children: [
SizedBox(height: 26.h),
CustomText(text: "🎉", size: 60.sp),
SizedBox(height: 32.h),
Text(
"All set! Your travel profile is complete",
textAlign: TextAlign.center,
style: TextStyle(
color: const Color(0xFF101828),
fontSize: 24.sp,
fontWeight: FontWeight.w500,
),
),
SizedBox(height: 4.h),
Text(
"Weve got everything we need to plan your perfect trip",
style: TextStyle(color: Color(0xFF6A7282), fontSize: 14),
textAlign: TextAlign.center,
),
SizedBox(height: 32.h),
Container(
width: double.infinity,
padding: EdgeInsets.symmetric(
horizontal: 16.w,
vertical: 20.h,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(24.r),
border: Border.all(color: Color(0xFFF3F4F6), width: 1.1),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomText(
text: "Your Profile:",
size: 16.sp,
weight: FontWeight.w500,
color: const Color(0xFF364153),
),
SizedBox(height: 16.h),
_buildProfileRow("Visit Date", "Oct 15, 2025"),
_buildProfileRow("City", "Tokyo"),
_buildProfileRow("Energy", "Relaxed"),
_buildProfileRow("With kids", "Yes"),
_buildProfileRow("Dietary", "Vegan"),
_buildProfileRow("Museums", ""),
_buildProfileRow("Scenic", ""),
_buildProfileRow("Cultural", "⭐⭐⭐"),
_buildProfileRow("Wildlife", "⭐⭐"),
_buildProfileRow("Shopping", "⭐⭐⭐"),
],
),
),
SizedBox(height: 32.h),
OutlinedButton(
style: OutlinedButton.styleFrom(
side: const BorderSide(
color: Color(0xFFE5E7EB),
width: 1.1,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(40),
),
minimumSize: Size(double.infinity, 42.h),
),
onPressed: () {
// Reset or restart flow
},
child: CustomText(
text: "Start Over",
size: 16.sp,
color: const Color(0xFF364153),
),
),
SizedBox(height: 12.h),
CustomFilledButton(
width: double.infinity,
label: "Get My Trip Plan",
showArrow: true,
onTap: () {
// Navigate to next step
},
),
],
),
SizedBox(height: 32.h),
// Profile summary card
],
),
),
);
}
Widget _buildProfileRow(String title, String value) {
return Container(
height: 44.h,
margin: EdgeInsets.only(bottom: 8.h),
padding: EdgeInsets.symmetric(horizontal: 12.w),
decoration: BoxDecoration(
color: const Color(0xFFF9FAFB),
borderRadius: BorderRadius.circular(16.r),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CustomText(text: title, size: 14.sp, color: const Color(0xFF4A5565)),
CustomText(
text: value,
size: 14.sp,
weight: FontWeight.w400,
color: const Color(0xFF101828),
),
],
),
);
}
}

View File

@@ -0,0 +1,73 @@
import 'package:citycards_customer/common_packages/custom_filled_button.dart';
import 'package:citycards_customer/common_packages/custom_text.dart';
import 'package:citycards_customer/itinerary_creation/bloc/itinerary_steps_selection_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class KidsSelectionView extends StatelessWidget {
KidsSelectionView({super.key});
final List<Map<String, String>> options = [
{"icon": "🎈", "option": "Yes!"},
{"icon": "🎒", "option": "No"},
];
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"👋 Hello! We'd love to know more about you. Are you travelling with kids?",
style: TextStyle(
color: Color(0xFF101828),
fontSize: 24.sp,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
SizedBox(height: 40.h),
...List.generate(options.length, (index) {
final item = options[index];
return Padding(
padding: EdgeInsets.only(bottom: 12.h),
child: GestureDetector(
onTap: () {
context.read<ItineraryStepNavigationBloc>().add(
ItineraryStepNavigationNextEvent(),
);
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 24.w),
height: 82.h,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(28.r),
),
alignment: Alignment.center,
child: Row(
children: [
CustomText(
text: item["icon"] ?? "",
size: 36.sp,
color: const Color(0xFF101828),
weight: FontWeight.w500,
),
SizedBox(width: 16.w),
CustomText(
text: item["option"] ?? "",
size: 14.sp,
color: const Color(0xFF101828),
weight: FontWeight.w500,
),
],
),
),
),
);
}),
],
);
}
}

View File

@@ -0,0 +1,74 @@
import 'package:citycards_customer/common_packages/custom_text.dart';
import 'package:citycards_customer/itinerary_creation/bloc/itinerary_steps_selection_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class ScenicViewpointsRatingView extends StatelessWidget {
ScenicViewpointsRatingView({super.key});
final List<Map<String, String>> options = [
{"icon": "😐", "name": "Not my thing"},
{"icon": "📸", "name": "If we have time"},
{"icon": "😍", "name": "Yes, definitely!"},
{"icon": "🌄", "name": "Must-have!"},
];
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"👋 Hello! We'd love to know more about you. Do you enjoy visiting museums and art galleries?",
style: TextStyle(
color: Color(0xFF101828),
fontSize: 24.sp,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
SizedBox(height: 32.h),
...List.generate(options.length, (index) {
final item = options[index];
return Padding(
padding: EdgeInsets.only(bottom: 12.h),
child: GestureDetector(
onTap: () {
context.read<ItineraryStepNavigationBloc>().add(
ItineraryStepNavigationNextEvent(),
);
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 24.w),
height: 83.h,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(28.r),
),
alignment: Alignment.center,
child: Row(
children: [
CustomText(
text: item['icon'] ?? "",
size: 36.sp,
color: const Color(0xFF101828),
weight: FontWeight.w500,
),
SizedBox(width: 16.w,),
CustomText(
text: item['name'] ?? "",
size: 16.sp,
color: const Color(0xFF101828),
weight: FontWeight.w500,
),
],
),
),
),
);
}),
],
);
}
}

View File

@@ -0,0 +1,83 @@
import 'package:citycards_customer/common_packages/custom_text.dart';
import 'package:citycards_customer/itinerary_creation/bloc/itinerary_steps_selection_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class ShoppingRatingView extends StatelessWidget {
ShoppingRatingView({super.key});
final List<Map<String, String>> shoppingRatingOption = [
{"icon": "assets/icons/tr_rate1.png", "option": "Not interested"},
{"icon": "assets/icons/tr_rate2.png", "option": "Just for souvenirs"},
{"icon": "assets/icons/tr_rate3.png", "option": "Love browsing!"},
{"icon": "assets/icons/tr_rate4.png", "option": "Shopping is a must!"},
];
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CustomText(text: "🛒", size: 36.sp),
SizedBox(height: 9.h),
Text(
"How do you feel about shopping during your trip?",
style: TextStyle(
color: Color(0xFF101828),
fontSize: 24.sp,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
SizedBox(height: 30.h),
...List.generate(shoppingRatingOption.length, (index) {
final item = shoppingRatingOption[index];
return Padding(
padding: EdgeInsets.only(bottom: 12.h),
child: GestureDetector(
onTap: () {
context.read<ItineraryStepNavigationBloc>().add(
ItineraryStepNavigationNextEvent(),
);
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 24.w),
height: 82.h,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16.r),
border: Border.all(
color: Color(0xFFE5E7EB),
width: 1.1
),
boxShadow: [
BoxShadow(
color: Colors.black12,
offset: Offset(1,2),
blurRadius: 1,
)
]
),
alignment: Alignment.center,
child: Row(
children: [
Image.asset(item['icon']?? "", scale: 4,),
SizedBox(width: 16.w,),
CustomText(
text: item['option'] ?? "",
size: 16.sp,
color: const Color(0xFF101828),
weight: FontWeight.w500,
),
],
),
),
),
);
}),
],
);
}
}

View File

@@ -0,0 +1,83 @@
import 'package:citycards_customer/common_packages/custom_text.dart';
import 'package:citycards_customer/itinerary_creation/bloc/itinerary_steps_selection_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class WildlifeRatingView extends StatelessWidget {
WildlifeRatingView({super.key});
final List<Map<String, String>> wildlifeRatingOption = [
{"icon": "assets/icons/wi_rate1.png", "option": "No thanks"},
{"icon": "assets/icons/wi_rate2.png", "option": "If it happens naturally"},
{"icon": "assets/icons/wi_rate3.png", "option": "Yes, would be nice!"},
{"icon": "assets/icons/wi_rate4.png", "option": "Absolutely essential!"},
];
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CustomText(text: "🦒", size: 36.sp),
SizedBox(height: 9.h),
Text(
"Would you like to include wildlife experiences?",
style: TextStyle(
color: Color(0xFF101828),
fontSize: 24.sp,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
SizedBox(height: 30.h),
...List.generate(wildlifeRatingOption.length, (index) {
final item = wildlifeRatingOption[index];
return Padding(
padding: EdgeInsets.only(bottom: 12.h),
child: GestureDetector(
onTap: () {
context.read<ItineraryStepNavigationBloc>().add(
ItineraryStepNavigationNextEvent(),
);
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 24.w),
height: 82.h,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16.r),
border: Border.all(
color: Color(0xFFE5E7EB),
width: 1.1
),
boxShadow: [
BoxShadow(
color: Colors.black12,
offset: Offset(1,2),
blurRadius: 1,
)
]
),
alignment: Alignment.center,
child: Row(
children: [
Image.asset(item['icon']?? "", scale: 4,),
SizedBox(width: 16.w,),
CustomText(
text: item['option'] ?? "",
size: 16.sp,
color: const Color(0xFF101828),
weight: FontWeight.w500,
),
],
),
),
),
);
}),
],
);
}
}

View File

@@ -0,0 +1,122 @@
import 'package:citycards_customer/itinerary_creation/bloc/itinerary_steps_selection_bloc.dart';
import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/art_gallery_selection_view.dart';
import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/city_selection_view.dart';
import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/date_selection_view.dart';
import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/dietary_selection_view.dart';
import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/energy_selection_view.dart';
import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/historical_site_rating_view.dart';
import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/itinerary_completion_view.dart';
import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/kids_selection_view.dart';
import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/scenic_viewpoints_rating_view.dart';
import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/shopping_rating_view.dart';
import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/wildlife_rating_view.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class ItineraryCreationPage extends StatefulWidget {
const ItineraryCreationPage({super.key});
@override
State<ItineraryCreationPage> createState() => _ItineraryCreationPageState();
}
class _ItineraryCreationPageState extends State<ItineraryCreationPage> {
final PageController _pageController = PageController();
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFFFFF5F5),
appBar: AppBar(
backgroundColor: Color(0xFFFFF5F5),
centerTitle: true,
leading: GestureDetector(
onTap: () {
context.read<ItineraryStepNavigationBloc>().add(
ItineraryStepNavigationPreviousEvent(),
);
},
child: Icon(Icons.arrow_back),
),
title:
BlocBuilder<
ItineraryStepNavigationBloc,
ItineraryStepNavigationState
>(
builder: (context, state) {
return Text(
"${state.selectedIndex} / 10",
style: TextStyle(color: Color(0xFF4A5565), fontSize: 14.sp),
);
},
),
),
body:
BlocListener<
ItineraryStepNavigationBloc,
ItineraryStepNavigationState
>(
listener: (context, state) {
_pageController.animateToPage(
state.selectedIndex,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
},
child: Column(
children: [
Padding(
padding: EdgeInsets.only(
left: 20.w,
right: 20.w,
bottom: 20.h,
),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child:
BlocBuilder<
ItineraryStepNavigationBloc,
ItineraryStepNavigationState
>(
builder: (context, state) {
return LinearProgressIndicator(
value: state.selectedIndex / 10,
borderRadius: BorderRadius.circular(10),
backgroundColor: Colors.white,
color: const Color(0xFFF95F62),
minHeight: 6.h,
);
},
),
),
),
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w),
child: PageView(
controller: _pageController,
physics: const NeverScrollableScrollPhysics(),
children: [
DateSelectionView(),
CitySelectionView(),
EnergySelectionView(),
KidsSelectionView(),
DietarySelectionView(),
ArtGallerySelectionView(),
ScenicViewpointsRatingView(),
HistoricalSiteRatingView(),
WildlifeRatingView(),
ShoppingRatingView(),
ItineraryCompletionView(),
],
),
),
),
],
),
),
);
}
}

View File

@@ -29,7 +29,7 @@ class MyApp extends StatelessWidget {
builder: (context, child) {
return MaterialApp(
onGenerateRoute: _appRouter.onGenerateRoute,
initialRoute: RouteConstants.home,
initialRoute: RouteConstants.itineraryCreationStart,
debugShowCheckedModeBanner: false,
title: 'City Cards',
theme: ThemeData(