Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 840a81f09e | |||
|
|
a2bad0f139 | ||
|
|
22f2de1bbe | ||
|
|
b77bcea769 | ||
| ada6040514 | |||
| 3c05534262 | |||
| 3caa52d9f8 |
@@ -1,42 +1,33 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
|
||||
<application
|
||||
android:label="CityCard Customer"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/launcher_icon"
|
||||
android:label="CityCard Customer">
|
||||
android:icon="@mipmap/launcher_icon">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:exported="true"
|
||||
android:hardwareAccelerated="true"
|
||||
android:launchMode="singleTop"
|
||||
android:taskAffinity=""
|
||||
android:theme="@style/NormalTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme" />
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
<data android:scheme="mailto" />
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- Don't delete the meta-data below.
|
||||
@@ -52,8 +43,8 @@
|
||||
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.PROCESS_TEXT" />
|
||||
<data android:mimeType="text/plain" />
|
||||
<action android:name="android.intent.action.PROCESS_TEXT"/>
|
||||
<data android:mimeType="text/plain"/>
|
||||
</intent>
|
||||
</queries>
|
||||
</manifest>
|
||||
|
||||
File diff suppressed because one or more lines are too long
1
assets/intro/splash.json
Normal file
1
assets/intro/splash.json
Normal file
File diff suppressed because one or more lines are too long
3
devtools_options.yaml
Normal file
3
devtools_options.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
description: This file stores settings for Dart & Flutter DevTools.
|
||||
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
|
||||
extensions:
|
||||
@@ -1,7 +1,12 @@
|
||||
PODS:
|
||||
- Flutter (1.0.0)
|
||||
- flutter_angle (0.3.8):
|
||||
- Flutter
|
||||
- FlutterAngle (~> 0.0.8)
|
||||
- FlutterMacOS
|
||||
- flutter_native_splash (2.4.3):
|
||||
- Flutter
|
||||
- FlutterAngle (0.0.8)
|
||||
- geolocator_apple (1.2.0):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
@@ -21,12 +26,10 @@ PODS:
|
||||
- path_provider_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- share_plus (0.0.1):
|
||||
- Flutter
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- url_launcher_ios (0.0.1):
|
||||
- three_js_sensors (0.1.2):
|
||||
- Flutter
|
||||
- video_player_avfoundation (0.0.1):
|
||||
- Flutter
|
||||
@@ -34,25 +37,28 @@ PODS:
|
||||
|
||||
DEPENDENCIES:
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_angle (from `.symlinks/plugins/flutter_angle/darwin`)
|
||||
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
||||
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/darwin`)
|
||||
- google_maps_flutter_ios (from `.symlinks/plugins/google_maps_flutter_ios/ios`)
|
||||
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
- three_js_sensors (from `.symlinks/plugins/three_js_sensors/ios`)
|
||||
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- FlutterAngle
|
||||
- Google-Maps-iOS-Utils
|
||||
- GoogleMaps
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
flutter_angle:
|
||||
:path: ".symlinks/plugins/flutter_angle/darwin"
|
||||
flutter_native_splash:
|
||||
:path: ".symlinks/plugins/flutter_native_splash/ios"
|
||||
geolocator_apple:
|
||||
@@ -65,18 +71,18 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||
path_provider_foundation:
|
||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||
share_plus:
|
||||
:path: ".symlinks/plugins/share_plus/ios"
|
||||
shared_preferences_foundation:
|
||||
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||
url_launcher_ios:
|
||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||
three_js_sensors:
|
||||
:path: ".symlinks/plugins/three_js_sensors/ios"
|
||||
video_player_avfoundation:
|
||||
:path: ".symlinks/plugins/video_player_avfoundation/darwin"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||
flutter_angle: 7b1a2b3e733221bf2e0325e42fc3edf95b5d44c4
|
||||
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
|
||||
FlutterAngle: c810891af800750361b1d0e7cc944f2338d5ae18
|
||||
geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e
|
||||
Google-Maps-iOS-Utils: 0a484b05ed21d88c9f9ebbacb007956edd508a96
|
||||
google_maps_flutter_ios: 0291eb2aa252298a769b04d075e4a9d747ff7264
|
||||
@@ -84,9 +90,8 @@ SPEC CHECKSUMS:
|
||||
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
|
||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
||||
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
|
||||
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
||||
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
|
||||
three_js_sensors: f516b092803411e05b1e3dc7625efa36acd8f455
|
||||
video_player_avfoundation: dd410b52df6d2466a42d28550e33e4146928280a
|
||||
|
||||
PODFILE CHECKSUM: 1857a7cdb7dfafe45f2b0e9a9af44644190f7506
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<string>3</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
|
||||
484
lib/attraction_details/attraction_details_view.dart
Normal file
484
lib/attraction_details/attraction_details_view.dart
Normal file
@@ -0,0 +1,484 @@
|
||||
import 'package:citycards_customer/attraction_details/share_bottomsheet.dart';
|
||||
import 'package:citycards_customer/common_packages/app_bar.dart';
|
||||
import 'package:citycards_customer/common_packages/custom_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
||||
import '../core/route_constants.dart';
|
||||
|
||||
class AttractionDetailsView extends StatelessWidget {
|
||||
const AttractionDetailsView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
||||
Stack(
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/images/koh_rong_samloem_banner.png',
|
||||
height: 377.h,
|
||||
width: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: SafeArea(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 10.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
||||
CommonAppBar(isWhiteLogo: true, isProfilePage: false, showDivider: true,),
|
||||
|
||||
SizedBox(height: 10.h),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Navigator.pop(context),
|
||||
child: Icon(
|
||||
Icons.arrow_back,
|
||||
size: 24.sp,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Text(
|
||||
"Koh Rong Samloem",
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Positioned(
|
||||
bottom: 31.h,
|
||||
left: 12.w,
|
||||
child: Text(
|
||||
"Koh Rong\nSamloem",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 44.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
height: 1.2,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Positioned(
|
||||
bottom: 31.h,
|
||||
right: 17.w,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (context) => const ShareBottomSheet(),
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
height: 36.h,
|
||||
width: 36.w,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20.r),
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.share_sharp,
|
||||
color: Colors.black,
|
||||
size: 18.sp,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// About Section
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 30.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"About",
|
||||
style: TextStyle(
|
||||
fontSize: 18.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12.32.h),
|
||||
Text(
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Convallis condimentum morbi non egestas enim amet sagittis. Proin sed aliquet rhoncus ut pellentesque ullamcorper sit eget ac.Sit nisi, cras amet varius eget egestas pellentesque. Cursus gravida euismod non...",
|
||||
style: TextStyle(
|
||||
color: Color(0xFF262626),
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 14.sp,
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 41.h),
|
||||
// Booking Section
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.w),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"How to make a booking?",
|
||||
style: TextStyle(
|
||||
fontSize: 18.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 12.w,
|
||||
vertical: 12.h,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
border: Border.all(color: Color(0xFFF95F62)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.call,
|
||||
color: Color(0xFFF95F62),
|
||||
size: 32.w,
|
||||
),
|
||||
SizedBox(width: 16.w),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CustomText(
|
||||
text: "Contact Number",
|
||||
color: Colors.black.withOpacity(.6),
|
||||
size: 12.sp,
|
||||
weight: FontWeight.w500,
|
||||
),
|
||||
SizedBox(height: 6.h),
|
||||
CustomText(
|
||||
text: "+1012 3456 789",
|
||||
color: Colors.black,
|
||||
size: 14.sp,
|
||||
weight: FontWeight.w600,
|
||||
),
|
||||
|
||||
SizedBox(height: 6.h),
|
||||
CustomText(
|
||||
text: "Tap to call",
|
||||
color: Colors.black.withOpacity(.4),
|
||||
size: 12.sp,
|
||||
weight: FontWeight.w400,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 12.w,
|
||||
vertical: 12.h,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
border: Border.all(color: Color(0xFFF95F62)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.email_sharp,
|
||||
color: Color(0xFFF95F62),
|
||||
size: 32.w,
|
||||
),
|
||||
SizedBox(width: 16.w),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CustomText(
|
||||
text: "Email",
|
||||
color: Colors.black.withOpacity(.6),
|
||||
size: 12.sp,
|
||||
weight: FontWeight.w500,
|
||||
),
|
||||
SizedBox(height: 6.h),
|
||||
CustomText(
|
||||
text: "CityCards24@gmail.com",
|
||||
color: Colors.black,
|
||||
size: 14.sp,
|
||||
weight: FontWeight.w600,
|
||||
),
|
||||
|
||||
SizedBox(height: 6.h),
|
||||
CustomText(
|
||||
text: "Tap to email",
|
||||
color: Colors.black.withOpacity(.4),
|
||||
size: 12.sp,
|
||||
weight: FontWeight.w400,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
|
||||
InkWell(
|
||||
onTap: (){
|
||||
Navigator.of(context).pushNamed(RouteConstants.makeBooking);
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 24.w,
|
||||
vertical: 18.h,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFF95F62),
|
||||
borderRadius: BorderRadius.circular(10.r),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CustomText(
|
||||
text: "Via CityCards",
|
||||
size: 16.sp,
|
||||
weight: FontWeight.w500,
|
||||
color: Colors.white,
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
CustomText(
|
||||
text: "Create a booking via app",
|
||||
size: 11.sp,
|
||||
weight: FontWeight.w400,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Icon(
|
||||
Icons.arrow_forward_ios_outlined,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 30.h),
|
||||
|
||||
Divider(color: Colors.black.withOpacity(0.2)),
|
||||
SizedBox(height: 30.h),
|
||||
Text(
|
||||
"What is included",
|
||||
style: TextStyle(
|
||||
fontSize: 24.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 4.h),
|
||||
|
||||
Wrap(
|
||||
runSpacing: 16.h,
|
||||
spacing: 16.w,
|
||||
children: [
|
||||
includedBox(
|
||||
"assets/icons/bus.png",
|
||||
"Bus",
|
||||
"Transportation",
|
||||
),
|
||||
includedBox(
|
||||
"assets/icons/clock.png",
|
||||
"2 day 1 night",
|
||||
"Duration",
|
||||
),
|
||||
includedBox(
|
||||
"assets/icons/bx_qr.png",
|
||||
"TAC200812695",
|
||||
"Product code",
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 30.h),
|
||||
|
||||
Divider(color: Colors.black.withOpacity(0.2)),
|
||||
SizedBox(height: 30.h),
|
||||
Text(
|
||||
"Exact Location",
|
||||
style: TextStyle(
|
||||
fontSize: 18.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
|
||||
CustomText(
|
||||
text: "View the location on map",
|
||||
size: 12.sp,
|
||||
color: Colors.black.withOpacity(.6),
|
||||
),
|
||||
SizedBox(height: 17.h),
|
||||
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(13.54.r),
|
||||
child: Image.asset(
|
||||
height: 178.7.h,
|
||||
width: double.infinity,
|
||||
"assets/images/attra_detail_map.png",
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 17.h),
|
||||
|
||||
CustomText(
|
||||
text:
|
||||
"Angkor Mails Hotel \nNR6, Krong Siem Reap Cambodia",
|
||||
size: 12.sp,
|
||||
color: Colors.black.withOpacity(0.6),
|
||||
),
|
||||
|
||||
SizedBox(height: 30.h),
|
||||
|
||||
Divider(color: Colors.black.withOpacity(0.2)),
|
||||
SizedBox(height: 30.h),
|
||||
Text(
|
||||
"People frequently ask",
|
||||
style: TextStyle(
|
||||
fontSize: 18.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 15.h),
|
||||
|
||||
faqBox(
|
||||
"About this place",
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. A id diam nisl, non justo, in odio...",
|
||||
),
|
||||
|
||||
SizedBox(height: 15.h),
|
||||
|
||||
faqBox(
|
||||
"Term and condition",
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. A id diam nisl, non justo, in odio...",
|
||||
),
|
||||
SizedBox(height: 15.h),
|
||||
|
||||
faqBox(
|
||||
"Cancellation Policy",
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. A id diam nisl, non justo, in odio...",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 24.h),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget includedBox(String icon, String title, String disc) {
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 10.h),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFFFF5F5),
|
||||
borderRadius: BorderRadius.circular(10.r),
|
||||
border: Border.all(color: Color(0xFFFDCDCE)),
|
||||
),
|
||||
child: IntrinsicWidth(
|
||||
child: Row(
|
||||
children: [
|
||||
Image.asset(icon, scale: 4),
|
||||
SizedBox(width: 16.w),
|
||||
Column(
|
||||
children: [
|
||||
CustomText(
|
||||
text: title,
|
||||
size: 16.sp,
|
||||
weight: FontWeight.w500,
|
||||
color: Color(0xFF212121),
|
||||
),
|
||||
SizedBox(height: 4.h),
|
||||
CustomText(
|
||||
text: disc,
|
||||
size: 11.sp,
|
||||
weight: FontWeight.w400,
|
||||
color: Color(0xFF666666),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget faqBox(String title, String desc) {
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 12.h),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFFFF5F5),
|
||||
border: Border.all(color: Color(0xFFFDCDCE)),
|
||||
borderRadius: BorderRadius.circular(10.r),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
CustomText(
|
||||
text: title,
|
||||
size: 16.sp,
|
||||
weight: FontWeight.w500,
|
||||
color: Color(0xFF212121),
|
||||
),
|
||||
SizedBox(width: 20.w),
|
||||
Icon(Icons.arrow_forward_ios_outlined, size: 18.sp),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 9.h),
|
||||
CustomText(text: desc, size: 11.sp, color: Color(0xFF7D7D7D)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import 'package:citycards_customer/attraction_details/bloc/attraction_details_event.dart';
|
||||
import 'package:citycards_customer/attraction_details/bloc/attraction_details_state.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class AttractionDetailsBloc
|
||||
extends Bloc<AttractionDetailsEvent, AttractionDetailsState> {
|
||||
AttractionDetailsBloc() : super(AttractionDetailsState()) {
|
||||
on<SetFlowFromPass>(_setFlowFromPass);
|
||||
}
|
||||
|
||||
void _setFlowFromPass(SetFlowFromPass event, Emitter<AttractionDetailsState> emit) {
|
||||
print("Setting the flow from pass ${event.fromByPass}");
|
||||
emit(state.copyWith(fromByPass: event.fromByPass));
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class AttractionDetailsEvent {
|
||||
}
|
||||
|
||||
class SetFlowFromPass extends AttractionDetailsEvent{
|
||||
final bool fromByPass;
|
||||
SetFlowFromPass(this.fromByPass);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class AttractionDetailsState extends Equatable {
|
||||
bool fromByPass;
|
||||
|
||||
AttractionDetailsState({this.fromByPass = false});
|
||||
|
||||
AttractionDetailsState copyWith({required bool fromByPass}) {
|
||||
return AttractionDetailsState(fromByPass: fromByPass ?? this.fromByPass);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [fromByPass];
|
||||
}
|
||||
@@ -1,555 +0,0 @@
|
||||
import 'package:citycards_customer/attraction_details/bloc/attraction_details_bloc.dart';
|
||||
import 'package:citycards_customer/attraction_details/bloc/attraction_details_state.dart';
|
||||
import 'package:citycards_customer/attraction_details/view_model/attraction_details_view_model.dart';
|
||||
import 'package:citycards_customer/attraction_details/widgets/share_bottomsheet.dart';
|
||||
import 'package:citycards_customer/common_packages/app_bar.dart';
|
||||
import 'package:citycards_customer/common_packages/custom_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import '../../core/route_constants.dart';
|
||||
|
||||
class AttractionDetailsView extends StatelessWidget {
|
||||
AttractionDetailsView({super.key});
|
||||
|
||||
final AttractionDetailsViewModel viewModel = AttractionDetailsViewModel();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Stack(
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/images/koh_rong_samloem_banner.png',
|
||||
height: 377.h,
|
||||
width: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: SafeArea(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 20.w,
|
||||
vertical: 10.h,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CommonAppBar(
|
||||
isWhiteLogo: true,
|
||||
isProfilePage: false,
|
||||
showDivider: true,
|
||||
),
|
||||
|
||||
SizedBox(height: 10.h),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Navigator.pop(context),
|
||||
child: Icon(
|
||||
Icons.arrow_back,
|
||||
size: 24.sp,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Text(
|
||||
"Koh Rong Samloem",
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Positioned(
|
||||
bottom: 31.h,
|
||||
left: 12.w,
|
||||
child: Text(
|
||||
"Koh Rong\nSamloem",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 44.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
height: 1.2,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Positioned(
|
||||
bottom: 31.h,
|
||||
right: 17.w,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Share.share(
|
||||
'Check out my City Card app!',
|
||||
subject: 'City Card App',
|
||||
);
|
||||
// showModalBottomSheet(
|
||||
// context: context,
|
||||
// isScrollControlled: true,
|
||||
// backgroundColor: Colors.transparent,
|
||||
// builder: (context) => const ShareBottomSheet(),
|
||||
// );
|
||||
},
|
||||
child: Container(
|
||||
height: 36.h,
|
||||
width: 36.w,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20.r),
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.share_sharp,
|
||||
color: Colors.black,
|
||||
size: 18.sp,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// About Section
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 30.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"About",
|
||||
style: TextStyle(
|
||||
fontSize: 18.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12.32.h),
|
||||
Text(
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Convallis condimentum morbi non egestas enim amet sagittis. Proin sed aliquet rhoncus ut pellentesque ullamcorper sit eget ac.Sit nisi, cras amet varius eget egestas pellentesque. Cursus gravida euismod non...",
|
||||
style: TextStyle(
|
||||
color: Color(0xFF262626),
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 14.sp,
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 41.h),
|
||||
// Booking Section
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.w),
|
||||
child: BlocBuilder<AttractionDetailsBloc, AttractionDetailsState>(
|
||||
builder: (context, state) {
|
||||
print("fajfasfasjfjas======= ${state.fromByPass}");
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (state.fromByPass)
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
"How to make a booking?",
|
||||
style: TextStyle(
|
||||
fontSize: 18.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 16.h),
|
||||
InkWell(
|
||||
onTap: () => viewModel.makePhoneCall(
|
||||
'+10123456789',
|
||||
context,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 12.w,
|
||||
vertical: 12.h,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
border: Border.all(
|
||||
color: Color(0xFFF95F62),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.call,
|
||||
color: Color(0xFFF95F62),
|
||||
size: 32.w,
|
||||
),
|
||||
SizedBox(width: 16.w),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
CustomText(
|
||||
text: "Contact Number",
|
||||
color: Colors.black.withOpacity(
|
||||
.6,
|
||||
),
|
||||
size: 12.sp,
|
||||
weight: FontWeight.w500,
|
||||
),
|
||||
SizedBox(height: 6.h),
|
||||
CustomText(
|
||||
text: "+1012 3456 789",
|
||||
color: Colors.black,
|
||||
size: 14.sp,
|
||||
weight: FontWeight.w600,
|
||||
),
|
||||
|
||||
SizedBox(height: 6.h),
|
||||
CustomText(
|
||||
text: "Tap to call",
|
||||
color: Colors.black.withOpacity(
|
||||
.4,
|
||||
),
|
||||
size: 12.sp,
|
||||
weight: FontWeight.w400,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
InkWell(
|
||||
onTap: () => viewModel.sendEmail(
|
||||
'CityCards24@gmail.com',
|
||||
context,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 12.w,
|
||||
vertical: 12.h,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
border: Border.all(
|
||||
color: Color(0xFFF95F62),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.email_sharp,
|
||||
color: Color(0xFFF95F62),
|
||||
size: 32.w,
|
||||
),
|
||||
SizedBox(width: 16.w),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
CustomText(
|
||||
text: "Email",
|
||||
color: Colors.black.withOpacity(
|
||||
.6,
|
||||
),
|
||||
size: 12.sp,
|
||||
weight: FontWeight.w500,
|
||||
),
|
||||
SizedBox(height: 6.h),
|
||||
CustomText(
|
||||
text: "CityCards24@gmail.com",
|
||||
color: Colors.black,
|
||||
size: 14.sp,
|
||||
weight: FontWeight.w600,
|
||||
),
|
||||
|
||||
SizedBox(height: 6.h),
|
||||
CustomText(
|
||||
text: "Tap to email",
|
||||
color: Colors.black.withOpacity(
|
||||
.4,
|
||||
),
|
||||
size: 12.sp,
|
||||
weight: FontWeight.w400,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(
|
||||
context,
|
||||
).pushNamed(RouteConstants.makeBooking);
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 24.w,
|
||||
vertical: 18.h,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFF95F62),
|
||||
borderRadius: BorderRadius.circular(10.r),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
CustomText(
|
||||
text: "Via CityCards",
|
||||
size: 16.sp,
|
||||
weight: FontWeight.w500,
|
||||
color: Colors.white,
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
CustomText(
|
||||
text:
|
||||
"Create a booking via app",
|
||||
size: 11.sp,
|
||||
weight: FontWeight.w400,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Icon(
|
||||
Icons.arrow_forward_ios_outlined,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(height: 30.h),
|
||||
|
||||
Divider(color: Colors.black.withOpacity(0.2)),
|
||||
SizedBox(height: 30.h),
|
||||
Text(
|
||||
"What is included",
|
||||
style: TextStyle(
|
||||
fontSize: 24.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 4.h),
|
||||
|
||||
Wrap(
|
||||
runSpacing: 16.h,
|
||||
spacing: 16.w,
|
||||
children: [
|
||||
includedBox(
|
||||
"assets/icons/bus.png",
|
||||
"Bus",
|
||||
"Transportation",
|
||||
),
|
||||
includedBox(
|
||||
"assets/icons/clock.png",
|
||||
"2 day 1 night",
|
||||
"Duration",
|
||||
),
|
||||
includedBox(
|
||||
"assets/icons/bx_qr.png",
|
||||
"TAC200812695",
|
||||
"Product code",
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 30.h),
|
||||
|
||||
Divider(color: Colors.black.withOpacity(0.2)),
|
||||
SizedBox(height: 30.h),
|
||||
Text(
|
||||
"Exact Location",
|
||||
style: TextStyle(
|
||||
fontSize: 18.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
|
||||
CustomText(
|
||||
text: "View the location on map",
|
||||
size: 12.sp,
|
||||
color: Colors.black.withOpacity(.6),
|
||||
),
|
||||
SizedBox(height: 17.h),
|
||||
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(13.54.r),
|
||||
child: Image.asset(
|
||||
height: 178.7.h,
|
||||
width: double.infinity,
|
||||
"assets/images/attra_detail_map.png",
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 17.h),
|
||||
|
||||
CustomText(
|
||||
text:
|
||||
"Angkor Mails Hotel \nNR6, Krong Siem Reap Cambodia",
|
||||
size: 12.sp,
|
||||
color: Colors.black.withOpacity(0.6),
|
||||
),
|
||||
|
||||
SizedBox(height: 30.h),
|
||||
|
||||
Divider(color: Colors.black.withOpacity(0.2)),
|
||||
SizedBox(height: 30.h),
|
||||
Text(
|
||||
"People frequently ask",
|
||||
style: TextStyle(
|
||||
fontSize: 18.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 15.h),
|
||||
|
||||
faqBox(
|
||||
"About this place",
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. A id diam nisl, non justo, in odio...",
|
||||
() {},
|
||||
),
|
||||
|
||||
SizedBox(height: 15.h),
|
||||
|
||||
faqBox(
|
||||
"Term and condition",
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. A id diam nisl, non justo, in odio...",
|
||||
() {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
RouteConstants.termsAndCondition,
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 15.h),
|
||||
|
||||
faqBox(
|
||||
"Cancellation Policy",
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. A id diam nisl, non justo, in odio...",
|
||||
() {},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 24.h),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget includedBox(String icon, String title, String disc) {
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 10.h),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFFFF5F5),
|
||||
borderRadius: BorderRadius.circular(10.r),
|
||||
border: Border.all(color: Color(0xFFFDCDCE)),
|
||||
),
|
||||
child: IntrinsicWidth(
|
||||
child: Row(
|
||||
children: [
|
||||
Image.asset(icon, scale: 4),
|
||||
SizedBox(width: 16.w),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CustomText(
|
||||
text: title,
|
||||
size: 16.sp,
|
||||
weight: FontWeight.w500,
|
||||
color: Color(0xFF212121),
|
||||
),
|
||||
SizedBox(height: 4.h),
|
||||
CustomText(
|
||||
text: disc,
|
||||
size: 11.sp,
|
||||
weight: FontWeight.w400,
|
||||
color: Color(0xFF666666),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget faqBox(String title, String desc, Function() onTap) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 12.h),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFFFF5F5),
|
||||
border: Border.all(color: Color(0xFFFDCDCE)),
|
||||
borderRadius: BorderRadius.circular(10.r),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
CustomText(
|
||||
text: title,
|
||||
size: 16.sp,
|
||||
weight: FontWeight.w500,
|
||||
color: Color(0xFF212121),
|
||||
),
|
||||
SizedBox(width: 20.w),
|
||||
Icon(Icons.arrow_forward_ios_outlined, size: 18.sp),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 9.h),
|
||||
CustomText(text: desc, size: 11.sp, color: Color(0xFF7D7D7D)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AttractionDetailsViewModel {
|
||||
// 📞 Call method
|
||||
Future<void> makePhoneCall(String phoneNumber, BuildContext context) async {
|
||||
final Uri url = Uri(scheme: 'tel', path: phoneNumber);
|
||||
if (await canLaunchUrl(url)) {
|
||||
await launchUrl(url);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Could not launch $phoneNumber')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 📧 Email method
|
||||
Future<void> sendEmail(String emailAddress, BuildContext context) async {
|
||||
final Uri url = Uri(
|
||||
scheme: 'mailto',
|
||||
path: emailAddress,
|
||||
query: 'subject=Hello City Cards&body=Hi there,',
|
||||
);
|
||||
|
||||
if (await canLaunchUrl(url)) {
|
||||
await launchUrl(url);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Could not launch mail app')),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'package:citycards_customer/common_packages/common_app_texts.dart';
|
||||
|
||||
import '../models/attraction_model.dart';
|
||||
|
||||
class AttractionsRepository {
|
||||
@@ -8,7 +10,7 @@ class AttractionsRepository {
|
||||
location: "Krong Siem Reap",
|
||||
price: "\$25",
|
||||
image: "assets/dummy/dummy_1.jpg",
|
||||
tags: ["Unlimited Card", "Flexi Card"],
|
||||
tags: ["Unlimited Card", "${CommonAppText.selectiveCard} Card"],
|
||||
isBookingRequired: false,
|
||||
description:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Convallis condimentum morbi non egestas enim amet sagittis. Proin sed aliquet rhoncus ut pellentesque ullamcorper sit eget ac.Sit nisi, cras amet varius eget egestas pellentesque. Cursus gravida euismod non... ",
|
||||
@@ -28,7 +30,7 @@ class AttractionsRepository {
|
||||
location: "Krong Siem Reap",
|
||||
price: "\$25",
|
||||
image: "assets/dummy/dummy_3.jpg",
|
||||
tags: ["Unlimited Card", "Flexi Card"],
|
||||
tags: ["Unlimited Card", "${CommonAppText.selectiveCard} Card"],
|
||||
isBookingRequired: false,
|
||||
description:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Convallis condimentum morbi non egestas enim amet sagittis. Proin sed aliquet rhoncus ut pellentesque ullamcorper sit eget ac.Sit nisi, cras amet varius eget egestas pellentesque. Cursus gravida euismod non... ",
|
||||
@@ -38,7 +40,7 @@ class AttractionsRepository {
|
||||
location: "Krong Siem Reap",
|
||||
price: "\$25",
|
||||
image: "assets/dummy/dummy_4.jpg",
|
||||
tags: ["Flexi Card"],
|
||||
tags: ["${CommonAppText.selectiveCard} Card"],
|
||||
isBookingRequired: false,
|
||||
description:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Convallis condimentum morbi non egestas enim amet sagittis. Proin sed aliquet rhoncus ut pellentesque ullamcorper sit eget ac.Sit nisi, cras amet varius eget egestas pellentesque. Cursus gravida euismod non... ",
|
||||
@@ -48,7 +50,7 @@ class AttractionsRepository {
|
||||
location: "Krong Siem Reap",
|
||||
price: "\$25",
|
||||
image: "assets/dummy/dummy_5.jpg",
|
||||
tags: ["Unlimited Card", "Flexi Card"],
|
||||
tags: ["Unlimited Card", "${CommonAppText.selectiveCard} Card"],
|
||||
isBookingRequired: false,
|
||||
description:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Convallis condimentum morbi non egestas enim amet sagittis. Proin sed aliquet rhoncus ut pellentesque ullamcorper sit eget ac.Sit nisi, cras amet varius eget egestas pellentesque. Cursus gravida euismod non... ",
|
||||
@@ -63,7 +65,7 @@ class AttractionsRepository {
|
||||
location: "Krong Siem Reap",
|
||||
price: "\$25",
|
||||
image: "assets/dummy/dummy_1.jpg",
|
||||
tags: ["Unlimited Card", "Flexi Card"],
|
||||
tags: ["Unlimited Card", "${CommonAppText.selectiveCard} Card"],
|
||||
isBookingRequired: true,
|
||||
description:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Convallis condimentum morbi non egestas enim amet sagittis. Proin sed aliquet rhoncus ut pellentesque ullamcorper sit eget ac.Sit nisi, cras amet varius eget egestas pellentesque. Cursus gravida euismod non... ",
|
||||
@@ -83,7 +85,7 @@ class AttractionsRepository {
|
||||
location: "Krong Siem Reap",
|
||||
price: "\$25",
|
||||
image: "assets/dummy/dummy_3.jpg",
|
||||
tags: ["Unlimited Card", "Flexi Card"],
|
||||
tags: ["Unlimited Card", "${CommonAppText.selectiveCard} Card"],
|
||||
isBookingRequired: true,
|
||||
description:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Convallis condimentum morbi non egestas enim amet sagittis. Proin sed aliquet rhoncus ut pellentesque ullamcorper sit eget ac.Sit nisi, cras amet varius eget egestas pellentesque. Cursus gravida euismod non... ",
|
||||
@@ -93,7 +95,7 @@ class AttractionsRepository {
|
||||
location: "Krong Siem Reap",
|
||||
price: "\$25",
|
||||
image: "assets/dummy/dummy_4.jpg",
|
||||
tags: ["Flexi Card"],
|
||||
tags: ["${CommonAppText.selectiveCard} Card"],
|
||||
isBookingRequired: true,
|
||||
description:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Convallis condimentum morbi non egestas enim amet sagittis. Proin sed aliquet rhoncus ut pellentesque ullamcorper sit eget ac.Sit nisi, cras amet varius eget egestas pellentesque. Cursus gravida euismod non... ",
|
||||
@@ -103,7 +105,7 @@ class AttractionsRepository {
|
||||
location: "Krong Siem Reap",
|
||||
price: "\$25",
|
||||
image: "assets/dummy/dummy_5.jpg",
|
||||
tags: ["Unlimited Card", "Flexi Card"],
|
||||
tags: ["Unlimited Card", "${CommonAppText.selectiveCard} Card"],
|
||||
isBookingRequired: true,
|
||||
description:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Convallis condimentum morbi non egestas enim amet sagittis. Proin sed aliquet rhoncus ut pellentesque ullamcorper sit eget ac.Sit nisi, cras amet varius eget egestas pellentesque. Cursus gravida euismod non... ",
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import 'package:citycards_customer/attraction_details/bloc/attraction_details_bloc.dart';
|
||||
import 'package:citycards_customer/common_packages/app_bar.dart';
|
||||
import 'package:citycards_customer/common_packages/back_widget.dart';
|
||||
import 'package:citycards_customer/core/route_constants.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import '../../attraction_details/bloc/attraction_details_event.dart';
|
||||
import '../../common_packages/custom_search_field.dart';
|
||||
import '../blocs/attractions_bloc.dart';
|
||||
import '../repository/attractions_repository.dart';
|
||||
@@ -18,27 +15,19 @@ class AttractionsPage extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
create: (_) {
|
||||
final bloc = AttractionsBloc(AttractionsRepository());
|
||||
// 🔥 Trigger event based on source
|
||||
if (source == "home") {
|
||||
bloc.add(LoadAttractions());
|
||||
} else {
|
||||
print("QR Passss -=------------------");
|
||||
context.read<AttractionDetailsBloc>().add(SetFlowFromPass(true));
|
||||
bloc.add(LoadMyPassAttraction());
|
||||
}
|
||||
return BlocProvider(
|
||||
create: (_) {
|
||||
final bloc = AttractionsBloc(AttractionsRepository());
|
||||
|
||||
return bloc;
|
||||
},
|
||||
),
|
||||
BlocProvider(create: (_) => AttractionDetailsBloc(),
|
||||
// 🔥 Trigger event based on source
|
||||
if (source == "home") {
|
||||
bloc.add(LoadAttractions());
|
||||
} else if (source == "qrPass") {
|
||||
bloc.add(LoadMyPassAttraction());
|
||||
}
|
||||
|
||||
)
|
||||
],
|
||||
return bloc;
|
||||
},
|
||||
child: BlocBuilder<AttractionsBloc, AttractionsState>(
|
||||
builder: (context, state) {
|
||||
final bloc = context.read<AttractionsBloc>();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import '../../common_packages/common_app_texts.dart';
|
||||
import '../../core/route_constants.dart';
|
||||
import '../models/attraction_model.dart';
|
||||
|
||||
@@ -11,12 +12,7 @@ class AttractionCard extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: (){
|
||||
print("the value of from page this pushed ${ModalRoute.of(context)?.settings.arguments}");
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
RouteConstants.attractionDetails,
|
||||
arguments: ModalRoute.of(context)?.settings.arguments, // FORWARD
|
||||
);
|
||||
Navigator.of(context).pushNamed(RouteConstants.attractionDetails);
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 8),
|
||||
@@ -91,13 +87,13 @@ class AttractionCard extends StatelessWidget {
|
||||
vertical: 4,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: tag == "Flexi Card"
|
||||
color: tag == "${CommonAppText.selectiveCard} Card"
|
||||
? const Color(0xffF95FAF).withOpacity(0.1)
|
||||
: const Color(
|
||||
0xffF95F62,
|
||||
).withOpacity(0.1),
|
||||
border: Border.all(
|
||||
color: tag == "Flexi Card"
|
||||
color: tag == "${CommonAppText.selectiveCard} Card"
|
||||
? const Color(0xffF95FAF)
|
||||
: const Color(0xffF95F62),
|
||||
),
|
||||
|
||||
@@ -7,6 +7,8 @@ import 'package:citycards_customer/core/route_constants.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
||||
import '../../common_packages/common_app_texts.dart';
|
||||
|
||||
class BuyPassView extends StatelessWidget {
|
||||
BuyPassView({super.key});
|
||||
|
||||
@@ -68,9 +70,9 @@ class BuyPassView extends StatelessWidget {
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: [
|
||||
PassCardView(themeColor: Color(0xFFF95FAF)),
|
||||
PassCardView(themeColor: Color(0xFFF97316)),
|
||||
SizedBox(width: 12.w),
|
||||
PassCardView(themeColor: Color(0xFF1E8AF6)),
|
||||
PassCardView(themeColor: Color(0xFF1E8AF6),),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -203,6 +205,8 @@ class BuyPassView extends StatelessWidget {
|
||||
text: offer["description"] ?? "",
|
||||
color: Colors.black.withOpacity(.6),
|
||||
size: 12.sp,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -215,7 +219,7 @@ class BuyPassView extends StatelessWidget {
|
||||
Center(
|
||||
child: PaymentCard(
|
||||
city: 'Melbourne',
|
||||
tag: 'Flexi Card',
|
||||
tag: '${CommonAppText.selectiveCard} Card',
|
||||
oldPrice: 120,
|
||||
newPrice: 90,
|
||||
),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
||||
import '../../common_packages/common_app_texts.dart';
|
||||
|
||||
class FeatureTable extends StatelessWidget {
|
||||
const FeatureTable({super.key});
|
||||
|
||||
@@ -66,7 +68,7 @@ class FeatureTable extends StatelessWidget {
|
||||
padding: EdgeInsets.symmetric(vertical: 6.h),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Flexi',
|
||||
'${CommonAppText.selectiveCard}',
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 16.sp),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -2,6 +2,8 @@ import 'package:citycards_customer/common_packages/custom_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
||||
import '../../common_packages/common_app_texts.dart';
|
||||
|
||||
class PassCardView extends StatelessWidget {
|
||||
final Color? themeColor;
|
||||
final String? city;
|
||||
@@ -26,7 +28,7 @@ class PassCardView extends StatelessWidget {
|
||||
border: Border.all(color:( themeColor ?? Color(0xFFF95FAF)).withOpacity(0.24)),
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
),
|
||||
child: Row(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
@@ -143,7 +145,7 @@ class PassCardView extends StatelessWidget {
|
||||
text: TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "Flexi ",
|
||||
text: "${CommonAppText.selectiveCard} ",
|
||||
style: TextStyle(color: Colors.white, fontSize: 16.sp),
|
||||
),
|
||||
TextSpan(
|
||||
@@ -158,6 +160,6 @@ class PassCardView extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import 'package:citycards_customer/common_packages/custom_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import '../../checkout/widget/login_email_bottomsheet.dart';
|
||||
import '../../common_packages/common_app_texts.dart';
|
||||
import '../blocs/pass_bloc.dart';
|
||||
|
||||
class MyPassesPage extends StatelessWidget {
|
||||
@@ -23,14 +25,13 @@ class MyPassesPage extends StatelessWidget {
|
||||
children: [
|
||||
SizedBox(height: 22.h),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
border: Border.all(
|
||||
color: Color(0xFFF95FAF).withOpacity(0.2),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
border: Border.all(
|
||||
color: Color(0xFFF95FAF).withOpacity(0.2),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
),
|
||||
child: Expanded(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
@@ -154,7 +155,7 @@ class MyPassesPage extends StatelessWidget {
|
||||
width: 35.w,
|
||||
height: 123.h,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFF95FAF),
|
||||
color: Color(0xFFF97316),
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomRight: Radius.circular(8.r),
|
||||
topRight: Radius.circular(8.r),
|
||||
@@ -167,7 +168,7 @@ class MyPassesPage extends StatelessWidget {
|
||||
text: TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "Flexi ",
|
||||
text: "${CommonAppText.selectiveCard} ",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16.sp,
|
||||
@@ -189,8 +190,6 @@ class MyPassesPage extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 15.h),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
@@ -336,7 +335,19 @@ class MyPassesPage extends StatelessWidget {
|
||||
SizedBox(height: 150.h,),
|
||||
|
||||
CustomFilledButton(
|
||||
onTap: () {},
|
||||
onTap: () {
|
||||
showModalBottomSheet(
|
||||
backgroundColor: Colors.white,
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(12.r),
|
||||
),
|
||||
),
|
||||
builder: (_) => const LoginEmailBottomsheet(),
|
||||
);
|
||||
},
|
||||
width: double.infinity,
|
||||
label: "Proceed to Checkout",
|
||||
),
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:citycards_customer/common_packages/custom_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import '../../checkout/widget/login_email_bottomsheet.dart';
|
||||
import '../blocs/postcard_bloc.dart';
|
||||
|
||||
class MyPostCardsPage extends StatelessWidget {
|
||||
@@ -156,7 +157,19 @@ class MyPostCardsPage extends StatelessWidget {
|
||||
),
|
||||
SizedBox(height: 60.h),
|
||||
CustomFilledButton(
|
||||
onTap: () {},
|
||||
onTap: () {
|
||||
showModalBottomSheet(
|
||||
backgroundColor: Colors.white,
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(12.r),
|
||||
),
|
||||
),
|
||||
builder: (_) => const LoginEmailBottomsheet(),
|
||||
);
|
||||
},
|
||||
width: double.infinity,
|
||||
label: "Proceed to Checkout",
|
||||
),
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
/// EVENTS
|
||||
abstract class EmailVerify {}
|
||||
|
||||
class SetEmailEvent extends EmailVerify {
|
||||
final String email;
|
||||
SetEmailEvent(this.email);
|
||||
}
|
||||
|
||||
class CheckOtpFilled extends EmailVerify {
|
||||
final bool isOtpFilled;
|
||||
CheckOtpFilled(this.isOtpFilled);
|
||||
}
|
||||
|
||||
class CheckIsLoggedIn extends EmailVerify{}
|
||||
|
||||
/// STATE
|
||||
class EmailVerifyState {
|
||||
final String email;
|
||||
final bool isOtpFilled;
|
||||
final bool loggedIn;
|
||||
|
||||
EmailVerifyState({
|
||||
required this.email,
|
||||
required this.isOtpFilled,
|
||||
required this.loggedIn
|
||||
});
|
||||
|
||||
EmailVerifyState copyWith({
|
||||
String? email,
|
||||
bool? isOtpFilled,
|
||||
bool? loggedIn
|
||||
}) {
|
||||
return EmailVerifyState(
|
||||
email: email ?? this.email,
|
||||
isOtpFilled: isOtpFilled ?? this.isOtpFilled,
|
||||
loggedIn: loggedIn ?? this.loggedIn
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// BLOC
|
||||
class EmailVerifyBloc extends Bloc<EmailVerify, EmailVerifyState> {
|
||||
EmailVerifyBloc()
|
||||
: super(EmailVerifyState(email: "", isOtpFilled: false,
|
||||
loggedIn: false
|
||||
)) {
|
||||
|
||||
on<SetEmailEvent>((event, emit) {
|
||||
emit(state.copyWith(email: event.email));
|
||||
});
|
||||
|
||||
on<CheckOtpFilled>((event, emit) {
|
||||
emit(state.copyWith(isOtpFilled: event.isOtpFilled));
|
||||
});
|
||||
|
||||
on<CheckIsLoggedIn>((event,emit){
|
||||
emit(state.copyWith(loggedIn: true));
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class LocalAuth {
|
||||
Future<void> setloggedIn(bool islogged)async{
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
prefs.setBool('islogged', islogged);
|
||||
}
|
||||
|
||||
Future<bool> getloggedIn()async{
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getBool('islogged') ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import 'package:citycards_customer/checkout/bloc/email_verify_bloc.dart';
|
||||
import 'package:citycards_customer/checkout/repository/auth_local_repository.dart';
|
||||
import 'package:citycards_customer/checkout/widget/all_coupons_bottomsheet.dart';
|
||||
import 'package:citycards_customer/checkout/widget/login_email_bottomsheet.dart';
|
||||
import 'package:citycards_customer/common_packages/app_bar.dart';
|
||||
@@ -7,391 +5,365 @@ import 'package:citycards_customer/common_packages/custom_filled_button.dart';
|
||||
import 'package:citycards_customer/common_packages/custom_text.dart';
|
||||
import 'package:citycards_customer/common_packages/custom_dashed_line.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
||||
import '../../common_packages/common_app_texts.dart';
|
||||
|
||||
class CheckoutView extends StatelessWidget {
|
||||
const CheckoutView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => EmailVerifyBloc(),
|
||||
child: Scaffold(
|
||||
resizeToAvoidBottomInset: true,
|
||||
backgroundColor: Colors.white,
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 20.w),
|
||||
child: Column(
|
||||
children: [
|
||||
CommonAppBar(
|
||||
isWhiteLogo: false,
|
||||
isProfilePage: false,
|
||||
showCart: false,
|
||||
showDivider: true,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Icon(Icons.arrow_back),
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
CustomText(text: "Checkout", size: 12.sp),
|
||||
],
|
||||
),
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: true,
|
||||
backgroundColor: Colors.white,
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 20.w),
|
||||
child: Column(
|
||||
children: [
|
||||
CommonAppBar(
|
||||
isWhiteLogo: false,
|
||||
isProfilePage: false,
|
||||
showCart: false,
|
||||
showDivider: true,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Icon(Icons.arrow_back),
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
CustomText(text: "Checkout", size: 12.sp),
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(height: 22.h),
|
||||
Container(
|
||||
SizedBox(height: 22.h),
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
border: Border.all(
|
||||
color: Color(0xFFF95FAF).withOpacity(0.2),
|
||||
),
|
||||
border: Border.all(color: Color(0xFFF95FAF).withOpacity(0.2)),
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
),
|
||||
child: Expanded(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(8.r),
|
||||
bottomLeft: Radius.circular(8.r),
|
||||
),
|
||||
child: Image.asset(
|
||||
"assets/images/card_banner.png",
|
||||
scale: 4,
|
||||
width: 105.w,
|
||||
height: 123.h,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 6.66.w),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CustomText(
|
||||
text: "Melbourne",
|
||||
weight: FontWeight.w500,
|
||||
size: 16.sp,
|
||||
),
|
||||
SizedBox(height: 5.h),
|
||||
CustomText(
|
||||
text: "2 Days",
|
||||
color: Color(0xFF8E8E8E),
|
||||
size: 12.sp,
|
||||
),
|
||||
SizedBox(height: 5.h),
|
||||
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * .5,
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/icons/adult.png',
|
||||
scale: 4,
|
||||
),
|
||||
SizedBox(width: 4.w),
|
||||
CustomText(
|
||||
text: "3 adults",
|
||||
color: Color(0xFF8E8E8E),
|
||||
size: 12.sp,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/icons/qty.png',
|
||||
scale: 4,
|
||||
),
|
||||
SizedBox(width: 4.w),
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "Qty:",
|
||||
style: TextStyle(
|
||||
color: Color(0xFF8E8E8E),
|
||||
fontSize: 12.sp,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: " 2",
|
||||
style: TextStyle(
|
||||
color: Color(0xFF000000),
|
||||
fontSize: 12.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 5.h),
|
||||
Row(
|
||||
children: [
|
||||
Image.asset(
|
||||
"assets/icons/kid.png",
|
||||
scale: 4,
|
||||
),
|
||||
SizedBox(width: 4.w),
|
||||
CustomText(
|
||||
text: "3 Kids",
|
||||
color: Color(0xFF8E8E8E),
|
||||
size: 12.sp,
|
||||
),
|
||||
|
||||
SizedBox(width: 53.w),
|
||||
|
||||
CustomText(
|
||||
text: "\$49.50",
|
||||
size: 24.sp,
|
||||
weight: FontWeight.w500,
|
||||
color: Color(0xFFF95F62),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Container(
|
||||
width: 35.w,
|
||||
height: 123.h,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFF95FAF),
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomRight: Radius.circular(8.r),
|
||||
topRight: Radius.circular(8.r),
|
||||
),
|
||||
),
|
||||
child: RotatedBox(
|
||||
quarterTurns: -1,
|
||||
child: Center(
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "Flexi ",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16.sp,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: "Card",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12.sp,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 15.h),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 12.w,
|
||||
vertical: 12.h,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFFFF5F5),
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
border: Border.all(
|
||||
color: Color(0xFFBB474A).withOpacity(0.4),
|
||||
width: 0.8,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
Row(
|
||||
children: [
|
||||
CustomText(
|
||||
text: "Get 10% off on your first trip",
|
||||
color: Color(0xFF262626),
|
||||
size: 14.sp,
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(8.r),
|
||||
bottomLeft: Radius.circular(8.r),
|
||||
),
|
||||
child: Image.asset(
|
||||
"assets/images/card_banner.png",
|
||||
scale: 4,
|
||||
width: 105.w,
|
||||
height: 123.h,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 7.h),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
SizedBox(width: 6.66.w),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(12.r),
|
||||
),
|
||||
CustomText(
|
||||
text: "Melbourne",
|
||||
weight: FontWeight.w500,
|
||||
size: 16.sp,
|
||||
),
|
||||
SizedBox(height: 5.h),
|
||||
CustomText(
|
||||
text: "2 Days",
|
||||
color: Color(0xFF8E8E8E),
|
||||
size: 12.sp,
|
||||
),
|
||||
SizedBox(height: 5.h),
|
||||
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * .5,
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/icons/adult.png',
|
||||
scale: 4,
|
||||
),
|
||||
SizedBox(width: 4.w),
|
||||
CustomText(
|
||||
text: "3 adults",
|
||||
color: Color(0xFF8E8E8E),
|
||||
size: 12.sp,
|
||||
),
|
||||
],
|
||||
),
|
||||
builder: (_) => AllCouponsBottomsheet(),
|
||||
);
|
||||
},
|
||||
child: CustomText(
|
||||
text: "View all coupons",
|
||||
color: Color(0xFFF95F62),
|
||||
size: 12,
|
||||
|
||||
Row(
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/icons/qty.png',
|
||||
scale: 4,
|
||||
),
|
||||
SizedBox(width: 4.w),
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "Qty:",
|
||||
style: TextStyle(
|
||||
color: Color(0xFF8E8E8E),
|
||||
fontSize: 12.sp,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: " 2",
|
||||
style: TextStyle(
|
||||
color: Color(0xFF000000),
|
||||
fontSize: 12.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(width: 3.w),
|
||||
Icon(Icons.arrow_right, color: Color(0xFFF95F62)),
|
||||
|
||||
SizedBox(height: 5.h),
|
||||
Row(
|
||||
children: [
|
||||
Image.asset("assets/icons/kid.png", scale: 4),
|
||||
SizedBox(width: 4.w),
|
||||
CustomText(
|
||||
text: "3 Kids",
|
||||
color: Color(0xFF8E8E8E),
|
||||
size: 12.sp,
|
||||
),
|
||||
|
||||
SizedBox(width: 53.w),
|
||||
|
||||
CustomText(
|
||||
text: "\$49.50",
|
||||
size: 24.sp,
|
||||
weight: FontWeight.w500,
|
||||
color: Color(0xFFF95F62),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const Spacer(),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 20.w,
|
||||
vertical: 10.h,
|
||||
),
|
||||
width: 35.w,
|
||||
height: 123.h,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Color(0xFFF95F62)),
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
color: Color(0xFFF97316),
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomRight: Radius.circular(8.r),
|
||||
topRight: Radius.circular(8.r),
|
||||
),
|
||||
),
|
||||
child: CustomText(
|
||||
text: "Apply",
|
||||
color: Color(0xFFF95F62),
|
||||
size: 14.sp,
|
||||
child: RotatedBox(
|
||||
quarterTurns: -1,
|
||||
child: Center(
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "${CommonAppText.selectiveCard} ",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16.sp,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: "Card",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12.sp,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 15.h),
|
||||
|
||||
DashedDivider(
|
||||
color: Color(0xFFACACAC),
|
||||
thickness: 1.h,
|
||||
dashLength: 4,
|
||||
dashSpace: 4,
|
||||
SizedBox(height: 10.h),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 12.h),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFFFF5F5),
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
border: Border.all(
|
||||
color: Color(0xFFBB474A).withOpacity(0.4),
|
||||
width: 0.8,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10.h),
|
||||
Row(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
CustomText(text: "Subtotal", size: 14.sp),
|
||||
CustomText(
|
||||
text: "\$49.50",
|
||||
size: 14.sp,
|
||||
weight: FontWeight.w500,
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CustomText(
|
||||
text: "Get 10% off on your first trip",
|
||||
color: Color(0xFF262626),
|
||||
size: 14.sp,
|
||||
),
|
||||
SizedBox(height: 7.h),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(12.r),
|
||||
),
|
||||
),
|
||||
builder: (_) => AllCouponsBottomsheet(),
|
||||
);
|
||||
},
|
||||
child: CustomText(
|
||||
text: "View all coupons",
|
||||
color: Color(0xFFF95F62),
|
||||
size: 12,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 3.w),
|
||||
Icon(Icons.arrow_right, color: Color(0xFFF95F62)),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 14.h),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
CustomText(text: "Discount", size: 14.sp),
|
||||
CustomText(
|
||||
text: "-7.20%",
|
||||
size: 14.sp,
|
||||
weight: FontWeight.w500,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.h),
|
||||
DashedDivider(
|
||||
color: Color(0xFFACACAC),
|
||||
thickness: 1.h,
|
||||
dashLength: 4,
|
||||
dashSpace: 4,
|
||||
),
|
||||
SizedBox(height: 10.h),
|
||||
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CustomText(text: 'Total', size: 14.sp),
|
||||
SizedBox(height: 4.h),
|
||||
CustomText(
|
||||
text: "Including \$2.24 in taxes",
|
||||
size: 12.sp,
|
||||
color: Colors.black.withOpacity(0.6),
|
||||
),
|
||||
],
|
||||
const Spacer(),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 20.w,
|
||||
vertical: 10.h,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Color(0xFFF95F62)),
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
),
|
||||
child: CustomText(
|
||||
text: "Apply",
|
||||
color: Color(0xFFF95F62),
|
||||
size: 14.sp,
|
||||
),
|
||||
),
|
||||
CustomText(
|
||||
text: "\$42.60",
|
||||
size: 24.sp,
|
||||
weight: FontWeight.w500,
|
||||
),
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
FutureBuilder(
|
||||
future: LocalAuth().getloggedIn(),
|
||||
builder: (context, snap) {
|
||||
final isLoggedIn = snap.data ?? false;
|
||||
return BlocBuilder<EmailVerifyBloc, EmailVerifyState>(
|
||||
builder: (context, state){
|
||||
return CustomFilledButton(
|
||||
onTap: () async {
|
||||
final rootContext = context;
|
||||
showModalBottomSheet(
|
||||
backgroundColor: Colors.white,
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(12.r),
|
||||
),
|
||||
),
|
||||
builder: (_) => BlocProvider(
|
||||
create: (rootContext) => EmailVerifyBloc(),
|
||||
child: LoginEmailBottomsheet(
|
||||
rootContext: rootContext,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
width: double.infinity,
|
||||
label: isLoggedIn || state.loggedIn ? "Proceed to Checkouts" :"Login to Checkout",
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 25.h),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 15.h),
|
||||
|
||||
DashedDivider(
|
||||
color: Color(0xFFACACAC),
|
||||
thickness: 1.h,
|
||||
dashLength: 4,
|
||||
dashSpace: 4,
|
||||
),
|
||||
SizedBox(height: 10.h),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
CustomText(text: "Subtotal", size: 14.sp),
|
||||
CustomText(
|
||||
text: "\$49.50",
|
||||
size: 14.sp,
|
||||
weight: FontWeight.w500,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 14.h),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
CustomText(text: "Discount", size: 14.sp),
|
||||
CustomText(
|
||||
text: "-7.20%",
|
||||
size: 14.sp,
|
||||
weight: FontWeight.w500,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.h),
|
||||
DashedDivider(
|
||||
color: Color(0xFFACACAC),
|
||||
thickness: 1.h,
|
||||
dashLength: 4,
|
||||
dashSpace: 4,
|
||||
),
|
||||
SizedBox(height: 10.h),
|
||||
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CustomText(text: 'Total', size: 14.sp),
|
||||
SizedBox(height: 4.h),
|
||||
CustomText(
|
||||
text: "Including \$2.24 in taxes",
|
||||
size: 12.sp,
|
||||
color: Colors.black.withOpacity(0.6),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
CustomText(
|
||||
text: "\$42.60",
|
||||
size: 24.sp,
|
||||
weight: FontWeight.w500,
|
||||
),
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
CustomFilledButton(
|
||||
onTap: () {
|
||||
showModalBottomSheet(
|
||||
backgroundColor: Colors.white,
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(12.r),
|
||||
),
|
||||
),
|
||||
builder: (_) => const LoginEmailBottomsheet(),
|
||||
);
|
||||
},
|
||||
width: double.infinity,
|
||||
label: "Login to Checkout",
|
||||
),
|
||||
SizedBox(height: 25.h),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,35 +1,12 @@
|
||||
import 'package:citycards_customer/checkout/bloc/email_verify_bloc.dart';
|
||||
import 'package:citycards_customer/checkout/widget/verify_otp_bottomsheet.dart';
|
||||
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_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
||||
class LoginEmailBottomsheet extends StatefulWidget {
|
||||
final BuildContext rootContext;
|
||||
LoginEmailBottomsheet({super.key, required this.rootContext});
|
||||
|
||||
@override
|
||||
State<LoginEmailBottomsheet> createState() => _LoginEmailBottomsheetState();
|
||||
}
|
||||
|
||||
class _LoginEmailBottomsheetState extends State<LoginEmailBottomsheet> {
|
||||
TextEditingController emailController = TextEditingController();
|
||||
String? emailError;
|
||||
|
||||
bool emailValidate() {
|
||||
final emailRegex = RegExp(r'^[\w\.-]+@[\w\.-]+\.\w+$');
|
||||
|
||||
setState(() {
|
||||
emailError = !emailRegex.hasMatch(emailController.text.trim())
|
||||
? "Invalid email format"
|
||||
: null;
|
||||
});
|
||||
|
||||
return emailError == null;
|
||||
}
|
||||
class LoginEmailBottomsheet extends StatelessWidget {
|
||||
const LoginEmailBottomsheet({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -42,81 +19,47 @@ class _LoginEmailBottomsheetState extends State<LoginEmailBottomsheet> {
|
||||
right: 20.h,
|
||||
bottom: MediaQuery.of(context).viewInsets.bottom,
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min, // shrink to fit content
|
||||
children: [
|
||||
Image.asset("assets/logo/logo_city_cards_orange.png", scale: 4),
|
||||
SizedBox(height: 8.h),
|
||||
CustomText(
|
||||
text: "Get Started",
|
||||
size: 18.sp,
|
||||
weight: FontWeight.w500,
|
||||
),
|
||||
SizedBox(height: 42.h),
|
||||
CustomText(
|
||||
text: "Enter your email to begin your CityCards journey",
|
||||
size: 14.sp,
|
||||
color: const Color(0xFF000000).withOpacity(.6),
|
||||
),
|
||||
SizedBox(height: 12.h),
|
||||
|
||||
TextField(
|
||||
controller: emailController,
|
||||
onChanged: (val) {
|
||||
final bloc = context.read<EmailVerifyBloc>();
|
||||
setState(() => emailValidate());
|
||||
bloc.add(SetEmailEvent(val));
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
errorText: emailError,
|
||||
filled: true,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 6.h),
|
||||
fillColor: const Color(0xFFFFF5F5),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: const Color(0xFFBB474A),
|
||||
width: 0.4.w,
|
||||
child: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min, // shrink to fit content
|
||||
children: [
|
||||
Image.asset("assets/logo/logo_city_cards_orange.png", scale: 4),
|
||||
SizedBox(height: 8.h),
|
||||
CustomText(text: "Get Started", size: 18.sp, weight: FontWeight.w500),
|
||||
SizedBox(height: 42.h),
|
||||
CustomText(
|
||||
text: "Enter your email to begin your CityCards journey",
|
||||
size: 14.sp,
|
||||
color: const Color(0xFF000000).withOpacity(.6),
|
||||
),
|
||||
SizedBox(height: 12.h),
|
||||
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 6.h),
|
||||
fillColor: const Color(0xFFFFF5F5),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: const Color(0xFFBB474A), width: 0.4.w),
|
||||
borderRadius: BorderRadius.circular(8.sp),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8.sp),
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: const Color(0xFFBB474A),
|
||||
width: 0.4.w,
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: const Color(0xFFBB474A), width: 0.4.w),
|
||||
borderRadius: BorderRadius.circular(8.sp),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8.sp),
|
||||
),
|
||||
focusedErrorBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: const Color(0xFFBB474A),
|
||||
width: 0.4.w,
|
||||
prefixIcon: const Icon(Icons.email_outlined, color: Color(0xFFF95F62)),
|
||||
hintText: "john.doe@gmail.com",
|
||||
hintStyle: TextStyle(
|
||||
color: const Color(0xFF000000).withOpacity(0.6),
|
||||
fontSize: 12.sp,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8.sp),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: const Color(0xFFBB474A),
|
||||
width: 0.4.w,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8.sp),
|
||||
),
|
||||
prefixIcon: const Icon(
|
||||
Icons.email_outlined,
|
||||
color: Color(0xFFF95F62),
|
||||
),
|
||||
hintText: "john.doe@gmail.com",
|
||||
hintStyle: TextStyle(
|
||||
color: const Color(0xFF000000).withOpacity(0.6),
|
||||
fontSize: 12.sp,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 38.h),
|
||||
CustomFilledButton(
|
||||
onTap: () {
|
||||
if (emailValidate()) {
|
||||
|
||||
SizedBox(height: 38.h),
|
||||
CustomFilledButton(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
@@ -127,47 +70,44 @@ class _LoginEmailBottomsheetState extends State<LoginEmailBottomsheet> {
|
||||
top: Radius.circular(12.r),
|
||||
),
|
||||
),
|
||||
builder: (_) => BlocProvider(
|
||||
create: (rootcontext) => EmailVerifyBloc(),
|
||||
child: VerifyOtpBottomsheet(),
|
||||
),
|
||||
builder: (_) => VerifyOtpBottomsheet(),
|
||||
);
|
||||
}
|
||||
},
|
||||
label: "Continue",
|
||||
width: double.infinity,
|
||||
),
|
||||
|
||||
SizedBox(height: 20.h),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context).pushNamed(RouteConstants.createAcct);
|
||||
},
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "Already have an account?",
|
||||
style: TextStyle(
|
||||
color: Colors.black.withOpacity(0.6),
|
||||
fontSize: 12.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
},
|
||||
label: "Continue",
|
||||
width: double.infinity,
|
||||
),
|
||||
|
||||
SizedBox(height: 20.h),
|
||||
InkWell(
|
||||
onTap: (){
|
||||
Navigator.of(context).pushNamed(RouteConstants.createAcct);
|
||||
},
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "Already have an account?",
|
||||
style: TextStyle(
|
||||
color: Colors.black.withOpacity(0.6),
|
||||
fontSize: 12.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: " Sign in",
|
||||
style: TextStyle(
|
||||
color: const Color(0xFFF95F62),
|
||||
fontSize: 12.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
TextSpan(
|
||||
text: " Sign in",
|
||||
style: TextStyle(
|
||||
color: const Color(0xFFF95F62),
|
||||
fontSize: 12.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 15.h),
|
||||
],
|
||||
SizedBox(height: 15.h),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import 'package:citycards_customer/checkout/bloc/email_verify_bloc.dart';
|
||||
import 'package:citycards_customer/checkout/repository/auth_local_repository.dart';
|
||||
import 'package:citycards_customer/common_packages/custom_filled_button.dart';
|
||||
import 'package:citycards_customer/common_packages/custom_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_otp_text_field/flutter_otp_text_field.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
||||
@@ -28,88 +25,64 @@ class VerifyOtpBottomsheet extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.min, // shrink to fit content
|
||||
children: [
|
||||
Image.asset("assets/logo/logo_city_cards_orange.png", scale: 4),
|
||||
|
||||
SizedBox(height: 8.h),
|
||||
|
||||
CustomText(
|
||||
text: "Verify your phone",
|
||||
size: 18.sp,
|
||||
weight: FontWeight.w500,
|
||||
),
|
||||
|
||||
SizedBox(height: 42.h),
|
||||
|
||||
BlocBuilder<EmailVerifyBloc, EmailVerifyState>(
|
||||
builder: (context, state) {
|
||||
return Text.rich(
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text:
|
||||
"Enter the verification code sent to your email id: ",
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
color: Colors.black.withOpacity(0.6),
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: " ${state.email}",
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
],
|
||||
text: "Enter the verification code sent to your email id",
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
color: Colors.black.withOpacity(0.6),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
TextSpan(
|
||||
text: " frank7824@mail.com",
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 15.h),
|
||||
|
||||
BlocBuilder<EmailVerifyBloc, EmailVerifyState>(
|
||||
builder: (context, state) {
|
||||
final bloc = (context).read<EmailVerifyBloc>();
|
||||
return OtpTextField(
|
||||
numberOfFields: 6,
|
||||
borderWidth: 0.4.w,
|
||||
fieldWidth: 48.w,
|
||||
fieldHeight: 60.h,
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
filled: true,
|
||||
fillColor: const Color(0xFFFFF5F5),
|
||||
borderColor: const Color(0xFFBB474A),
|
||||
cursorColor: const Color(0xFFF95F62),
|
||||
showFieldAsBox: true,
|
||||
textStyle: TextStyle(
|
||||
fontSize: 18.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
onCodeChanged: (code) {},
|
||||
onSubmit: (code) {
|
||||
bloc.add(CheckOtpFilled(true));
|
||||
debugPrint("OTP entered: $code");
|
||||
},
|
||||
);
|
||||
OtpTextField(
|
||||
numberOfFields: 6,
|
||||
borderWidth: 0.4.w,
|
||||
fieldWidth: 48.w,
|
||||
fieldHeight: 60.h,
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
filled: true,
|
||||
fillColor: const Color(0xFFFFF5F5),
|
||||
borderColor: const Color(0xFFBB474A),
|
||||
cursorColor: const Color(0xFFF95F62),
|
||||
showFieldAsBox: true,
|
||||
textStyle: TextStyle(
|
||||
fontSize: 18.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
onCodeChanged: (code) {},
|
||||
onSubmit: (code) {
|
||||
debugPrint("OTP entered: $code");
|
||||
},
|
||||
),
|
||||
|
||||
SizedBox(height: 42.h),
|
||||
BlocBuilder<EmailVerifyBloc, EmailVerifyState>(
|
||||
builder: (context, state) {
|
||||
return CustomFilledButton(
|
||||
onTap: () async {
|
||||
if (state.isOtpFilled) {
|
||||
Navigator.pop(context);
|
||||
await LocalAuth().setloggedIn(true);
|
||||
context.read<EmailVerifyBloc>().add(CheckIsLoggedIn());
|
||||
}
|
||||
},
|
||||
label: "Continue",
|
||||
width: double.infinity,
|
||||
);
|
||||
CustomFilledButton(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
label: "Continue",
|
||||
width: double.infinity,
|
||||
),
|
||||
|
||||
SizedBox(height: 20.h),
|
||||
|
||||
3
lib/common_packages/common_app_texts.dart
Normal file
3
lib/common_packages/common_app_texts.dart
Normal file
@@ -0,0 +1,3 @@
|
||||
class CommonAppText {
|
||||
static const String selectiveCard = "Selective";
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:citycards_customer/Profile/profile_page_view.dart';
|
||||
import 'package:citycards_customer/add_details/add_details_view.dart';
|
||||
import 'package:citycards_customer/attraction_details/view/attraction_details_view.dart';
|
||||
import 'package:citycards_customer/attraction_details/attraction_details_view.dart';
|
||||
import 'package:citycards_customer/buy_a_pass/view/buy_pass_view.dart';
|
||||
import 'package:citycards_customer/checkout/view/checkout_view.dart';
|
||||
import 'package:citycards_customer/common_bloc/language_selection_bloc.dart';
|
||||
@@ -18,7 +18,6 @@ import 'package:citycards_customer/itinerary_creation/views/itinerary_creation_v
|
||||
import 'package:citycards_customer/itinerary_creation/views/magic_itinerary_empty_view.dart';
|
||||
import 'package:citycards_customer/itinerary_creation/views/magic_itinerary_filled_view.dart';
|
||||
import 'package:citycards_customer/offer_pass_detail/offer_pass_detail_view.dart';
|
||||
import 'package:citycards_customer/postcard/views/postcard_creation_page_view.dart';
|
||||
import 'package:citycards_customer/privacy/privacy_view.dart';
|
||||
import 'package:citycards_customer/search_offers/bloc/search_offers_listing_bloc.dart';
|
||||
import 'package:citycards_customer/search_offers/view/search_offers_with_listing.dart';
|
||||
@@ -232,12 +231,6 @@ class AppRouter {
|
||||
return RegisteredUserHomePage();
|
||||
},
|
||||
);
|
||||
|
||||
case RouteConstants.postCardCreationPage:
|
||||
return MaterialPageRoute(builder: (_){
|
||||
return PostcardCreationPage();
|
||||
});
|
||||
|
||||
default:
|
||||
return MaterialPageRoute(
|
||||
builder: (_) =>
|
||||
|
||||
@@ -5,7 +5,7 @@ import 'package:citycards_customer/postcard/views/add_filter_step_page_view.dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../attraction_details/view/attraction_details_view.dart';
|
||||
import '../attraction_details/attraction_details_view.dart';
|
||||
import '../attractions/views/attractions_page_view.dart';
|
||||
import '../buy_a_pass/view/buy_pass_view.dart';
|
||||
import '../checkout/view/checkout_view.dart';
|
||||
@@ -45,7 +45,7 @@ Widget buildOffstageNavigator(
|
||||
return IntroScreensView();
|
||||
});
|
||||
|
||||
// 🔹 Attractions PageF
|
||||
// 🔹 Attractions Page
|
||||
case RouteConstants.attractionsPage:
|
||||
final args = settings.arguments as String;
|
||||
return MaterialPageRoute(
|
||||
@@ -93,7 +93,7 @@ Widget buildOffstageNavigator(
|
||||
);
|
||||
|
||||
// 🔹 Upload Photo Page (start of postcard creation flow)
|
||||
case RouteConstants.postCardCreationPage:
|
||||
case RouteConstants.uploadPhotoPage:
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => BlocProvider(
|
||||
create: (_) => PostcardCreationBloc(),
|
||||
|
||||
@@ -56,5 +56,4 @@ class RouteConstants {
|
||||
static const String qrPage = '/qrPage';
|
||||
static const String makeBooking = '/makeBooking';
|
||||
static const String bookingSuccessful = '/bookingSuccessful';
|
||||
static const String postCardCreationPage = '/postCardCreationPage';
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ class CreateAccountView extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
body: SafeArea(
|
||||
@@ -110,7 +109,7 @@ class CreateAccountView extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 36.h),
|
||||
SizedBox(height: 16.h),
|
||||
CustomFilledButton(
|
||||
width: double.infinity,
|
||||
onTap: (){}, label: "Create Account")
|
||||
|
||||
112
lib/home/model/city_list_model.dart
Normal file
112
lib/home/model/city_list_model.dart
Normal file
@@ -0,0 +1,112 @@
|
||||
class CityList {
|
||||
List<Cities>? cities;
|
||||
List<UpcomingCities>? upcomingCities;
|
||||
|
||||
CityList({this.cities, this.upcomingCities});
|
||||
|
||||
CityList.fromJson(Map<String, dynamic> json) {
|
||||
if (json['cities'] != null) {
|
||||
cities = <Cities>[];
|
||||
json['cities'].forEach((v) {
|
||||
cities!.add(new Cities.fromJson(v));
|
||||
});
|
||||
}
|
||||
if (json['upcomingCities'] != null) {
|
||||
upcomingCities = <UpcomingCities>[];
|
||||
json['upcomingCities'].forEach((v) {
|
||||
upcomingCities!.add(new UpcomingCities.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
if (this.cities != null) {
|
||||
data['cities'] = this.cities!.map((v) => v.toJson()).toList();
|
||||
}
|
||||
if (this.upcomingCities != null) {
|
||||
data['upcomingCities'] =
|
||||
this.upcomingCities!.map((v) => v.toJson()).toList();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class Cities {
|
||||
int? id;
|
||||
String? cityName;
|
||||
String? tagLine;
|
||||
String? bannerImage;
|
||||
int? indivisualTicketAmt;
|
||||
int? cityCardTicketAmt;
|
||||
int? saveAmount;
|
||||
String? saveLabel;
|
||||
List<UpcomingCities>? upcomingCities;
|
||||
|
||||
Cities(
|
||||
{this.id,
|
||||
this.cityName,
|
||||
this.tagLine,
|
||||
this.bannerImage,
|
||||
this.indivisualTicketAmt,
|
||||
this.cityCardTicketAmt,
|
||||
this.saveAmount,
|
||||
this.saveLabel,
|
||||
this.upcomingCities});
|
||||
|
||||
Cities.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
cityName = json['cityName'];
|
||||
tagLine = json['tagLine'];
|
||||
bannerImage = json['bannerImage'];
|
||||
indivisualTicketAmt = json['indivisualTicketAmt'];
|
||||
cityCardTicketAmt = json['cityCardTicketAmt'];
|
||||
saveAmount = json['saveAmount'];
|
||||
saveLabel = json['saveLabel'];
|
||||
if (json['upcomingCities'] != null) {
|
||||
upcomingCities = <UpcomingCities>[];
|
||||
json['upcomingCities'].forEach((v) {
|
||||
upcomingCities!.add(new UpcomingCities.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['id'] = this.id;
|
||||
data['cityName'] = this.cityName;
|
||||
data['tagLine'] = this.tagLine;
|
||||
data['bannerImage'] = this.bannerImage;
|
||||
data['indivisualTicketAmt'] = this.indivisualTicketAmt;
|
||||
data['cityCardTicketAmt'] = this.cityCardTicketAmt;
|
||||
data['saveAmount'] = this.saveAmount;
|
||||
data['saveLabel'] = this.saveLabel;
|
||||
if (this.upcomingCities != null) {
|
||||
data['upcomingCities'] =
|
||||
this.upcomingCities!.map((v) => v.toJson()).toList();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class UpcomingCities {
|
||||
int? id;
|
||||
String? cityName;
|
||||
String? imgPathName;
|
||||
|
||||
UpcomingCities({this.id, this.cityName, this.imgPathName});
|
||||
|
||||
UpcomingCities.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
cityName = json['cityName'];
|
||||
imgPathName = json['imgPathName'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['id'] = this.id;
|
||||
data['cityName'] = this.cityName;
|
||||
data['imgPathName'] = this.imgPathName;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -23,11 +23,6 @@ class RegisteredUserHomePage extends StatefulWidget {
|
||||
|
||||
class _RegisteredUserHomePageState extends State<RegisteredUserHomePage> {
|
||||
final List<Map<String, String>> attractions = [
|
||||
{
|
||||
'title': 'Long-Tail Boat Charter',
|
||||
'subtitle': 'Lorem ipsum dolor sit amet...',
|
||||
'image': 'assets/images/clock.png',
|
||||
},
|
||||
{
|
||||
'title': 'Koh Rong Samloemr',
|
||||
'subtitle': 'Lorem ipsum dolor sit amet...',
|
||||
@@ -43,6 +38,11 @@ class _RegisteredUserHomePageState extends State<RegisteredUserHomePage> {
|
||||
'subtitle': 'Lorem ipsum dolor sit amet...',
|
||||
'image': 'assets/images/koh_rong.png',
|
||||
},
|
||||
{
|
||||
'title': 'Long-Tail Boat Charter',
|
||||
'subtitle': 'Lorem ipsum dolor sit amet...',
|
||||
'image': 'assets/images/clock.png',
|
||||
},
|
||||
];
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import '../../core/route_constants.dart';
|
||||
|
||||
class AttractionsListView extends StatefulWidget {
|
||||
final List<Map<String, String>> attractions;
|
||||
@@ -38,69 +39,74 @@ class _AttractionsListViewState extends State<AttractionsListView> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 240,
|
||||
child: ListView.builder(
|
||||
controller: _scrollController,
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: const EdgeInsets.only(right: 16),
|
||||
itemCount: widget.attractions.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = widget.attractions[index];
|
||||
return Container(
|
||||
alignment: Alignment.center,
|
||||
margin: const EdgeInsets.only(right: 16),
|
||||
padding: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: const Color(0xFFF95F62).withOpacity(0.24),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Container(
|
||||
height: 232,
|
||||
width: 161,
|
||||
return InkWell(
|
||||
onTap: (){
|
||||
Navigator.of(context).pushNamed(RouteConstants.attractionDetails);
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 240,
|
||||
child: ListView.builder(
|
||||
controller: _scrollController,
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: const EdgeInsets.only(right: 16),
|
||||
itemCount: widget.attractions.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = widget.attractions[index];
|
||||
return Container(
|
||||
alignment: Alignment.center,
|
||||
margin: const EdgeInsets.only(right: 16),
|
||||
padding: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: const Color(0xFFF95F62).withOpacity(0.24),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
image: DecorationImage(
|
||||
image: AssetImage(item['image']!),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
child: Container(
|
||||
height: 232,
|
||||
width: 161,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
image: DecorationImage(
|
||||
image: AssetImage(item['image']!),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
alignment: Alignment.bottomLeft,
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Text(
|
||||
item['title']!,
|
||||
style: GoogleFonts.poppins(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
alignment: Alignment.bottomLeft,
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Text(
|
||||
item['title']!,
|
||||
style: GoogleFonts.poppins(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: SizedBox(
|
||||
width: 200,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: LinearProgressIndicator(
|
||||
value: _scrollProgress,
|
||||
minHeight: 6,
|
||||
backgroundColor: const Color(0xffFEE7E7),
|
||||
color: const Color(0xffF95F62),
|
||||
const SizedBox(height: 20),
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: SizedBox(
|
||||
width: 200,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: LinearProgressIndicator(
|
||||
value: _scrollProgress,
|
||||
minHeight: 6,
|
||||
backgroundColor: const Color(0xffFEE7E7),
|
||||
color: const Color(0xffF95F62),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
import '../../core/route_constants.dart';
|
||||
@@ -10,15 +11,14 @@ class GetYourPassCard extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 14, vertical: 0),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
|
||||
margin: EdgeInsets.symmetric(horizontal: 14.w, vertical: 0),
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 20.h),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFFF1F1),
|
||||
borderRadius: BorderRadius.circular(40),
|
||||
border: Border.all(color: Color(0xffFDCDCE))
|
||||
color: const Color(0xFFFFF1F1),
|
||||
borderRadius: BorderRadius.circular(40.r),
|
||||
border: Border.all(color: const Color(0xffFDCDCE))
|
||||
),
|
||||
child: Column(
|
||||
|
||||
children: [
|
||||
// ===== Left Section =====
|
||||
Row(
|
||||
@@ -27,7 +27,7 @@ class GetYourPassCard extends StatelessWidget {
|
||||
Text(
|
||||
"Get your Pass",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 18,
|
||||
fontSize: 18.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black,
|
||||
),
|
||||
@@ -37,37 +37,38 @@ class GetYourPassCard extends StatelessWidget {
|
||||
Navigator.of(context).pushNamed(RouteConstants.buyPass);
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
padding: EdgeInsets.all(8.r),
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xffF95F62),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
child: Icon(
|
||||
Icons.arrow_forward,
|
||||
color: Colors.black,
|
||||
size: 18,
|
||||
size: 18.sp,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(
|
||||
height: 7,
|
||||
),
|
||||
SizedBox(height: 7.h),
|
||||
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
AttractionsAvatarStack(imagePath: 'assets/images/get_your_pass_bg.jpg',),
|
||||
const SizedBox(width: 8),
|
||||
AttractionsAvatarStack(
|
||||
imagePath: 'assets/images/get_your_pass_bg.jpg',
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Text(
|
||||
"Attractions",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 13,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w400
|
||||
fontSize: 13.sp,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w400
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -78,7 +79,7 @@ class GetYourPassCard extends StatelessWidget {
|
||||
Text(
|
||||
"From",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontSize: 12.sp,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
@@ -88,7 +89,7 @@ class GetYourPassCard extends StatelessWidget {
|
||||
TextSpan(
|
||||
text: "\$20",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.black,
|
||||
),
|
||||
@@ -96,7 +97,7 @@ class GetYourPassCard extends StatelessWidget {
|
||||
TextSpan(
|
||||
text: " /Adult",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 13,
|
||||
fontSize: 13.sp,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
@@ -116,10 +117,10 @@ class GetYourPassCard extends StatelessWidget {
|
||||
class AttractionsAvatarStack extends StatelessWidget {
|
||||
const AttractionsAvatarStack({
|
||||
super.key,
|
||||
required this.imagePath, // from your assets/figma
|
||||
this.size = 35, // circle diameter
|
||||
this.count = 4, // total circles including the last “16+”
|
||||
this.overlap = 8, // how much they overlap
|
||||
required this.imagePath,
|
||||
this.size = 35,
|
||||
this.count = 4,
|
||||
this.overlap = 8,
|
||||
this.moreText = '16+',
|
||||
});
|
||||
|
||||
@@ -131,10 +132,13 @@ class AttractionsAvatarStack extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final step = size - overlap; // horizontal step between circles
|
||||
final responsiveSize = size.r;
|
||||
final responsiveOverlap = overlap.r;
|
||||
final step = responsiveSize - responsiveOverlap;
|
||||
|
||||
return SizedBox(
|
||||
width: size + (count - 1) * step,
|
||||
height: size,
|
||||
width: responsiveSize + (count - 1) * step,
|
||||
height: responsiveSize,
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: List.generate(count, (i) {
|
||||
@@ -144,7 +148,7 @@ class AttractionsAvatarStack extends StatelessWidget {
|
||||
return Positioned(
|
||||
left: left,
|
||||
child: _AvatarCircle(
|
||||
size: size,
|
||||
size: responsiveSize,
|
||||
imagePath: imagePath,
|
||||
showOverlayText: isLast,
|
||||
overlayText: moreText,
|
||||
@@ -174,10 +178,10 @@ class _AvatarCircle extends StatelessWidget {
|
||||
return Container(
|
||||
width: size,
|
||||
height: size,
|
||||
padding: const EdgeInsets.all(1.5), // white ring thickness
|
||||
padding: EdgeInsets.all(1.5.r),
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.white, // ring color
|
||||
color: Colors.white,
|
||||
),
|
||||
child: ClipOval(
|
||||
child: Stack(
|
||||
@@ -190,9 +194,9 @@ class _AvatarCircle extends StatelessWidget {
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
overlayText,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 11,
|
||||
fontSize: 11.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
@@ -203,4 +207,3 @@ class _AvatarCircle extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
import '../../common_packages/common_app_texts.dart';
|
||||
import '../../core/route_constants.dart';
|
||||
|
||||
class ChooseYourPassSection extends StatefulWidget {
|
||||
@@ -20,7 +21,7 @@ class _ChooseYourPassSectionState extends State<ChooseYourPassSection> {
|
||||
|
||||
final List<Map<String, dynamic>> passes = [
|
||||
{
|
||||
"title": "Chicago-\nFLEXI CARD",
|
||||
"title": "Chicago-\n${CommonAppText.selectiveCard} CARD",
|
||||
"price": "\$50",
|
||||
"color": const Color(0xffF95FAF),
|
||||
"bgColor": const Color(0xFFFDE7F1),
|
||||
|
||||
@@ -19,156 +19,158 @@ class IntroScreensView extends StatelessWidget {
|
||||
return BlocProvider(
|
||||
create: (_) => IntroScreensCubit(),
|
||||
child: Scaffold(
|
||||
body: BlocBuilder<IntroScreensCubit, IntroScreensState>(
|
||||
builder: (context, state) {
|
||||
return Stack(
|
||||
children: [
|
||||
// Background PageView
|
||||
PageView.builder(
|
||||
controller: _pageController,
|
||||
itemCount: pages.length,
|
||||
onPageChanged: (index) =>
|
||||
context.read<IntroScreensCubit>().updatePage(index),
|
||||
itemBuilder: (context, index) {
|
||||
final page = pages[index];
|
||||
return Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Image.asset(page.image, fit: BoxFit.cover),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [Color(0xFF00000000), Color(0xFF000000)],
|
||||
body: SafeArea(
|
||||
child: BlocBuilder<IntroScreensCubit, IntroScreensState>(
|
||||
builder: (context, state) {
|
||||
return Stack(
|
||||
children: [
|
||||
// Background PageView
|
||||
PageView.builder(
|
||||
controller: _pageController,
|
||||
itemCount: pages.length,
|
||||
onPageChanged: (index) =>
|
||||
context.read<IntroScreensCubit>().updatePage(index),
|
||||
itemBuilder: (context, index) {
|
||||
final page = pages[index];
|
||||
return Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Image.asset(page.image, fit: BoxFit.cover),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [Color(0xFF00000000), Color(0xFF000000)],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
// Skip Button (Only first 2 pages)
|
||||
if (state.currentPage < pages.length - 1)
|
||||
Positioned(
|
||||
top: 50,
|
||||
right: 20,
|
||||
child: GestureDetector(
|
||||
onTap: (){
|
||||
Navigator.pushReplacementNamed(context,RouteConstants.home);
|
||||
},
|
||||
child: Container(
|
||||
height: 48.h,
|
||||
width: 92.w,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16.r),
|
||||
border: Border.all(color: Colors.white),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Skip',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
// Skip Button (Only first 2 pages)
|
||||
if (state.currentPage < pages.length - 1)
|
||||
Positioned(
|
||||
top: 50,
|
||||
right: 20,
|
||||
child: GestureDetector(
|
||||
onTap: (){
|
||||
Navigator.pushReplacementNamed(context,RouteConstants.home);
|
||||
},
|
||||
child: Container(
|
||||
height: 48.h,
|
||||
width: 92.w,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16.r),
|
||||
border: Border.all(color: Colors.white),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Skip',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Bottom Content
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: GlassMorphismContainer(
|
||||
blurIntensity: 0.5,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 24.w,
|
||||
vertical: 17.h,
|
||||
),
|
||||
margin: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
pages[state.currentPage].title,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 24.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
Text(
|
||||
pages[state.currentPage].description,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14.sp,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 24.h),
|
||||
|
||||
// Dots Indicator
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: List.generate(
|
||||
pages.length,
|
||||
(index) => Container(
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: 4.0,
|
||||
),
|
||||
width: 36.w,
|
||||
height: 12.h,
|
||||
decoration: BoxDecoration(
|
||||
color: state.currentPage == index
|
||||
? Color(0xFFF95F62)
|
||||
: Color(0xFFF95F62).withOpacity(0.42),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 24.h),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Color(0xFFF95F62),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16.r),
|
||||
),
|
||||
minimumSize: const Size(double.infinity, 52),
|
||||
),
|
||||
onPressed: () {
|
||||
if (state.currentPage == pages.length - 1) {
|
||||
Navigator.pushReplacementNamed(context, '/home');
|
||||
} else {
|
||||
_pageController.nextPage(
|
||||
duration: const Duration(milliseconds: 400),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
state.currentPage == pages.length - 1
|
||||
? "Let's Get Started"
|
||||
: 'Continue',
|
||||
|
||||
// Bottom Content
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: GlassMorphismContainer(
|
||||
blurIntensity: 0.5,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 24.w,
|
||||
vertical: 17.h,
|
||||
),
|
||||
margin: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
pages[state.currentPage].title,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 20.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 24.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
Text(
|
||||
pages[state.currentPage].description,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14.sp,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 24.h),
|
||||
|
||||
// Dots Indicator
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: List.generate(
|
||||
pages.length,
|
||||
(index) => Container(
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: 4.0,
|
||||
),
|
||||
width: 36.w,
|
||||
height: 12.h,
|
||||
decoration: BoxDecoration(
|
||||
color: state.currentPage == index
|
||||
? Color(0xFFF95F62)
|
||||
: Color(0xFFF95F62).withOpacity(0.42),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
SizedBox(height: 24.h),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Color(0xFFF95F62),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16.r),
|
||||
),
|
||||
minimumSize: const Size(double.infinity, 52),
|
||||
),
|
||||
onPressed: () {
|
||||
if (state.currentPage == pages.length - 1) {
|
||||
Navigator.pushReplacementNamed(context, '/home');
|
||||
} else {
|
||||
_pageController.nextPage(
|
||||
duration: const Duration(milliseconds: 400),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
state.currentPage == pages.length - 1
|
||||
? "Let's Get Started"
|
||||
: 'Continue',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 20.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -36,28 +36,29 @@ class _DietarySelectionViewState extends State<DietarySelectionView> {
|
||||
Text(
|
||||
"👋 Hello! We'd love to know more about you. Do you follow any dietary preferences?",
|
||||
style: TextStyle(
|
||||
color: Color(0xFF101828),
|
||||
fontSize: 24.sp,
|
||||
color: const Color(0xFF101828),
|
||||
fontSize: 20.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 12.h),
|
||||
SizedBox(height: 10.h),
|
||||
CustomText(
|
||||
text: "Select all that apply",
|
||||
size: 14.sp,
|
||||
size: 12.sp,
|
||||
color: const Color(0xFF6A7282),
|
||||
),
|
||||
SizedBox(height: 38.h),
|
||||
SizedBox(height: 32.h),
|
||||
SizedBox(
|
||||
height: 350.h,
|
||||
height: 320.h,
|
||||
child: BlocBuilder<AddItineraryDetailBloc, ItineraryDetailState>(
|
||||
builder: (context, sate) {
|
||||
return GridView.builder(
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
padding: EdgeInsets.zero,
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
mainAxisSpacing: 12,
|
||||
crossAxisSpacing: 16,
|
||||
mainAxisSpacing: 10.h,
|
||||
crossAxisSpacing: 14.w,
|
||||
crossAxisCount: 2,
|
||||
childAspectRatio: 1.7,
|
||||
),
|
||||
@@ -72,12 +73,15 @@ class _DietarySelectionViewState extends State<DietarySelectionView> {
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
width: 168.w,
|
||||
width: 150.w,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
borderRadius: BorderRadius.circular(20.r),
|
||||
border: isSelected
|
||||
? Border.all(color: Color(0xFFF95F62))
|
||||
? Border.all(
|
||||
color: const Color(0xFFF95F62),
|
||||
width: 1.5.w,
|
||||
)
|
||||
: Border.all(color: Colors.transparent),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
@@ -85,11 +89,15 @@ class _DietarySelectionViewState extends State<DietarySelectionView> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Image.asset(item["icon"] ?? "", scale: 4),
|
||||
SizedBox(height: 8),
|
||||
Image.asset(
|
||||
item["icon"] ?? "",
|
||||
width: 40.w,
|
||||
height: 40.h,
|
||||
),
|
||||
SizedBox(height: 6.h),
|
||||
CustomText(
|
||||
text: item["name"] ?? "",
|
||||
size: 16.sp,
|
||||
size: 14.sp,
|
||||
weight: FontWeight.w500,
|
||||
color: const Color(0xFF364153),
|
||||
),
|
||||
@@ -103,7 +111,7 @@ class _DietarySelectionViewState extends State<DietarySelectionView> {
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 41.h),
|
||||
SizedBox(height: 36.h),
|
||||
CustomFilledButton(
|
||||
onTap: () {
|
||||
context.read<ItineraryStepNavigationBloc>().add(
|
||||
|
||||
@@ -6,7 +6,6 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'attraction_details/bloc/attraction_details_bloc.dart';
|
||||
import 'core/app_router.dart';
|
||||
import 'my_pass/blocs/my_pass_bloc.dart';
|
||||
|
||||
@@ -39,9 +38,6 @@ class MyApp extends StatelessWidget {
|
||||
BlocProvider<MyPassBloc>(
|
||||
create: (_) => MyPassBloc()..add(LoadMyPasses()),
|
||||
),
|
||||
BlocProvider<AttractionDetailsBloc>(
|
||||
create: (_) => AttractionDetailsBloc(),
|
||||
),
|
||||
],
|
||||
child: MaterialApp(
|
||||
onGenerateRoute: _appRouter.onGenerateRoute,
|
||||
|
||||
6
lib/networkApiServices/api_urls.dart
Normal file
6
lib/networkApiServices/api_urls.dart
Normal file
@@ -0,0 +1,6 @@
|
||||
class ApiUrls {
|
||||
|
||||
static const baseUrl = "https://devapi.citycards.betadelivery.com";
|
||||
|
||||
static const cityList = "$baseUrl/mobile/city_list";
|
||||
}
|
||||
192
lib/networkApiServices/network_api_services.dart
Normal file
192
lib/networkApiServices/network_api_services.dart
Normal file
@@ -0,0 +1,192 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class NetworkApiService {
|
||||
static final NetworkApiService _instance = NetworkApiService._internal();
|
||||
late Dio _dio;
|
||||
|
||||
factory NetworkApiService() {
|
||||
return _instance;
|
||||
}
|
||||
|
||||
NetworkApiService._internal() {
|
||||
_dio = Dio(
|
||||
BaseOptions(
|
||||
connectTimeout: const Duration(seconds: 30),
|
||||
receiveTimeout: const Duration(seconds: 30),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Add interceptors
|
||||
_dio.interceptors.add(
|
||||
InterceptorsWrapper(
|
||||
onRequest: (options, handler) {
|
||||
// Add token if available
|
||||
// String? token = "use token here from local shared preference";
|
||||
// if (token != null) {
|
||||
// options.headers['Authorization'] = 'Bearer $token';
|
||||
// }
|
||||
|
||||
if (kDebugMode) {
|
||||
print('REQUEST[${options.method}] => URL: ${options.uri}');
|
||||
}
|
||||
return handler.next(options);
|
||||
},
|
||||
onResponse: (response, handler) {
|
||||
if (kDebugMode) {
|
||||
print('RESPONSE[${response.statusCode}] => DATA: ${response.data}');
|
||||
}
|
||||
return handler.next(response);
|
||||
},
|
||||
onError: (error, handler) {
|
||||
if (kDebugMode) {
|
||||
print('ERROR[${error.response?.statusCode}] => MESSAGE: ${error.message}');
|
||||
}
|
||||
return handler.next(error);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Add logging interceptor in debug mode
|
||||
if (kDebugMode) {
|
||||
_dio.interceptors.add(LogInterceptor(
|
||||
request: true,
|
||||
requestHeader: true,
|
||||
requestBody: true,
|
||||
responseHeader: false,
|
||||
responseBody: true,
|
||||
error: true,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// GET API Request
|
||||
Future<Response> getApi({
|
||||
required String url,
|
||||
Map<String, dynamic>? queryParameters,
|
||||
Options? options,
|
||||
CancelToken? cancelToken,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _dio.get(
|
||||
url,
|
||||
queryParameters: queryParameters,
|
||||
options: options,
|
||||
cancelToken: cancelToken,
|
||||
);
|
||||
return response;
|
||||
} on DioException catch (e) {
|
||||
throw _handleError(e);
|
||||
}
|
||||
}
|
||||
|
||||
// POST API Request
|
||||
Future<Response> postApi({
|
||||
required String url,
|
||||
dynamic data,
|
||||
Map<String, dynamic>? queryParameters,
|
||||
Options? options,
|
||||
CancelToken? cancelToken,
|
||||
ProgressCallback? onSendProgress,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _dio.post(
|
||||
url,
|
||||
data: data,
|
||||
queryParameters: queryParameters,
|
||||
options: options,
|
||||
cancelToken: cancelToken,
|
||||
onSendProgress: onSendProgress,
|
||||
);
|
||||
return response;
|
||||
} on DioException catch (e) {
|
||||
throw _handleError(e);
|
||||
}
|
||||
}
|
||||
|
||||
// PUT API Request (Bonus)
|
||||
Future<Response> putApi({
|
||||
required String url,
|
||||
dynamic data,
|
||||
Map<String, dynamic>? queryParameters,
|
||||
Options? options,
|
||||
CancelToken? cancelToken,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _dio.put(
|
||||
url,
|
||||
data: data,
|
||||
queryParameters: queryParameters,
|
||||
options: options,
|
||||
cancelToken: cancelToken,
|
||||
);
|
||||
return response;
|
||||
} on DioException catch (e) {
|
||||
throw _handleError(e);
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE API Request (Bonus)
|
||||
Future<Response> deleteApi({
|
||||
required String url,
|
||||
dynamic data,
|
||||
Map<String, dynamic>? queryParameters,
|
||||
Options? options,
|
||||
CancelToken? cancelToken,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _dio.delete(
|
||||
url,
|
||||
data: data,
|
||||
queryParameters: queryParameters,
|
||||
options: options,
|
||||
cancelToken: cancelToken,
|
||||
);
|
||||
return response;
|
||||
} on DioException catch (e) {
|
||||
throw _handleError(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Error Handler
|
||||
String _handleError(DioException error) {
|
||||
String errorDescription = "";
|
||||
switch (error.type) {
|
||||
case DioExceptionType.connectionTimeout:
|
||||
errorDescription = "Connection timeout. Please try again.";
|
||||
break;
|
||||
case DioExceptionType.sendTimeout:
|
||||
errorDescription = "Send timeout. Please try again.";
|
||||
break;
|
||||
case DioExceptionType.receiveTimeout:
|
||||
errorDescription = "Receive timeout. Please try again.";
|
||||
break;
|
||||
case DioExceptionType.badCertificate:
|
||||
errorDescription = "Bad certificate.";
|
||||
break;
|
||||
case DioExceptionType.badResponse:
|
||||
errorDescription = error.response?.data['message'] ??
|
||||
"Received invalid status code: ${error.response?.statusCode}";
|
||||
break;
|
||||
case DioExceptionType.cancel:
|
||||
errorDescription = "Request was cancelled.";
|
||||
break;
|
||||
case DioExceptionType.connectionError:
|
||||
errorDescription = "No internet connection.";
|
||||
break;
|
||||
case DioExceptionType.unknown:
|
||||
errorDescription = "Something went wrong. Please try again.";
|
||||
break;
|
||||
}
|
||||
return errorDescription;
|
||||
}
|
||||
|
||||
// Update headers (e.g., add token)
|
||||
void updateHeaders(Map<String, dynamic> headers) {
|
||||
_dio.options.headers.addAll(headers);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
import 'package:citycards_customer/attraction_details/widgets/share_bottomsheet.dart';
|
||||
import 'package:citycards_customer/attraction_details/share_bottomsheet.dart';
|
||||
import 'package:citycards_customer/common_packages/app_bar.dart';
|
||||
import 'package:citycards_customer/common_packages/custom_bullet_points.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
class OfferPassDetailView extends StatelessWidget {
|
||||
const OfferPassDetailView({super.key});
|
||||
@@ -92,16 +91,12 @@ class OfferPassDetailView extends StatelessWidget {
|
||||
right: 17.w,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Share.share(
|
||||
'Check out my City Card app!',
|
||||
subject: 'City Card App',
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (context) => const ShareBottomSheet(),
|
||||
);
|
||||
// showModalBottomSheet(
|
||||
// context: context,
|
||||
// isScrollControlled: true,
|
||||
// backgroundColor: Colors.transparent,
|
||||
// builder: (context) => const ShareBottomSheet(),
|
||||
// );
|
||||
},
|
||||
child: Container(
|
||||
height: 36.h,
|
||||
|
||||
@@ -18,44 +18,46 @@ class PostcardCreationPage extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<PostcardCreationBloc, PostcardCreationState>(
|
||||
builder: (context, state) {
|
||||
Widget stepWidget;
|
||||
switch (state.currentStep) {
|
||||
case PostcardStep.uploadPhoto:
|
||||
stepWidget = const UploadPhotoStepPageView();
|
||||
break;
|
||||
case PostcardStep.addFilter:
|
||||
stepWidget = const AddFilterStepPageView();
|
||||
break;
|
||||
case PostcardStep.writeMessage:
|
||||
stepWidget = const WriteMessageStepPageView();
|
||||
break;
|
||||
case PostcardStep.preview:
|
||||
stepWidget = const PreviewPostcardStepPageView();
|
||||
break;
|
||||
case PostcardStep.purchase:
|
||||
stepWidget = const PostcardPurchaseFormPageView();
|
||||
break;
|
||||
case PostcardStep.checkout:
|
||||
stepWidget = const PostcardCheckoutPageView();
|
||||
break;
|
||||
case PostcardStep.orderSuccess:
|
||||
stepWidget = const OrderSuccessPageView();
|
||||
break;
|
||||
case PostcardStep.myOrders:
|
||||
stepWidget = const MyOrdersPageView();
|
||||
break;
|
||||
case PostcardStep.myOrderPostcardPreview:
|
||||
stepWidget = const OrderPostcardPreviewPageView();
|
||||
break;
|
||||
}
|
||||
return BlocProvider(
|
||||
create: (_) => PostcardCreationBloc(),
|
||||
child: BlocBuilder<PostcardCreationBloc, PostcardCreationState>(
|
||||
builder: (context, state) {
|
||||
Widget stepWidget;
|
||||
switch (state.currentStep) {
|
||||
case PostcardStep.uploadPhoto:
|
||||
stepWidget = const UploadPhotoStepPageView();
|
||||
break;
|
||||
case PostcardStep.addFilter:
|
||||
stepWidget = const AddFilterStepPageView();
|
||||
break;
|
||||
case PostcardStep.writeMessage:
|
||||
stepWidget = const WriteMessageStepPageView();
|
||||
break;
|
||||
case PostcardStep.preview:
|
||||
stepWidget = const PreviewPostcardStepPageView();
|
||||
break;
|
||||
case PostcardStep.purchase:
|
||||
stepWidget = const PostcardPurchaseFormPageView();
|
||||
break;
|
||||
case PostcardStep.checkout:
|
||||
stepWidget = const PostcardCheckoutPageView();
|
||||
break;
|
||||
case PostcardStep.orderSuccess:
|
||||
stepWidget = const OrderSuccessPageView();
|
||||
break;
|
||||
case PostcardStep.myOrders:
|
||||
stepWidget = const MyOrdersPageView();
|
||||
break;
|
||||
case PostcardStep.myOrderPostcardPreview:
|
||||
stepWidget = const OrderPostcardPreviewPageView();
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
body: SafeArea(child: stepWidget),
|
||||
);
|
||||
},
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
body: SafeArea(child: stepWidget),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ class PostcardPage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(RouteConstants.postCardCreationPage);
|
||||
Navigator.of(context).pushNamed(RouteConstants.uploadPhotoPage);
|
||||
},
|
||||
child: Text(
|
||||
"Lets Create",
|
||||
|
||||
@@ -8,71 +8,13 @@ import '../blocs/postcard_creation_bloc.dart';
|
||||
import '../blocs/postcard_creation_events.dart';
|
||||
import '../blocs/postcard_creation_state.dart';
|
||||
|
||||
class PostcardPurchaseFormPageView extends StatefulWidget {
|
||||
class PostcardPurchaseFormPageView extends StatelessWidget {
|
||||
const PostcardPurchaseFormPageView({super.key});
|
||||
|
||||
@override
|
||||
State<PostcardPurchaseFormPageView> createState() =>
|
||||
_PostcardPurchaseFormPageViewState();
|
||||
}
|
||||
|
||||
class _PostcardPurchaseFormPageViewState
|
||||
extends State<PostcardPurchaseFormPageView> {
|
||||
|
||||
/// Controllers
|
||||
final titleCtrl = TextEditingController();
|
||||
final fullNameCtrl = TextEditingController();
|
||||
final emailCtrl = TextEditingController();
|
||||
final phoneCtrl = TextEditingController();
|
||||
final cityCtrl = TextEditingController();
|
||||
final zipCtrl = TextEditingController();
|
||||
|
||||
String? country;
|
||||
String? stateName;
|
||||
|
||||
/// Error messages
|
||||
String? titleError;
|
||||
String? fullNameError;
|
||||
String? emailError;
|
||||
String? phoneError;
|
||||
String? cityError;
|
||||
String? countryError;
|
||||
String? stateError;
|
||||
String? zipError;
|
||||
|
||||
bool validate() {
|
||||
final emailRegex = RegExp(r'^[\w\.-]+@[\w\.-]+\.\w+$');
|
||||
|
||||
setState(() {
|
||||
titleError = titleCtrl.text.isEmpty ? "Required" : null;
|
||||
fullNameError = fullNameCtrl.text.isEmpty ? "Required" : null;
|
||||
emailError = !emailRegex.hasMatch(emailCtrl.text.trim())
|
||||
? "Invalid email format"
|
||||
: null;
|
||||
|
||||
phoneError = phoneCtrl.text.length < 10 ? "Invalid phone" : null;
|
||||
cityError = cityCtrl.text.isEmpty ? "Required" : null;
|
||||
countryError = country == null ? "Required" : null;
|
||||
stateError = stateName == null ? "Required" : null;
|
||||
zipError = zipCtrl.text.length < 4 ? "Invalid zip" : null;
|
||||
});
|
||||
|
||||
return titleError == null &&
|
||||
fullNameError == null &&
|
||||
emailError == null &&
|
||||
phoneError == null &&
|
||||
cityError == null &&
|
||||
countryError == null &&
|
||||
stateError == null &&
|
||||
zipError == null;
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<PostcardCreationBloc, PostcardCreationState>(
|
||||
builder: (context, state) {
|
||||
|
||||
final bloc = context.read<PostcardCreationBloc>();
|
||||
|
||||
return SafeArea(
|
||||
@@ -81,7 +23,8 @@ class _PostcardPurchaseFormPageViewState
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CommonAppBar(isWhiteLogo: false, isProfilePage: false, showDivider: true),
|
||||
CommonAppBar(isWhiteLogo: false, isProfilePage: false, showDivider: true,),
|
||||
|
||||
// Order ID
|
||||
Text(
|
||||
"#78895436",
|
||||
@@ -93,7 +36,7 @@ class _PostcardPurchaseFormPageViewState
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Title
|
||||
// Postcard image + title
|
||||
Row(
|
||||
children: [
|
||||
ClipRRect(
|
||||
@@ -115,38 +58,24 @@ class _PostcardPurchaseFormPageViewState
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextField(
|
||||
controller: titleCtrl,
|
||||
onChanged: (_) {
|
||||
setState(() => titleError = null);
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
hintText: "Add title",
|
||||
hintStyle: GoogleFonts.poppins(
|
||||
color: const Color(0xff999999),
|
||||
fontSize: 14.sp),
|
||||
enabledBorder: const UnderlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: Color(0xffFDCDCE), width: 1),
|
||||
),
|
||||
focusedBorder: const UnderlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: Color(0xffFDCDCE), width: 1),
|
||||
),
|
||||
),
|
||||
style: GoogleFonts.poppins(fontSize: 14.sp),
|
||||
child: TextField(
|
||||
decoration: InputDecoration(
|
||||
hintText: "Add title",
|
||||
hintStyle: GoogleFonts.poppins(
|
||||
color: const Color(0xff999999), fontSize: 14.sp),
|
||||
enabledBorder: const UnderlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: Color(0xffFDCDCE), width: 1),
|
||||
),
|
||||
if (titleError != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: Text(titleError!,
|
||||
style: const TextStyle(
|
||||
color: Colors.red, fontSize: 12)),
|
||||
),
|
||||
],
|
||||
focusedBorder: const UnderlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: Color(0xffFDCDCE), width: 1),
|
||||
),
|
||||
),
|
||||
style: GoogleFonts.poppins(fontSize: 14.sp),
|
||||
onChanged: (val) {
|
||||
// You can dispatch event here: bloc.add(UpdateTitle(val));
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -154,7 +83,7 @@ class _PostcardPurchaseFormPageViewState
|
||||
|
||||
const SizedBox(height: 28),
|
||||
|
||||
// Personal details
|
||||
// Personal details section
|
||||
Text(
|
||||
"Add personal details",
|
||||
style: TextStyle(
|
||||
@@ -168,45 +97,21 @@ class _PostcardPurchaseFormPageViewState
|
||||
_buildInputField(
|
||||
label: "Full Name",
|
||||
hint: "Lorem Ipsum",
|
||||
controller: fullNameCtrl,
|
||||
errorText: fullNameError,
|
||||
onChanged: (_) {
|
||||
setState(() => fullNameError = null);
|
||||
},
|
||||
),
|
||||
|
||||
_buildInputField(
|
||||
label: "Email ID",
|
||||
hint: "Lorem@gmail.com",
|
||||
icon: Icons.email_outlined,
|
||||
controller: emailCtrl,
|
||||
errorText: emailError,
|
||||
type: TextInputType.emailAddress,
|
||||
onChanged: (_) {
|
||||
setState(() => emailError = null);
|
||||
},
|
||||
),
|
||||
|
||||
_buildInputField(
|
||||
label: "Phone number",
|
||||
hint: "+91 9999 999 999",
|
||||
icon: Icons.phone_outlined,
|
||||
controller: phoneCtrl,
|
||||
errorText: phoneError,
|
||||
onChanged: (value) {
|
||||
if (value.length > 10) {
|
||||
phoneCtrl.text = value.substring(0, 10);
|
||||
phoneCtrl.selection = TextSelection.fromPosition(
|
||||
TextPosition(offset: phoneCtrl.text.length),
|
||||
);
|
||||
}
|
||||
setState(() => phoneError = null);
|
||||
},
|
||||
type: TextInputType.number
|
||||
),
|
||||
|
||||
const SizedBox(height: 28),
|
||||
|
||||
// Address details section
|
||||
Text(
|
||||
"Add address details",
|
||||
style: TextStyle(
|
||||
@@ -217,51 +122,10 @@ class _PostcardPurchaseFormPageViewState
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
_buildInputField(
|
||||
label: "City",
|
||||
hint: "Lorem Ipsum",
|
||||
controller: cityCtrl,
|
||||
errorText: cityError,
|
||||
onChanged: (_) {
|
||||
setState(() => cityError = null);
|
||||
},
|
||||
),
|
||||
|
||||
_buildDropdownField(
|
||||
label: "Country",
|
||||
hint: "Lorem Ipsum",
|
||||
value: country,
|
||||
errorText: countryError,
|
||||
onChanged: (val) {
|
||||
setState(() {
|
||||
country = val;
|
||||
countryError = null;
|
||||
});
|
||||
},
|
||||
),
|
||||
|
||||
_buildDropdownField(
|
||||
label: "State",
|
||||
hint: "Lorem Ipsum",
|
||||
value: stateName,
|
||||
errorText: stateError,
|
||||
onChanged: (val) {
|
||||
setState(() {
|
||||
stateName = val;
|
||||
stateError = null;
|
||||
});
|
||||
},
|
||||
),
|
||||
|
||||
_buildInputField(
|
||||
label: "Zip Code",
|
||||
hint: "000000",
|
||||
controller: zipCtrl,
|
||||
errorText: zipError,
|
||||
onChanged: (_) {
|
||||
setState(() => zipError = null);
|
||||
},
|
||||
),
|
||||
_buildInputField(label: "City", hint: "Lorem Ipsum"),
|
||||
_buildDropdownField(label: "Country", hint: "Lorem Ipsum"),
|
||||
_buildDropdownField(label: "State", hint: "Lorem Ipsum"),
|
||||
_buildInputField(label: "Zip Code", hint: "000000"),
|
||||
|
||||
const SizedBox(height: 30),
|
||||
|
||||
@@ -270,9 +134,7 @@ class _PostcardPurchaseFormPageViewState
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
if (validate()) {
|
||||
bloc.add(GoToNextStep());
|
||||
}
|
||||
bloc.add(GoToNextStep());
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xffF95F62),
|
||||
@@ -299,39 +161,36 @@ class _PostcardPurchaseFormPageViewState
|
||||
);
|
||||
}
|
||||
|
||||
/// TEXT FIELD (NO UI CHANGES)
|
||||
/// 🔹 Reusable text field widget
|
||||
Widget _buildInputField({
|
||||
required String label,
|
||||
required String hint,
|
||||
required TextEditingController controller,
|
||||
required Function(String) onChanged,
|
||||
String? errorText,
|
||||
IconData? icon,
|
||||
TextInputType? type
|
||||
}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 18),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(label,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 13.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: const Color(0xff1A1A1A))),
|
||||
Text(
|
||||
label,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 13.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: const Color(0xff1A1A1A),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
TextField(
|
||||
controller: controller,
|
||||
onChanged: onChanged,
|
||||
keyboardType: type,
|
||||
decoration: InputDecoration(
|
||||
hintText: hint,
|
||||
hintStyle: GoogleFonts.poppins(
|
||||
color: const Color(0xff999999),
|
||||
fontSize: 14.sp,
|
||||
),
|
||||
suffixIcon:
|
||||
icon != null ? Icon(icon, color: Colors.black, size: 20) : null,
|
||||
suffixIcon: icon != null
|
||||
? Icon(icon, color: Colors.black, size: 20)
|
||||
: null,
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(vertical: 14, horizontal: 12),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
@@ -344,38 +203,32 @@ class _PostcardPurchaseFormPageViewState
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorText != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: Text(errorText!,
|
||||
style: const TextStyle(color: Colors.red, fontSize: 12)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// DROPDOWN FIELD (NO UI CHANGES)
|
||||
/// 🔹 Dropdown input
|
||||
Widget _buildDropdownField({
|
||||
required String label,
|
||||
required String hint,
|
||||
required String? value,
|
||||
required Function(String?) onChanged,
|
||||
String? errorText,
|
||||
}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 18),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(label,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 13.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: const Color(0xff1A1A1A))),
|
||||
Text(
|
||||
label,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 13.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: const Color(0xff1A1A1A),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
DropdownButtonFormField<String>(
|
||||
value: value,
|
||||
value: null,
|
||||
decoration: InputDecoration(
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(vertical: 14, horizontal: 12),
|
||||
@@ -400,14 +253,8 @@ class _PostcardPurchaseFormPageViewState
|
||||
items: const [
|
||||
DropdownMenuItem(value: "Lorem Ipsum", child: Text("Lorem Ipsum")),
|
||||
],
|
||||
onChanged: onChanged,
|
||||
onChanged: (val) {},
|
||||
),
|
||||
if (errorText != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: Text(errorText!,
|
||||
style: const TextStyle(color: Colors.red, fontSize: 12)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -5,171 +5,36 @@ import 'package:google_fonts/google_fonts.dart';
|
||||
class MessageCardWidget extends StatelessWidget {
|
||||
final String message;
|
||||
final String? selectedFont;
|
||||
MessageCardWidget({super.key, required this.message, this.selectedFont});
|
||||
const MessageCardWidget({super.key, required this.message, this.selectedFont});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
constraints: BoxConstraints(minHeight: 227.h),
|
||||
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8.h),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5.r),
|
||||
gradient: const LinearGradient(
|
||||
colors: [
|
||||
Color(0xFFF5E9D7), // top-left shade
|
||||
Color(0xFFE7D3B8), // bottom-right shade
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
return Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/images/postcard_bg.png',
|
||||
width: double.infinity,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Color(0xFFA08264).withOpacity(0.15),
|
||||
blurRadius: 6,
|
||||
spreadRadius: 0,
|
||||
offset: Offset(-0.5, -0.5),
|
||||
|
||||
Positioned(
|
||||
right: 10,
|
||||
top: 50,
|
||||
child: SizedBox(
|
||||
width: 150.w,
|
||||
child: Text(message,
|
||||
textAlign: TextAlign.left,
|
||||
style: TextStyle(
|
||||
fontFamily: selectedFont ??
|
||||
GoogleFonts.poppins().fontFamily,
|
||||
color: Colors.black,
|
||||
fontSize: 10,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
foregroundDecoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5.r),
|
||||
gradient: RadialGradient(
|
||||
center: const Alignment(-0.6, -0.6),
|
||||
radius: 1.2,
|
||||
colors: [Color(0xFFBEA078).withOpacity(0.15), Colors.transparent],
|
||||
),
|
||||
),
|
||||
|
||||
// ---------- IMPORTANT: IntrinsicHeight ensures children get bounded height ----------
|
||||
child: IntrinsicHeight(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch, // stretch so children fill height
|
||||
children: [
|
||||
// ---------------- LEFT SECTION ---------------- //
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// SpaceBetween ensures top content stays top and CityCards.co stays bottom
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
// Top group
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Image.asset(
|
||||
"assets/logo/logo_city_cards.png",
|
||||
height: 22.h,
|
||||
),
|
||||
SizedBox(height: 4.h),
|
||||
|
||||
Text(
|
||||
"POSTCARD",
|
||||
style: TextStyle(
|
||||
fontSize: 6.sp,
|
||||
letterSpacing: 0.93,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF000000).withOpacity(0.4),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 7.h),
|
||||
|
||||
Text(
|
||||
"MESSAGE PREVIEW",
|
||||
style: TextStyle(
|
||||
fontSize: 5.sp,
|
||||
letterSpacing: 0.93,
|
||||
color: Color(0xFF000000).withOpacity(0.6),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 4.h),
|
||||
|
||||
Text(
|
||||
message,
|
||||
style: TextStyle(
|
||||
fontFamily: selectedFont,
|
||||
fontSize: 12.sp,
|
||||
height: 1.6,
|
||||
color: Color(0xFF0A0D13).withOpacity(0.8),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// Bottom text — will stay at the bottom of the left column
|
||||
Text(
|
||||
"CityCards.co",
|
||||
style: TextStyle(fontSize: 12.sp, color: Color(0xFFF95F62)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// ---------------- DIVIDER (middle) ---------------- //
|
||||
Container(
|
||||
width: 3.86.w,
|
||||
height: double.infinity, // will match IntrinsicHeight-bounded Row height
|
||||
margin: EdgeInsets.only(right: 10.w,top:24.h,bottom: 24.h),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Color(0xFF000000).withOpacity(0),
|
||||
Color(0xFF65543F).withOpacity(0.4),
|
||||
Color(0xFF65543F).withOpacity(0.6),
|
||||
Color(0xFF65543F).withOpacity(0.8),
|
||||
Color(0xFF65543F).withOpacity(0.6),
|
||||
Color(0xFF65543F).withOpacity(0.4),
|
||||
Color(0xFF000000).withOpacity(0),
|
||||
],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// ---------------- RIGHT SECTION ---------------- //
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(7),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(4.r),
|
||||
border: Border.all(
|
||||
color: Color(0xFF000000).withOpacity(0.12),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"ADDRESS",
|
||||
style: TextStyle(
|
||||
fontSize: 5.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
letterSpacing: 0.93,
|
||||
color: Color(0xFF000000).withOpacity(0.6),
|
||||
),
|
||||
),
|
||||
|
||||
Text(
|
||||
"121 Saint Denis Street,\n"
|
||||
"Louisiana,\n"
|
||||
"United States of America",
|
||||
style: TextStyle(
|
||||
fontFamily: selectedFont,
|
||||
fontSize: 8.sp,
|
||||
height: 1.7.h,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,13 +16,22 @@ class PostCardPreviewWidget extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: 227.h,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(6.r),
|
||||
child: Image.file(File(imagePath), fit: BoxFit.cover)),
|
||||
|
||||
height: 230.h,
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(colors: [
|
||||
Color(0xffE2D6C2),
|
||||
Color(0xffFFF5E6),
|
||||
Color(0xffFFF5E6),
|
||||
]),
|
||||
border: Border.all(
|
||||
color: Color(0xff000000).withOpacity(0.12),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Image.file(File(imagePath), fit: BoxFit.cover),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
||||
import '../../common_packages/common_app_texts.dart';
|
||||
|
||||
class SearchOffersWithListing extends StatelessWidget {
|
||||
SearchOffersWithListing({super.key});
|
||||
|
||||
@@ -32,7 +34,7 @@ class SearchOffersWithListing extends StatelessWidget {
|
||||
},
|
||||
child: Icon(Icons.arrow_back)),
|
||||
SizedBox(width: 8.w),
|
||||
CustomText(text: "Offers with Flexi Card", size: 12.sp),
|
||||
CustomText(text: "Offers with ${CommonAppText.selectiveCard} Card", size: 12.sp),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -142,6 +144,8 @@ class SearchOffersWithListing extends StatelessWidget {
|
||||
text: offer["description"] ?? "",
|
||||
color: Colors.black.withOpacity(.6),
|
||||
size: 12.sp,
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -31,7 +31,7 @@ class _SplashScreenState extends State<SplashScreen> {
|
||||
backgroundColor: const Color(0xFFF95F62), // Coral red background
|
||||
body: Center(
|
||||
child: Lottie.asset(
|
||||
'assets/intro/animm.json', // Your Lottie file
|
||||
'assets/intro/animation.json', // Your Lottie file
|
||||
fit: BoxFit.cover,
|
||||
repeat: true,
|
||||
),
|
||||
|
||||
@@ -246,4 +246,4 @@
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
240
pubspec.lock
240
pubspec.lock
@@ -129,6 +129,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.11"
|
||||
dio:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dio
|
||||
sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.9.0"
|
||||
dio_web_adapter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dio_web_adapter
|
||||
sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
dylib:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dylib
|
||||
sha256: bf609b3eb6492a3309b3d1dbe8f83a4031de5535dd7686be33487051cc760bb0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.3"
|
||||
equatable:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -206,6 +230,14 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_angle:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_angle
|
||||
sha256: "344a6b5ba7fa4893799ba8a98ed1b164e0b92ce77cf98b6c0f276efe1032249e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.8"
|
||||
flutter_bloc:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -557,6 +589,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.1"
|
||||
logger:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logger
|
||||
sha256: a7967e31b703831a893bbc3c3dd11db08126fe5f369b5c648a36f821979f5be3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.2"
|
||||
lottie:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -585,10 +625,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
version: "1.17.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -605,6 +645,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
opentype_dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: opentype_dart
|
||||
sha256: "4bd96aeed494289a87e92bde20afe60f59648dcef253c0a7159b65ffa23899dc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.0.1"
|
||||
package_info_plus:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -725,22 +773,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
share_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: share_plus
|
||||
sha256: "14c8860d4de93d3a7e53af51bff479598c4e999605290756bbbe45cf65b37840"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "12.0.1"
|
||||
share_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: share_plus_platform_interface
|
||||
sha256: "88023e53a13429bd65d8e85e11a9b484f49d4c190abbd96c7932b74d6927cc9a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.0"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -894,10 +926,106 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
|
||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.6"
|
||||
version: "0.7.7"
|
||||
three_js:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: three_js
|
||||
sha256: "76554d705ee0f7101d969773bc92fbd18d2e0f28ee87a9e41f7c0a86bd76f480"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.6"
|
||||
three_js_advanced_loaders:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: three_js_advanced_loaders
|
||||
sha256: "901ee356fba0ffe57547e448ee73aaa351b68a46608cf8f13c798b28980fbd38"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.4"
|
||||
three_js_animations:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: three_js_animations
|
||||
sha256: "7f7d708e96301e0fef1ab71ebaef280d5885c081f184e49c6956a22bd51ee444"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1"
|
||||
three_js_controls:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: three_js_controls
|
||||
sha256: "4cc52d53e3bf1cca0d469c5f200b10c5c96954663d551bdb6b0b4278a0dca148"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.2"
|
||||
three_js_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: three_js_core
|
||||
sha256: "4aa78017f1562cd2c67b3146587eb8c6b75870ca030b0a5263d532d6660c31dd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.6"
|
||||
three_js_core_loaders:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: three_js_core_loaders
|
||||
sha256: "85ca58d66b57b94acb5b4598e97ebd54a1679025f4eaf8cfa2dade4b561b2189"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1"
|
||||
three_js_curves:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: three_js_curves
|
||||
sha256: "9f11f2fa869dc6b1aa01219bd0587b768cc0dcbf0bde5bbb576c5fabcbb3b3c7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1"
|
||||
three_js_geometry:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: three_js_geometry
|
||||
sha256: "11cbf2661b32fe90d7150c2dddd2846866de6500589e13eb9d682b017c19420a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1"
|
||||
three_js_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: three_js_math
|
||||
sha256: "8687016308b8d6d027a8fb3982eedb7514bbc4cf21145da0116dbd801254aa50"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.5"
|
||||
three_js_sensors:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: three_js_sensors
|
||||
sha256: "40874a52c6e617762d324e8149356ccacf90aa65a8904f6e3520e4aba1bff447"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.2"
|
||||
three_js_simple_loaders:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: three_js_simple_loaders
|
||||
sha256: "3728e84dd74cf68fbf7b6260d5eda47afbcddc48dd7b127a56fef85d92b87c78"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.2"
|
||||
three_js_text:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: three_js_text
|
||||
sha256: ba5f44e5da00b5f510a24308644da72c5d3b039542da3d1c13b08f7e28066d29
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
timezone:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -922,70 +1050,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
url_launcher:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.2"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: "5c8b6c2d89a78f5a1cca70a73d9d5f86c701b36b42f9c9dac7bad592113c28e9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.24"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_ios
|
||||
sha256: "6b63f1441e4f653ae799166a72b50b1767321ecc263a57aadf825a7a2a5477d9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.5"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_linux
|
||||
sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_macos
|
||||
sha256: "8262208506252a3ed4ff5c0dc1e973d2c0e0ef337d0a074d35634da5d44397c9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.4"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_platform_interface
|
||||
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
url_launcher_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.4"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1006,10 +1070,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: video_player
|
||||
sha256: "0d55b1f1a31e5ad4c4967bfaa8ade0240b07d20ee4af1dfef5f531056512961a"
|
||||
sha256: "096bc28ce10d131be80dfb00c223024eb0fba301315a406728ab43dd99c45bdf"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.10.0"
|
||||
version: "2.10.1"
|
||||
video_player_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1022,10 +1086,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player_avfoundation
|
||||
sha256: "19ed1162a7a5520e7d7791e0b7b73ba03161b6a69428b82e4689e435b325432d"
|
||||
sha256: "03fc6d07dba2499588d30887329b399c1fe2d68ce4b7fcff0db79f44a2603f69"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.8.5"
|
||||
version: "2.8.6"
|
||||
video_player_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -33,6 +33,7 @@ dependencies:
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
three_js: ^0.2.6
|
||||
google_fonts: ^6.3.2
|
||||
flutter_bloc: ^9.1.1
|
||||
cupertino_icons: ^1.0.8
|
||||
@@ -48,11 +49,10 @@ dependencies:
|
||||
shared_preferences: ^2.5.3
|
||||
flutter_launcher_icons: ^0.14.4
|
||||
flutter_glass_morphism: ^1.0.2
|
||||
video_player: ^2.10.0
|
||||
lottie: ^3.3.2
|
||||
flutter_native_splash: ^2.4.7
|
||||
url_launcher: ^6.3.2
|
||||
share_plus: ^12.0.1
|
||||
video_player: ^2.10.1
|
||||
dio: ^5.9.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user