Files
CityCards_Customer_Flutter/lib/home/views/registered_user_home_page.dart

512 lines
20 KiB
Dart

import 'package:cached_network_image/cached_network_image.dart';
import 'package:citycards_customer/home/widgets/e_sim_offer_section.dart';
import 'package:citycards_customer/home/widgets/hotel_offers_section.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:google_fonts/google_fonts.dart';
import '../../common_bloc/bottom_navigation_bloc.dart';
import '../../common_packages/app_bar.dart';
import '../../common_packages/custom_filled_button.dart';
import '../../common_packages/custom_text.dart';
import '../../core/route_constants.dart';
import '../../localPreference/local_preference.dart';
import '../../networkApiServices/api_urls.dart';
import '../../postcard/blocs/myPostCards/my_postcard_bloc.dart';
import '../../postcard/blocs/myPostCards/my_postcard_event.dart';
import '../../profile/bloc/profile/profile_bloc.dart';
import '../../profile/bloc/profile/profile_event.dart';
import '../bloc/registeredHome/home_bloc.dart';
import '../bloc/registeredHome/home_event.dart';
import '../bloc/registeredHome/home_state.dart';
import '../widgets/attractions_list.dart';
import '../widgets/get_your_pass_card.dart';
import '../widgets/gradient_container_bg.dart';
import '../widgets/itineary_animation.dart';
import '../widgets/pass_card_list.dart';
import '../widgets/search_city_bottomsheet.dart';
class RegisteredUserHomePage extends StatefulWidget {
const RegisteredUserHomePage({super.key});
@override
State<RegisteredUserHomePage> createState() => _RegisteredUserHomePageState();
}
class _RegisteredUserHomePageState extends State<RegisteredUserHomePage> {
@override
void initState() {
super.initState();
// _loadMyPostCards();
_checkAndShowCitySelection();
_loadProfileIfLoggedIn();
}
Future<void> _loadProfileIfLoggedIn() async {
final userId = await LocalPreference.getUserId();
if (userId != null && mounted) {
context.read<ProfileBloc>().add(
FetchProfileEvent(userId: userId),
);
}
}
Future<void> _loadMyPostCards() async {
final userId = await LocalPreference.getUserId();
if (userId != null && mounted) {
context.read<MyPostCardBloc>().add(FetchDraftPostCards());
context.read<MyPostCardBloc>().add(RefreshDraftPostCards());
context.read<MyPostCardBloc>().add(RefreshOrderPostCards());
context.read<MyPostCardBloc>().add(FetchOrderPostCards());
}
}
Future<void> _checkAndShowCitySelection() async {
final int cityId = await LocalPreference.getSelectedCityId();
if (cityId == 0) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_showCitySelectionBottomSheet();
});
} else {
if (mounted) {
context.read<HomeBloc>().add(FetchHomeData());
}
}
}
void _showCitySelectionBottomSheet() {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
isDismissible: false,
enableDrag: false,
builder: (_) => const CitySelectionBottomSheet(),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: RefreshIndicator(
color: Color(0xffF95F62),
onRefresh: () async {
await _checkAndShowCitySelection();
},
child: BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) {
if (state is HomeLoading) {
return const Center(child: CircularProgressIndicator(color: Color(0xffF95F62)));
}
if (state is HomeError) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(height: 40.h),
Icon(
Icons.error_outline,
size: 120.sp,
color: Colors.red.withOpacity(0.3),
),
SizedBox(height: 32.h),
CustomText(
text: "Oops! Something went wrong",
size: 18.sp,
weight: FontWeight.w600,
textAlign: TextAlign.center,
),
SizedBox(height: 12.h),
Padding(
padding: EdgeInsets.symmetric(horizontal: 24.w),
child: CustomText(
text: state.message,
size: 14.sp,
color: Color(0xFF656565),
textAlign: TextAlign.center,
),
),
SizedBox(height: 32.h),
CustomFilledButton(
onTap:() {
context.read<HomeBloc>().add(FetchHomeData());
},
label: "Try Again",
),
],
),
);
}
if (state is HomeLoaded) {
final city = state.homeModel.city;
final attractions = state.homeModel.attraction ?? [];
final String? cityIconUrl =
city?.cityIconPath != null && city!.cityIconPath!.isNotEmpty
? "${ApiUrls.baseUrl}${city.cityIconPath}"
: null;
final bannerImageUrl = city?.cityBanners?.isNotEmpty == true
? city!.cityBanners!
.firstWhere(
(banner) =>
banner.isActive == true &&
banner.imageFilePath != null,
orElse: () => city.cityBanners!.first,
)
.imageFilePath
: null;
return SingleChildScrollView(
child: Stack(
children: [
// Background image - use city banner if available
_buildBannerImage(bannerImageUrl),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(10.r),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CommonAppBar(
isWhiteLogo: false,
isProfilePage: false,
showDivider: false,
// imageUrl: cityIconUrl,
isSelectCity: true,
),
SizedBox(height: 130.h),
// City name from API
Text(
city?.cityName ?? "City Name",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w500,
fontSize: 44.sp,
),
),
SizedBox(height: 4.h),
// City description from API
Text(
city?.description ?? "City description",
style: TextStyle(
color: Colors.white.withOpacity(0.9),
fontSize: 12.sp,
fontWeight: FontWeight.w400,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 12.h),
// Category tags
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: () {
final tags = (city?.cityHighlights ?? [])
.where((highlight) => highlight.isActive == true)
.map((highlight) => Padding(
padding: EdgeInsets.only(right: 8.w),
child: _buildTag(highlight.title ?? ""),
))
.toList();
return tags.isEmpty ? [_buildTag("No Highlights Available")] : tags;
}(),
),
),
SizedBox(height: 40.h),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text.rich(
TextSpan(
children: [
TextSpan(
text: "Popular ",
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w500,
color: Color(0xffF95F62),
),
),
TextSpan(
text: "Attractions",
style: TextStyle(
fontSize: 18.sp,
color: Colors.black,
fontWeight: FontWeight.w500,
),
),
],
),
),
InkWell(
onTap: () {
Navigator.of(context).pushNamed(
RouteConstants.attractionsPage,
arguments: "home",
);
},
child: Text(
"View all",
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w500,
color: Color(0xffF95F62),
),
),
),
],
),
SizedBox(height: 12.h),
// Pass attractions from API
AttractionsListView(attractions: attractions),
],
),
),
InwardCurvedContainer(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 40.h),
const ItineraryVideo(),
SizedBox(height: 20.h),
// Button section
Container(
margin: EdgeInsets.symmetric(horizontal: 16.w),
child: SizedBox(
width: 240.w,
child: ElevatedButton(
onPressed: () {
context.read<NavigationBloc>().add(
NavigationTabChanged(1),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xffF95F62),
padding: EdgeInsets.symmetric(
vertical: 14.h,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
30.r,
),
),
),
child: Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Text(
"Create My Magic Itinerary",
style: GoogleFonts.poppins(
fontWeight: FontWeight.w500,
fontSize: 14.sp,
color: Colors.white,
),
),
SizedBox(width: 4.w),
const Icon(
Icons.arrow_forward,
color: Colors.white,
),
],
),
),
),
),
],
),
),
ESimOfferSection(),
HotelOffersSection(),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
children: [
InkWell(
onTap: () {
Navigator.of(context).pushNamed(
RouteConstants.searchOffer,
);
},
child: _buildFeatureCard(
image:
"assets/images/claim_offers_bg.jpg",
title: "Claim offers with your City Cards",
subtitle: "Lorem ipsum dolor sit amet...",
),
),
],
),
SizedBox(height: 24.h),
ChooseYourPassSection(
cards: state.homeModel.city?.cards ?? [],
),
SizedBox(height: 20.h),
GetYourPassCard(),
SizedBox(height: 20.h),
],
),
),
],
),
],
),
);
}// Initial state
return const Center(child: CircularProgressIndicator(color: Color(0xffF95F62),));
},
),
),
),
);
}
Widget _buildTag(String label) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.h),
decoration: BoxDecoration(
color: const Color(0xffFFFFFF).withOpacity(0.29),
borderRadius: BorderRadius.circular(20.r),
),
child: Text(
label,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w500,
fontSize: 12.sp,
),
),
);
}
Widget _buildFeatureCard({
required String image,
required String title,
required String subtitle,
}) {
return Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(16.r),
child: Image.asset(
image,
height: 220.h,
width: double.infinity,
fit: BoxFit.cover,
),
),
Positioned(
left: 16.w,
right: 16.w,
bottom: 16.h,
child: Container(
padding: EdgeInsets.all(12.r),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.9),
borderRadius: BorderRadius.circular(16.r),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 18.sp,
),
),
Text(
subtitle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: GoogleFonts.poppins(
fontSize: 14.sp,
color: Colors.black.withOpacity(0.6),
),
),
],
),
),
SizedBox(width: 8.w),
Container(
decoration: const BoxDecoration(
color: Color(0xffFDCDCE),
shape: BoxShape.circle,
),
padding: EdgeInsets.all(12.r),
child: Image.asset(
"assets/icons/arrow_angle_up.png",
scale: 4,
),
),
],
),
),
),
],
);
}
Widget _buildBannerImage(String? imageUrl) {
return SizedBox(
height: 350.h,
width: double.infinity,
child: (imageUrl == null || imageUrl.isEmpty)
? Image.asset(
"assets/images/chicago.png",
fit: BoxFit.cover,
)
: CachedNetworkImage(
imageUrl: imageUrl,
fit: BoxFit.cover,
// 🔄 Loader (same as your loadingBuilder)
placeholder: (context, url) => Container(
color: Colors.grey[300],
child: const Center(
child: CircularProgressIndicator(
color: Color(0xffF95F62),
),
),
),
// ❌ Error fallback (same as errorBuilder)
errorWidget: (context, url, error) => Image.asset(
"assets/images/chicago.png",
fit: BoxFit.cover,
),
),
);
}
}