ssss
unknown
plain_text
a month ago
40 kB
5
Indexable
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:johntour_user/common/common.dart';
import 'package:johntour_user/common/pickup_icon.dart';
import 'package:johntour_user/core/utils/format_utils.dart';
import 'package:johntour_user/features/home/domain/models/trip_look_up_model.dart';
import 'package:johntour_user/features/home/presentation/pages/trip_look_up_detail_page.dart';
import 'package:johntour_user/features/tour/presentation/pages/tour_booking_list_page.dart';
import 'package:johntour_user/core/utils/custom_loader.dart';
import 'package:johntour_user/core/utils/custom_text.dart';
import 'package:johntour_user/core/utils/custom_textfield.dart';
import 'package:johntour_user/core/utils/custom_divider.dart';
import 'package:johntour_user/core/utils/debouncer.dart';
import 'package:johntour_user/features/home/application/home_bloc.dart';
import 'package:johntour_user/l10n/app_localizations.dart';
import 'package:shared_preferences/shared_preferences.dart';
class TripLookUpPage extends StatefulWidget {
static const String routeName = '/tripLookUpPage';
const TripLookUpPage({super.key});
@override
State<TripLookUpPage> createState() => _TripLookUpPageState();
}
class _TripLookUpPageState extends State<TripLookUpPage> {
late FocusNode _focusNode;
late TextEditingController _searchController;
late Future<List<String>> _bookCodesFuture;
late Future<bool> _darkModeFuture;
final Debouncer _debouncer = Debouncer(milliseconds: 1000);
bool _isFocused = false;
bool _showAll = false;
final int _lassItemBookCodes = 5;
String? errorMessage = "";
bool _isLoaderShowing = false;
bool _isProcessing = false;
LatLng? currentUserLocation;
@override
void initState() {
super.initState();
_focusNode = FocusNode();
_searchController = TextEditingController();
_bookCodesFuture = AppSharedPreference.getBookCodes();
_darkModeFuture = getDarkModeStatus();
_getCurrentLocation();
context.read<HomeBloc>().polylines.clear();
context.read<HomeBloc>().markerList.clear();
}
@override
void dispose() {
_focusNode.dispose();
_searchController.dispose();
_debouncer.cancel();
super.dispose();
}
Future<bool> getDarkModeStatus() async {
return await AppSharedPreference.getDarkThemeStatus();
}
Future<void> removeBookCode(String bookCode) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
List<String> bookCodes = prefs.getStringList('bookingCodes') ?? [];
bookCodes.remove(bookCode);
await prefs.setStringList('bookingCodes', bookCodes);
setState(() {
_bookCodesFuture = AppSharedPreference.getBookCodes();
});
}
Future<void> _getCurrentLocation() async {
try {
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) return;
LocationPermission permission = await Geolocator.checkPermission();
if (permission != LocationPermission.whileInUse &&
permission != LocationPermission.always) {
return;
}
Position position = await Geolocator.getCurrentPosition();
setState(() {
currentUserLocation = LatLng(position.latitude, position.longitude);
});
} catch (e) {
printWrapped('Error getting location: $e');
}
}
void _reloadPage() {
setState(() {
_bookCodesFuture = AppSharedPreference.getBookCodes();
errorMessage = "";
});
_getCurrentLocation();
_searchController.clear();
}
Widget _buildTitle(ThemeData theme) {
return MyText(
text: AppLocalizations.of(context)!.tripLookup,
textStyle: theme.textTheme.headlineLarge!.copyWith(
fontSize: 22,
fontWeight: FontWeight.bold,
),
);
}
@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
final theme = Theme.of(context);
return FutureBuilder<bool>(
future: _darkModeFuture,
builder: (context, snapshot) {
bool isDarkMode = snapshot.data ?? false;
final homeBloc = context.read<HomeBloc>();
return Scaffold(
appBar: AppBar(
backgroundColor: theme.scaffoldBackgroundColor,
elevation: 0,
title: _buildTitle(theme),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
decoration: BoxDecoration(
color: isDarkMode == true
? AppColors.inputColorDark
: AppColors.inputColor,
borderRadius: BorderRadius.circular(8),
),
child: BlocListener<HomeBloc, HomeState>(
listener: (context, state) async {
if (state is HomeLoadingStartState) {
if (!_isProcessing) return;
_isLoaderShowing = true;
CustomLoader.loader(context);
setState(() {
_isFocused = false;
});
FocusScope.of(context).requestFocus(FocusNode());
} else if (state is GetRidesRequestsState) {
if (!_isProcessing) return;
_isProcessing = false;
if (_isLoaderShowing) {
CustomLoader.dismiss(context);
_isLoaderShowing = false;
}
if (homeBloc.ridesRequests.isEmpty) {
setState(() {
errorMessage = "Trip details not available";
});
return;
}
final item = homeBloc.ridesRequests.first;
RequestBookings? requestBookings;
if (item.requestBookings != null &&
item.requestBookings!.isNotEmpty) {
String bookCode = _searchController.text;
requestBookings =
item.requestBookings!.firstWhere(
(booking) => booking.bookingCode == bookCode,
orElse: () => item.requestBookings!.first,
);
}
Navigator.pushNamed(
// ignore: use_build_context_synchronously
context,
TripLookUpDetailPage.routeName,
arguments: TripLookUpDetailPageArguments(
rideRequest: item,
requestBookings: requestBookings,
),
).then((result) {
if (result == true) {
_reloadPage();
}
});
} else if (state is ShowTripDateSelectionState) {
if (!_isProcessing) return;
_isProcessing = false;
if (_isLoaderShowing) {
CustomLoader.dismiss(context);
_isLoaderShowing = false;
}
_showTripDateSelectionDialog(context, state);
} else if (state is HomeLoadingStopState) {
if (_isLoaderShowing) {
CustomLoader.dismiss(context);
_isLoaderShowing = false;
}
} else if (state is VehicleTypesErrorState) {
if (!_isProcessing) return;
_isProcessing = false;
if (_isLoaderShowing) {
CustomLoader.dismiss(context);
_isLoaderShowing = false;
}
setState(() {
errorMessage = state.message;
});
}
},
child: CustomTextField(
controller: _searchController,
focusNode: _focusNode,
onChange: (value) {
if (errorMessage != "") {
setState(() {
errorMessage = "";
});
}
_debouncer.run(() {
if (value.isNotEmpty) {
_isProcessing = true;
context.read<HomeBloc>().add(
GetRidesRequestsEvent(bookCode: value),
);
}
});
},
hintText:
'${AppLocalizations.of(context)!.enterBookingCodeToSearch} ',
hintTextStyle: GoogleFonts.poppins(
color: Theme.of(context)
.primaryColorDark
.withOpacity(0.5),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: isDarkMode == true
? AppColors.inputColorDark
: AppColors.inputColor,
width: 1),
borderRadius: BorderRadius.circular(10),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: isDarkMode == true
? AppColors.inputColorDark
: AppColors.inputColor,
width: 0.8),
borderRadius: BorderRadius.circular(10),
),
prefixIcon: Icon(
Icons.search,
color: Theme.of(context).primaryColorDark,
),
borderRadius: 10,
filled: true,
fillColor: isDarkMode == true
? AppColors.inputColorDark
: AppColors.inputColor,
contentPadding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 7),
suffixIcon: (_isFocused)
? IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
_searchController.clear();
setState(() {});
},
)
: null,
)),
),
SizedBox(height: size.height * 0.01),
Column(
children: [
FutureBuilder<List<String>>(
future: _bookCodesFuture,
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(
child:
MyText(text: 'Error: ${snapshot.error}'));
} else if (!snapshot.hasData ||
snapshot.data!.isEmpty) {
return const SizedBox.shrink();
} else {
final bookCodes =
snapshot.data!.reversed.toList();
final visibleItems = _showAll
? bookCodes
: bookCodes.take(_lassItemBookCodes).toList();
return Column(
children: [
...visibleItems.map((bookCode) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 8),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: () {
FocusScope.of(context)
.requestFocus(FocusNode());
_isProcessing = true;
context.read<HomeBloc>().add(
GetRidesRequestsEvent(
bookCode: bookCode));
_searchController.text = bookCode;
},
child: Row(
children: [
Icon(
Icons
.access_time_filled_rounded,
color: Theme.of(context)
.disabledColor,
),
SizedBox(
width: size.width * 0.02),
MyText(
text: bookCode,
textStyle: TextStyle(
color: Theme.of(context)
.primaryColorDark,
fontWeight: FontWeight.w500,
),
),
],
),
),
GestureDetector(
onTap: () {
removeBookCode(bookCode);
},
child: Container(
margin: const EdgeInsets.only(
left: 8.0),
child: Icon(
Icons.close,
color: Theme.of(context)
.disabledColor,
size: 18,
),
),
),
],
),
);
}),
if (bookCodes.length > _lassItemBookCodes)
GestureDetector(
onTap: () {
setState(() {
_showAll = !_showAll;
});
},
child: Padding(
padding:
const EdgeInsets.only(top: 8.0),
child: GestureDetector(
onTap: () {
setState(() {
_showAll = !_showAll;
});
},
child: Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
MyText(
text: _showAll
? AppLocalizations.of(
context)!
.seeLess
: AppLocalizations.of(
context)!
.seeMore,
textStyle: TextStyle(
color: Theme.of(context)
.primaryColor,
fontWeight: FontWeight.bold,
),
),
Icon(
_showAll
? Icons
.keyboard_arrow_up_rounded
: Icons
.keyboard_arrow_down_rounded,
color: Theme.of(context)
.primaryColor,
size: 20,
),
],
),
),
),
),
],
);
}
},
),
],
),
if (_searchController.text.isEmpty) ...[
Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
children: [
Image.asset(
AppImages.howToSearch,
height: size.height * 0.2,
width: size.width * 0.45,
fit: BoxFit.cover,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.help_outline,
color: theme.primaryColor,
size: 20,
),
const SizedBox(width: 4),
MyText(
text: AppLocalizations.of(context)!
.howToGetBookingCode,
textStyle:
theme.textTheme.titleMedium!.copyWith(
fontWeight: FontWeight.bold,
color: theme.primaryColor,
),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding:
const EdgeInsets.only(left: 12.0),
child: MyText(
text:
"• ${AppLocalizations.of(context)!.exampleBookingCodes}",
textStyle: theme.textTheme.titleSmall!
.copyWith(fontSize: 14),
),
),
],
),
],
),
],
),
),
SizedBox(height: size.height * 0.01),
Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
children: [
Icon(
Icons.info_outline,
color: theme.primaryColor,
size: 20,
),
const SizedBox(width: 4),
MyText(
maxLines: 2,
text: AppLocalizations.of(context)!.userGuide,
textStyle:
theme.textTheme.titleMedium!.copyWith(
fontWeight: FontWeight.bold,
color: theme.primaryColor,
),
),
],
),
const SizedBox(height: 4),
Padding(
padding: const EdgeInsets.only(left: 12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MyText(
text:
"1. ${AppLocalizations.of(context)!.enterBookingCode}",
textStyle: theme.textTheme.titleSmall!
.copyWith(fontSize: 14),
),
const SizedBox(height: 4),
MyText(
text:
"2. ${AppLocalizations.of(context)!.viewTripInfo}",
textStyle: theme.textTheme.titleSmall!
.copyWith(fontSize: 14),
),
const SizedBox(height: 4),
MyText(
text:
"3. ${AppLocalizations.of(context)!.trackDriverRealtime}",
textStyle: theme.textTheme.titleSmall!
.copyWith(fontSize: 14),
),
],
),
),
const SizedBox(height: 16),
InkWell(
onTap: () {
Navigator.pushNamed(
context, TourBookingListPage.routeName);
},
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: theme.primaryColor,
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.tour_outlined,
color: Colors.white),
const SizedBox(width: 8),
MyText(
text: AppLocalizations.of(context)!
.bookedToursTitle,
textStyle:
theme.textTheme.titleMedium!.copyWith(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
],
),
),
],
if (errorMessage != "" && _searchController.text.isNotEmpty)
Center(
child: Column(
children: [
Image.asset(
AppImages.notFoundSearch,
height: size.height * 0.17,
width: size.width * 0.45,
fit: BoxFit.cover,
),
SizedBox(height: size.height * 0.01),
MyText(
text: errorMessage,
textAlign: TextAlign.center,
textStyle: Theme.of(context)
.textTheme
.bodyLarge!
.copyWith(
color: Theme.of(context).primaryColorDark,
fontSize: 16),
),
MyText(
text:
AppLocalizations.of(context)!.bookingNotFound,
textAlign: TextAlign.center,
textStyle: Theme.of(context)
.textTheme
.bodyLarge!
.copyWith(
color: Theme.of(context).primaryColorDark,
fontSize: 16),
),
MyText(
maxLines: 2,
text: AppLocalizations.of(context)!
.checkBookingCode,
textAlign: TextAlign.center,
textStyle: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
color: Theme.of(context).primaryColorDark,
fontSize: 14),
),
],
),
)
],
),
),
),
);
});
}
void _showTripDateSelectionDialog(
BuildContext context, ShowTripDateSelectionState state) {
final size = MediaQuery.sizeOf(context);
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext dialogContext) {
return AlertDialog(
actionsPadding: const EdgeInsets.all(8),
insetPadding: const EdgeInsets.all(8),
contentPadding: const EdgeInsets.all(0),
title: Column(
children: [
Icon(
Icons.event,
size: 40,
color: Theme.of(context).primaryColorDark,
),
MyText(
textAlign: TextAlign.center,
text: 'Chọn ngày chuyến đi',
textStyle: Theme.of(context).textTheme.headlineSmall!.copyWith(
color: Theme.of(context).primaryColorDark,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
MyText(
textAlign: TextAlign.center,
text: 'Vui lòng chọn ngày mà bạn muốn xem',
textStyle: Theme.of(context).textTheme.bodySmall!.copyWith(
color:
Theme.of(context).primaryColorDark.withOpacity(0.7),
),
),
SizedBox(height: size.height * 0.02),
],
),
content: SizedBox(
width: double.maxFinite,
child: ListView.builder(
shrinkWrap: true,
itemCount: state.ridesRequestList.length * 2 - 1,
itemBuilder: (context, index) {
if (index.isOdd) {
return const Column(
children: [
CustomDivider(
height: 0.1,
width: double.infinity,
),
],
);
}
final itemIndex = index ~/ 2;
final rideRequest = state.ridesRequestList[itemIndex];
return ListTile(
leading: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color:
Theme.of(context).primaryColorDark.withOpacity(0.05),
borderRadius: BorderRadius.circular(20),
),
child: Icon(
Icons.calendar_today,
size: 20,
color: Theme.of(context).primaryColorDark,
),
),
onTap: () {
Navigator.of(dialogContext).pop();
_isProcessing = true;
context.read<HomeBloc>().add(
SelectTripDateEvent(
selectedRideRequest: rideRequest,
bookCode: _searchController.text,
),
);
},
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
MyText(
text: formatDateTime(context,
rideRequest.requestBookings![0].ngayDi.toString(),
type: 'date'),
textStyle:
Theme.of(context).textTheme.bodyLarge!.copyWith(
color: Theme.of(context).primaryColorDark,
fontWeight: FontWeight.w500,
),
),
Icon(
Icons.arrow_forward_ios,
size: 12,
color: Theme.of(context).primaryColorDark,
),
],
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
spacing: 2,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const DropIcon(
size: 12,
),
Expanded(
child: MyText(
text: rideRequest.pickAddress,
textStyle: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
color: Theme.of(context)
.primaryColorDark
.withOpacity(0.7),
),
overflow: TextOverflow.ellipsis,
),
)
],
),
SizedBox(height: size.height * 0.005),
if (rideRequest.dropAddress != null &&
rideRequest.dropAddress!.isNotEmpty)
Row(
mainAxisAlignment: MainAxisAlignment.start,
spacing: 2,
children: [
const PickupIcon(
size: 10,
sizeIcon: 8,
),
Expanded(
child: MyText(
text: rideRequest.dropAddress,
textStyle: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
color: Theme.of(context)
.primaryColorDark
.withOpacity(0.7),
),
overflow: TextOverflow.ellipsis,
),
)
],
),
],
),
);
},
),
),
actions: [
Padding(
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 8),
child: SizedBox(
width: double.infinity,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(
color:
Theme.of(context).primaryColorDark.withOpacity(0.2),
width: 1,
),
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
Navigator.of(dialogContext).pop();
},
borderRadius: BorderRadius.circular(12),
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 16,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
MyText(
text: 'Hủy',
textStyle: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: Theme.of(context)
.primaryColorDark
.withOpacity(0.8),
fontWeight: FontWeight.w600,
),
),
const SizedBox(width: 8),
Icon(
Icons.close,
size: 18,
color: Theme.of(context)
.primaryColorDark
.withOpacity(0.7),
),
],
),
),
),
),
),
),
),
],
);
},
);
}
}
Editor is loading...
Leave a Comment