diff --git a/ride_share/.gitignore b/ride_share/.gitignore
index 24476c5..1f02e9c 100644
--- a/ride_share/.gitignore
+++ b/ride_share/.gitignore
@@ -37,8 +37,13 @@ app.*.symbols
# Obfuscation related
app.*.map.json
+config.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
+
+#Secrets file for API keys
+/lib/auth/secrets.dart
+/lib/auth.config.dart
diff --git a/ride_share/android/app/build.gradle b/ride_share/android/app/build.gradle
index cab9e96..af8b9ef 100644
--- a/ride_share/android/app/build.gradle
+++ b/ride_share/android/app/build.gradle
@@ -21,6 +21,12 @@ if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
+def envVariables = [
+ MAPS_API_KEY: project.hasProperty('MAPS_API_KEY')
+ ? MAPS_API_KEY
+ : "API_KEY",
+];
+
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
@@ -47,10 +53,11 @@ android {
applicationId "com.example.ride_share"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
- minSdkVersion flutter.minSdkVersion
+ minSdkVersion 23
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
+ resValue "string", "googleMapApiKey", envVariables.MAPS_API_KEY
}
buildTypes {
@@ -68,4 +75,5 @@ flutter {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation 'com.google.android.gms:play-services-maps:18.1.0'
}
diff --git a/ride_share/android/app/google-services.json b/ride_share/android/app/google-services.json
new file mode 100644
index 0000000..cf27c0c
--- /dev/null
+++ b/ride_share/android/app/google-services.json
@@ -0,0 +1,99 @@
+{
+ "project_info": {
+ "project_number": "943152405737",
+ "firebase_url": "https://school-carpool-csp1-default-rtdb.europe-west1.firebasedatabase.app",
+ "project_id": "school-carpool-csp1",
+ "storage_bucket": "school-carpool-csp1.appspot.com"
+ },
+ "client": [
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:943152405737:android:5457e3555963406fdce478",
+ "android_client_info": {
+ "package_name": "com.example.ride_share"
+ }
+ },
+ "oauth_client": [
+ {
+ "client_id": "943152405737-dl3gep47gr3a3m6e6uhjfmctev8nq0rd.apps.googleusercontent.com",
+ "client_type": 1,
+ "android_info": {
+ "package_name": "com.example.ride_share",
+ "certificate_hash": "0cef302226b6059fc5e61d390fbeeba8fc32c916"
+ }
+ },
+ {
+ "client_id": "943152405737-1ibrmd1ee903soduuo0cgvfoqag8k98n.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ],
+ "api_key": [
+ {
+ "current_key": "AIzaSyCZy5aFxa7OGpgnAFvVX-ozsCZ71DJWCbA"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": [
+ {
+ "client_id": "943152405737-1ibrmd1ee903soduuo0cgvfoqag8k98n.apps.googleusercontent.com",
+ "client_type": 3
+ },
+ {
+ "client_id": "943152405737-jheatv57d64h92jk6hlugo81ivhpqhq2.apps.googleusercontent.com",
+ "client_type": 2,
+ "ios_info": {
+ "bundle_id": "com.example.rideShare"
+ }
+ }
+ ]
+ }
+ }
+ },
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:943152405737:android:1756b43b64699837dce478",
+ "android_client_info": {
+ "package_name": "com.school.testrun"
+ }
+ },
+ "oauth_client": [
+ {
+ "client_id": "943152405737-4igp2ltm5gqsco0stb3r06e52v12sha0.apps.googleusercontent.com",
+ "client_type": 1,
+ "android_info": {
+ "package_name": "com.school.testrun",
+ "certificate_hash": "0cef302226b6059fc5e61d390fbeeba8fc32c916"
+ }
+ },
+ {
+ "client_id": "943152405737-1ibrmd1ee903soduuo0cgvfoqag8k98n.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ],
+ "api_key": [
+ {
+ "current_key": "AIzaSyCZy5aFxa7OGpgnAFvVX-ozsCZ71DJWCbA"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": [
+ {
+ "client_id": "943152405737-1ibrmd1ee903soduuo0cgvfoqag8k98n.apps.googleusercontent.com",
+ "client_type": 3
+ },
+ {
+ "client_id": "943152405737-jheatv57d64h92jk6hlugo81ivhpqhq2.apps.googleusercontent.com",
+ "client_type": 2,
+ "ios_info": {
+ "bundle_id": "com.example.rideShare"
+ }
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "configuration_version": "1"
+}
\ No newline at end of file
diff --git a/ride_share/android/app/src/main/AndroidManifest.xml b/ride_share/android/app/src/main/AndroidManifest.xml
index 9abd6f9..dacd20d 100644
--- a/ride_share/android/app/src/main/AndroidManifest.xml
+++ b/ride_share/android/app/src/main/AndroidManifest.xml
@@ -1,6 +1,8 @@
-
+
+
@@ -17,9 +19,9 @@
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
+ android:name="io.flutter.embedding.android.NormalTheme"
+ android:resource="@style/NormalTheme"
+ />
@@ -30,5 +32,7 @@
+
diff --git a/ride_share/android/app/src/main/kotlin/com/example/ride_share/MainActivity.kt b/ride_share/android/app/src/main/kotlin/com/example/ride_share/MainActivity.kt
index ca04e26..c4fe69f 100644
--- a/ride_share/android/app/src/main/kotlin/com/example/ride_share/MainActivity.kt
+++ b/ride_share/android/app/src/main/kotlin/com/example/ride_share/MainActivity.kt
@@ -1,6 +1,6 @@
package com.example.ride_share
-import io.flutter.embedding.android.FlutterActivity
+import io.flutter.embedding.android.FlutterFragmentActivity
-class MainActivity: FlutterActivity() {
+class MainActivity: FlutterFragmentActivity() {
}
diff --git a/ride_share/assets/Badge (2).png b/ride_share/assets/Badge (2).png
new file mode 100644
index 0000000..00c2641
Binary files /dev/null and b/ride_share/assets/Badge (2).png differ
diff --git a/ride_share/assets/Pin_current_location.png b/ride_share/assets/Pin_current_location.png
new file mode 100644
index 0000000..e8b3dc3
Binary files /dev/null and b/ride_share/assets/Pin_current_location.png differ
diff --git a/ride_share/assets/Pin_destination.png b/ride_share/assets/Pin_destination.png
new file mode 100644
index 0000000..315fc8e
Binary files /dev/null and b/ride_share/assets/Pin_destination.png differ
diff --git a/ride_share/assets/Pin_source.png b/ride_share/assets/Pin_source.png
new file mode 100644
index 0000000..1705029
Binary files /dev/null and b/ride_share/assets/Pin_source.png differ
diff --git a/ride_share/assets/images/profile.png b/ride_share/assets/images/profile.png
new file mode 100644
index 0000000..1c927a4
Binary files /dev/null and b/ride_share/assets/images/profile.png differ
diff --git a/ride_share/assets/map_style.txt b/ride_share/assets/map_style.txt
new file mode 100644
index 0000000..454740d
--- /dev/null
+++ b/ride_share/assets/map_style.txt
@@ -0,0 +1,218 @@
+[
+ {
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#f5f5f5"
+ }
+ ]
+ },
+ {
+ "elementType": "labels",
+ "stylers": [
+ {
+ "visibility": "off"
+ }
+ ]
+ },
+ {
+ "elementType": "labels.icon",
+ "stylers": [
+ {
+ "visibility": "off"
+ }
+ ]
+ },
+ {
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#616161"
+ }
+ ]
+ },
+ {
+ "elementType": "labels.text.stroke",
+ "stylers": [
+ {
+ "color": "#f5f5f5"
+ }
+ ]
+ },
+ {
+ "featureType": "administrative",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "visibility": "off"
+ }
+ ]
+ },
+ {
+ "featureType": "administrative.land_parcel",
+ "stylers": [
+ {
+ "visibility": "off"
+ }
+ ]
+ },
+ {
+ "featureType": "administrative.land_parcel",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#bdbdbd"
+ }
+ ]
+ },
+ {
+ "featureType": "administrative.neighborhood",
+ "stylers": [
+ {
+ "visibility": "off"
+ }
+ ]
+ },
+ {
+ "featureType": "poi",
+ "stylers": [
+ {
+ "visibility": "off"
+ }
+ ]
+ },
+ {
+ "featureType": "poi",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#eeeeee"
+ }
+ ]
+ },
+ {
+ "featureType": "poi",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#757575"
+ }
+ ]
+ },
+ {
+ "featureType": "poi.park",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#e5e5e5"
+ }
+ ]
+ },
+ {
+ "featureType": "poi.park",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#9e9e9e"
+ }
+ ]
+ },
+ {
+ "featureType": "road",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#ffffff"
+ }
+ ]
+ },
+ {
+ "featureType": "road",
+ "elementType": "labels.icon",
+ "stylers": [
+ {
+ "visibility": "off"
+ }
+ ]
+ },
+ {
+ "featureType": "road.arterial",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#757575"
+ }
+ ]
+ },
+ {
+ "featureType": "road.highway",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#dadada"
+ }
+ ]
+ },
+ {
+ "featureType": "road.highway",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#616161"
+ }
+ ]
+ },
+ {
+ "featureType": "road.local",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#9e9e9e"
+ }
+ ]
+ },
+ {
+ "featureType": "transit",
+ "stylers": [
+ {
+ "visibility": "off"
+ }
+ ]
+ },
+ {
+ "featureType": "transit.line",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#e5e5e5"
+ }
+ ]
+ },
+ {
+ "featureType": "transit.station",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#eeeeee"
+ }
+ ]
+ },
+ {
+ "featureType": "water",
+ "elementType": "geometry",
+ "stylers": [
+ {
+ "color": "#c9c9c9"
+ }
+ ]
+ },
+ {
+ "featureType": "water",
+ "elementType": "labels.text.fill",
+ "stylers": [
+ {
+ "color": "#9e9e9e"
+ }
+ ]
+ }
+]
diff --git a/ride_share/ios/Flutter/Debug.xcconfig b/ride_share/ios/Flutter/Debug.xcconfig
index 592ceee..f402a8f 100644
--- a/ride_share/ios/Flutter/Debug.xcconfig
+++ b/ride_share/ios/Flutter/Debug.xcconfig
@@ -1 +1,2 @@
+#include "Default.xcconfig"
#include "Generated.xcconfig"
diff --git a/ride_share/ios/Flutter/Default.xcconfig b/ride_share/ios/Flutter/Default.xcconfig
new file mode 100644
index 0000000..d1fa0eb
--- /dev/null
+++ b/ride_share/ios/Flutter/Default.xcconfig
@@ -0,0 +1 @@
+MAPS_API_KEY=API_KEY
\ No newline at end of file
diff --git a/ride_share/ios/Flutter/Release.xcconfig b/ride_share/ios/Flutter/Release.xcconfig
index 592ceee..f402a8f 100644
--- a/ride_share/ios/Flutter/Release.xcconfig
+++ b/ride_share/ios/Flutter/Release.xcconfig
@@ -1 +1,2 @@
+#include "Default.xcconfig"
#include "Generated.xcconfig"
diff --git a/ride_share/ios/Runner/GoogleService-Info.plist b/ride_share/ios/Runner/GoogleService-Info.plist
new file mode 100644
index 0000000..53c88d1
--- /dev/null
+++ b/ride_share/ios/Runner/GoogleService-Info.plist
@@ -0,0 +1,38 @@
+
+
+
+
+ CLIENT_ID
+ 943152405737-jheatv57d64h92jk6hlugo81ivhpqhq2.apps.googleusercontent.com
+ REVERSED_CLIENT_ID
+ com.googleusercontent.apps.943152405737-jheatv57d64h92jk6hlugo81ivhpqhq2
+ ANDROID_CLIENT_ID
+ 943152405737-4igp2ltm5gqsco0stb3r06e52v12sha0.apps.googleusercontent.com
+ API_KEY
+
+ GCM_SENDER_ID
+ 943152405737
+ PLIST_VERSION
+ 1
+ BUNDLE_ID
+ com.example.rideShare
+ PROJECT_ID
+ school-carpool-csp1
+ STORAGE_BUCKET
+ school-carpool-csp1.appspot.com
+ IS_ADS_ENABLED
+
+ IS_ANALYTICS_ENABLED
+
+ IS_APPINVITE_ENABLED
+
+ IS_GCM_ENABLED
+
+ IS_SIGNIN_ENABLED
+
+ GOOGLE_APP_ID
+ 1:943152405737:ios:f1b1f4fc2facef5bdce478
+ DATABASE_URL
+ https://school-carpool-csp1-default-rtdb.europe-west1.firebasedatabase.app
+
+
\ No newline at end of file
diff --git a/ride_share/ios/Runner/Info.plist b/ride_share/ios/Runner/Info.plist
index 10f511b..baf0edd 100644
--- a/ride_share/ios/Runner/Info.plist
+++ b/ride_share/ios/Runner/Info.plist
@@ -7,6 +7,9 @@
CFBundleDisplayName
Ride Share
CFBundleExecutable
+ CFBundleDisplayName
+ ${MAPS_API_KEY}
+ CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
$(PRODUCT_BUNDLE_IDENTIFIER)
diff --git a/ride_share/ios/firebase_app_id_file.json b/ride_share/ios/firebase_app_id_file.json
new file mode 100644
index 0000000..6726fb5
--- /dev/null
+++ b/ride_share/ios/firebase_app_id_file.json
@@ -0,0 +1,7 @@
+{
+ "file_generated_by": "FlutterFire CLI",
+ "purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory",
+ "GOOGLE_APP_ID": "1:943152405737:ios:f1b1f4fc2facef5bdce478",
+ "FIREBASE_PROJECT_ID": "school-carpool-csp1",
+ "GCM_SENDER_ID": "943152405737"
+}
\ No newline at end of file
diff --git a/ride_share/lib/firebase_options.dart b/ride_share/lib/firebase_options.dart
new file mode 100644
index 0000000..6c8bcae
--- /dev/null
+++ b/ride_share/lib/firebase_options.dart
@@ -0,0 +1,80 @@
+// File generated by FlutterFire CLI.
+// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members
+import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
+import 'package:flutter/foundation.dart'
+ show defaultTargetPlatform, kIsWeb, TargetPlatform;
+
+/// Default [FirebaseOptions] for use with your Firebase apps.
+///
+/// Example:
+/// ```dart
+/// import 'firebase_options.dart';
+/// // ...
+/// await Firebase.initializeApp(
+/// options: DefaultFirebaseOptions.currentPlatform,
+/// );
+/// ```
+class DefaultFirebaseOptions {
+ static FirebaseOptions get currentPlatform {
+ if (kIsWeb) {
+ return web;
+ }
+ switch (defaultTargetPlatform) {
+ case TargetPlatform.android:
+ return android;
+ case TargetPlatform.iOS:
+ return ios;
+ case TargetPlatform.macOS:
+ throw UnsupportedError(
+ 'DefaultFirebaseOptions have not been configured for macos - '
+ 'you can reconfigure this by running the FlutterFire CLI again.',
+ );
+ case TargetPlatform.windows:
+ throw UnsupportedError(
+ 'DefaultFirebaseOptions have not been configured for windows - '
+ 'you can reconfigure this by running the FlutterFire CLI again.',
+ );
+ case TargetPlatform.linux:
+ throw UnsupportedError(
+ 'DefaultFirebaseOptions have not been configured for linux - '
+ 'you can reconfigure this by running the FlutterFire CLI again.',
+ );
+ default:
+ throw UnsupportedError(
+ 'DefaultFirebaseOptions are not supported for this platform.',
+ );
+ }
+ }
+
+ static const FirebaseOptions web = FirebaseOptions(
+ apiKey: '',
+ appId: '1:943152405737:web:ae3cf64ad426bde0dce478',
+ messagingSenderId: '943152405737',
+ projectId: 'school-carpool-csp1',
+ authDomain: 'school-carpool-csp1.firebaseapp.com',
+ databaseURL: 'https://school-carpool-csp1-default-rtdb.europe-west1.firebasedatabase.app',
+ storageBucket: 'school-carpool-csp1.appspot.com',
+ measurementId: 'G-X5NJCDV6TM',
+ );
+
+ static const FirebaseOptions android = FirebaseOptions(
+ apiKey: '',
+ appId: '1:943152405737:android:5457e3555963406fdce478',
+ messagingSenderId: '943152405737',
+ projectId: 'school-carpool-csp1',
+ databaseURL: 'https://school-carpool-csp1-default-rtdb.europe-west1.firebasedatabase.app',
+ storageBucket: 'school-carpool-csp1.appspot.com',
+ );
+
+ static const FirebaseOptions ios = FirebaseOptions(
+ apiKey: '',
+ appId: '1:943152405737:ios:f1b1f4fc2facef5bdce478',
+ messagingSenderId: '943152405737',
+ projectId: 'school-carpool-csp1',
+ databaseURL: 'https://school-carpool-csp1-default-rtdb.europe-west1.firebasedatabase.app',
+ storageBucket: 'school-carpool-csp1.appspot.com',
+ androidClientId: '943152405737-4igp2ltm5gqsco0stb3r06e52v12sha0.apps.googleusercontent.com',
+ iosClientId: '943152405737-jheatv57d64h92jk6hlugo81ivhpqhq2.apps.googleusercontent.com',
+ iosBundleId: 'com.example.rideShare',
+ );
+}
diff --git a/ride_share/lib/main.dart b/ride_share/lib/main.dart
index b432ff0..420997f 100644
--- a/ride_share/lib/main.dart
+++ b/ride_share/lib/main.dart
@@ -1,10 +1,15 @@
+import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
+import 'package:ride_share/firebase_options.dart';
import 'package:ride_share/src/features/authentication/screens/splash_screen/splash_screen.dart';
import 'package:ride_share/src/features/authentication/screens/welcome/welcome_screen.dart';
+import 'package:ride_share/src/repository/authentication_repository.dart';
import 'package:ride_share/src/utils/theme/theme.dart';
void main() {
+ WidgetsFlutterBinding.ensureInitialized();
+ Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform).then((value) => Get.put(AuthenticationRepository()));
runApp(const MyApp());
}
diff --git a/ride_share/lib/src/common_widgets/location_list_tile.dart b/ride_share/lib/src/common_widgets/location_list_tile.dart
new file mode 100644
index 0000000..ed3439b
--- /dev/null
+++ b/ride_share/lib/src/common_widgets/location_list_tile.dart
@@ -0,0 +1,37 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+
+class LocationListTile extends StatelessWidget{
+ final String location;
+ final VoidCallback press;
+
+ const LocationListTile({
+ Key? key,
+ required this.location,
+ required this.press,
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ children: [
+ ListTile(
+ onTap: press,
+ horizontalTitleGap: 0,
+ leading: const Icon( Icons.location_on_outlined,),
+ title: Text(
+ location,
+ maxLines: 2,
+ overflow: TextOverflow.ellipsis,
+ ),
+ ),
+ const Divider(
+ height: 2,
+ thickness: 2,
+ color: Colors.grey,
+ ),
+ ],
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/ride_share/lib/src/common_widgets/nav_drawer.dart b/ride_share/lib/src/common_widgets/nav_drawer.dart
new file mode 100644
index 0000000..63167df
--- /dev/null
+++ b/ride_share/lib/src/common_widgets/nav_drawer.dart
@@ -0,0 +1,68 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:ride_share/src/constants/colors.dart';
+import 'package:ride_share/src/constants/text_strings.dart';
+import 'package:ride_share/src/features/home/screens/profile/profile_screen.dart';
+
+import '../repository/authentication_repository.dart';
+
+class NavDrawer extends StatelessWidget{
+ const NavDrawer({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return Drawer(
+ child: ListView(
+ padding: EdgeInsets.zero,
+ children: [
+ DrawerHeader(
+ child: ListTile(
+ leading: Icon(
+ size: 100,
+ Icons.person_outline
+ ),
+ title: Text(tEditProfile),
+ onTap: () => Get.to(() => const ProfileScreen()),
+ ),
+ ),
+ ListTile(
+ leading: Icon(Icons.wallet_outlined),
+ title: Text(tPayment),
+ onTap: () => {},
+ ),
+ ListTile(
+ leading: Icon(Icons.calendar_month),
+ title: Text(tMySchedule),
+ onTap: () => {Navigator.of(context).pop()},
+ ),
+ ListTile(
+ leading: Icon(Icons.history),
+ title: Text(tTripHistory),
+ onTap: () => {Navigator.of(context).pop()},
+ ),
+ ListTile(
+ leading: Icon(Icons.people_alt_outlined),
+ title: Text(tRideRequests),
+ onTap: () => {Navigator.of(context).pop()},
+ ),
+ Divider(
+ height: 2,
+ thickness: 1,
+ color: Colors.grey,
+ ),
+ ListTile(
+ leading: Icon(Icons.contact_support_outlined),
+ title: Text(tSupport),
+ onTap: () => {Navigator.of(context).pop()},
+ ),
+ ListTile(
+ leading: Icon(Icons.logout, color: Colors.red,),
+ title: Text(tAbout),
+ onTap: () => {AuthenticationRepository.instance.logout()},
+ ),
+ ],
+ ),
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/ride_share/lib/src/common_widgets/registration_header.dart b/ride_share/lib/src/common_widgets/registration_header.dart
new file mode 100644
index 0000000..326b550
--- /dev/null
+++ b/ride_share/lib/src/common_widgets/registration_header.dart
@@ -0,0 +1,39 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:google_fonts/google_fonts.dart';
+import 'package:ride_share/src/constants/colors.dart';
+
+Widget regHeaderPlain({required String title, String? subtitle}) {
+ return Container(
+ width: Get.width,
+ decoration: BoxDecoration(
+ color: tSecondaryColor,
+ ),
+ height: Get.height * 0.25,
+ child: Container(
+ height: Get.height * 0.1,
+ width: Get.width,
+ margin: EdgeInsets.only(top: Get.height * 0.05),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Text(
+ title,
+ style: GoogleFonts.poppins(
+ fontSize: 24,
+ fontWeight: FontWeight.bold,
+ color: Colors.white),
+ ),
+ if (subtitle != null)
+ Text(
+ subtitle,
+ style: GoogleFonts.poppins(
+ fontSize: 16,
+ fontWeight: FontWeight.w400,
+ color: Colors.white),
+ ),
+ ],
+ )),
+ );
+}
diff --git a/ride_share/lib/src/common_widgets/text_widget.dart b/ride_share/lib/src/common_widgets/text_widget.dart
new file mode 100644
index 0000000..a662ab6
--- /dev/null
+++ b/ride_share/lib/src/common_widgets/text_widget.dart
@@ -0,0 +1,6 @@
+import 'package:flutter/material.dart';
+import 'package:google_fonts/google_fonts.dart';
+
+Widget textWidget({required String text,double fontSize = 12, FontWeight fontWeight = FontWeight.normal,Color color = Colors.black}){
+ return Text(text, style: GoogleFonts.poppins(fontSize: fontSize,fontWeight: fontWeight,color: color),);
+}
\ No newline at end of file
diff --git a/ride_share/lib/src/constants/image_strings.dart b/ride_share/lib/src/constants/image_strings.dart
index 6ea98a6..50594b6 100644
--- a/ride_share/lib/src/constants/image_strings.dart
+++ b/ride_share/lib/src/constants/image_strings.dart
@@ -4,4 +4,9 @@ const String tLoginScreenImage = "assets/images/login.png";
const String tSignUpScreenImage = "assets/images/signup.png";
const String tResetPwdScreenImage = "assets/images/reset_pwd.png";
const String tForgotPwdScreenImage = "assets/images/forgot_pwd.png";
-const String tOTPScreenImage = "assets/images/enter_OTP.png";
\ No newline at end of file
+const String tOTPScreenImage = "assets/images/enter_OTP.png";
+const String tProfilePic = "assets/images/profile.png";
+const String tcurrentloc = "assets/images/Pin_current_location.png";
+const String tpindest = "assets/images/Pin_destination.png";
+const String tpinsource = "assets/images/Pin_source.png";
+const String tbadge = "assets/images/Badge (2).png";
\ No newline at end of file
diff --git a/ride_share/lib/src/constants/text_strings.dart b/ride_share/lib/src/constants/text_strings.dart
index e9a1f80..17a8cea 100644
--- a/ride_share/lib/src/constants/text_strings.dart
+++ b/ride_share/lib/src/constants/text_strings.dart
@@ -40,3 +40,24 @@ const String tOtpSubTitle = "Verification";
const String tOtpMessage = "Enter the verification code sent to\n";
const String tOtpNotSent = "Haven't received a code?";
const String tResendOtp = "Resend";
+
+
+const String tPickUpLocation = "Pickup Location";
+const String tRating = "Rating";
+const String tEditProfile = "Edit Profile";
+const String tPayment = "Payment";
+const String tMySchedule = "My Schedule";
+const String tSupport = "Support";
+const String tAbout = "About";
+const String tTripHistory = "Trip History";
+const String tRideRequests = "Ride Requests";
+const String tProfile = "Profile";
+const String tVerify = "Verify";
+const String tChange = "change";
+const String tLocations = "Locations";
+const String tAddHome = "Add a home location";
+const String tAddPickup = "Add a pickup location";
+const String tLanguage = "Language";
+const String tLanguageUK = "English - UK";
+const String tLogout = "Logout";
+const String tDeleteAcc = "Delete Account";
diff --git a/ride_share/lib/src/features/authentication/controllers/login_controller.dart b/ride_share/lib/src/features/authentication/controllers/login_controller.dart
index c4ec16d..1b40478 100644
--- a/ride_share/lib/src/features/authentication/controllers/login_controller.dart
+++ b/ride_share/lib/src/features/authentication/controllers/login_controller.dart
@@ -3,10 +3,8 @@ import 'package:get/get.dart';
class LoginController extends GetxController{
static LoginController get instance => Get.find();
- //
- // final userRepo = Get.put(UserRepository());
- final email = TextEditingController();
+ final phone = TextEditingController();
final password = TextEditingController();
}
\ No newline at end of file
diff --git a/ride_share/lib/src/features/authentication/controllers/otp_controller.dart b/ride_share/lib/src/features/authentication/controllers/otp_controller.dart
new file mode 100644
index 0000000..d368477
--- /dev/null
+++ b/ride_share/lib/src/features/authentication/controllers/otp_controller.dart
@@ -0,0 +1,28 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:ride_share/src/features/authentication/screens/forgot_pwd/password_reset_screen.dart';
+import 'package:ride_share/src/features/home/screens/home_screen.dart';
+
+import '../../../repository/authentication_repository.dart';
+
+class OTPController extends GetxController{
+ static OTPController get instance => Get.find();
+
+ final newPassword = TextEditingController();
+ final confirmPassword = TextEditingController();
+
+ void verifyOTP(String otp) async{
+ var isVerified = await AuthenticationRepository.instance.verifyOTP(otp);
+ isVerified ? Get.offAll(const HomeScreen()) : Get.back();
+ }
+
+ void verifyForgotPwdOTP(String otp, String phoneNo) async{
+ var isVerified = await AuthenticationRepository.instance.verifyOTP(otp);
+ isVerified ? Get.offAll(PasswordResetScreen(phoneNo: phoneNo)) : Get.back();
+ }
+
+ void verifyEmailOTP(String otp, String email) async{
+ var isVerified = await AuthenticationRepository.instance.verifyEmailOTP(email, otp);
+ isVerified ? Get.offAll(PasswordResetScreen(phoneNo: email)) : Get.back();
+ }
+}
\ No newline at end of file
diff --git a/ride_share/lib/src/features/authentication/controllers/signup_controller.dart b/ride_share/lib/src/features/authentication/controllers/signup_controller.dart
index f528f5a..962d720 100644
--- a/ride_share/lib/src/features/authentication/controllers/signup_controller.dart
+++ b/ride_share/lib/src/features/authentication/controllers/signup_controller.dart
@@ -1,16 +1,21 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
+import '../../../models/user_model.dart';
+import '../../../repository/authentication_repository.dart';
+import '../../../repository/user_repository.dart';
+import '../screens/forgot_pwd/otp_screen.dart';
+
class SignUpController extends GetxController {
static SignUpController get instance => Get.find();
- final firstName = TextEditingController();
+ final fullName = TextEditingController();
final lastName = TextEditingController();
final email = TextEditingController();
final phoneNo = TextEditingController();
final password = TextEditingController();
- // final userRepo = Get.put(UserRepository());
+ final userRepo = Get.put(UserRepository());
// void registerUser(String email, String password, String phoneNo) {
// String? error = AuthenticationRepository.instance
@@ -20,20 +25,19 @@ class SignUpController extends GetxController {
// }
// }
- // Future createUser(UserModel user) async {
- // await userRepo.createUser(user);
- // AuthenticationRepository.instance.createUserWithEmailAndPassword(user.email, user.password!);
- // // phoneAuthentication(user.phoneNo);
- // // Get.to(() => const OTPScreen());
- // }
+ Future createUser(UserModel user) async {
+ await userRepo.createUser(user);
+ print(user.phoneNo);
+ await phoneAuthentication(user.phoneNo);
+ // AuthenticationRepository.instance.createUserWithEmailAndPassword(user.email, user.password!) as bool;
+ Get.to(() => OTPScreen(phoneNo: user.phoneNo));
+ }
- // Future phoneAuthentication(String fullname, String email, String phone)async {
- // await userRepo.createRealTimeUser(fullname, email, phone);
- // AuthenticationRepository.instance.phoneAuthentication(phone);
- // }
- // phoneAuthentication(String phone) {
- // AuthenticationRepository.instance.phoneAuthentication(phone);
- // }
+ Future phoneAuthentication(String phone)async {
+ // await userRepo.createRealTimeUser(fullname, email, phone);
+ AuthenticationRepository.instance.phoneAuthentication(phone);
+ }
+
}
\ No newline at end of file
diff --git a/ride_share/lib/src/features/authentication/screens/forgot_pwd/email_otp_screen.dart b/ride_share/lib/src/features/authentication/screens/forgot_pwd/email_otp_screen.dart
new file mode 100644
index 0000000..4de8be5
--- /dev/null
+++ b/ride_share/lib/src/features/authentication/screens/forgot_pwd/email_otp_screen.dart
@@ -0,0 +1,138 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_otp_text_field/flutter_otp_text_field.dart';
+import 'package:get/get.dart';
+import 'package:ride_share/src/constants/colors.dart';
+import 'package:ride_share/src/constants/image_strings.dart';
+import 'package:ride_share/src/repository/authentication_repository.dart';
+
+import '../../../../constants/sizes.dart';
+import '../../../../constants/text_strings.dart';
+import '../../controllers/otp_controller.dart';
+
+// czpwkimeyjxmaujk
+
+class EmailOTPScreen extends StatelessWidget {
+ const EmailOTPScreen({super.key, required this.email});
+
+ final String email;
+
+ @override
+ Widget build(BuildContext context) {
+ var otpController = Get.put(OTPController());
+ String otp = "";
+ final size = MediaQuery.of(context).size;
+ return SafeArea(
+ child: Scaffold(
+ body: SingleChildScrollView(
+ child: Stack(
+ children: [
+ Column(
+ children: [
+ Container(
+ decoration: const BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.bottomCenter,
+ end: Alignment.topCenter,
+ colors: [
+ Colors.orange,
+ Colors.deepOrange,
+ ],
+ ),
+ ),
+ height: size.height * 0.45,
+ alignment: Alignment.center,
+ child: Image(
+ alignment: Alignment.bottomCenter,
+ image: const AssetImage(tOTPScreenImage),
+ height: size.height * 0.5,
+ ),
+ ),
+ Container(
+ height: size.height * 0.45,
+ padding: const EdgeInsets.all(tDefaultSize),
+ child: Column(
+ children: [
+ Text(
+ tOtpTitle,
+ textAlign: TextAlign.center,
+ style: Theme.of(context).textTheme.headlineMedium,
+ textScaleFactor: 1.8,
+ ),
+ Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(10),
+ color: tSecondaryColor,
+ ),
+ height: 5,
+ width: size.width * 0.3,
+ ),
+ const SizedBox(height: 20.0),
+ Text(
+ "$tOtpMessage $email",
+ textAlign: TextAlign.center,
+ ),
+ const SizedBox(height: 20.0),
+ OtpTextField(
+ mainAxisAlignment: MainAxisAlignment.center,
+ numberOfFields: 6,
+ fillColor: Colors.black.withOpacity(0.1),
+ filled: true,
+ cursorColor: tSecondaryColor,
+ focusedBorderColor: tSecondaryColor,
+ onSubmit: (code) {
+ otp = code;
+ otpController.verifyEmailOTP(otp, email);
+ },
+ ),
+ const SizedBox(
+ height: 30.0,
+ ),
+ SizedBox(
+ width: size.width * 0.75,
+ child: ElevatedButton(
+ onPressed: () {
+ otpController.verifyEmailOTP(otp, email);
+ },
+ child: const Text(
+ "Next",
+ style: TextStyle(
+ fontWeight: FontWeight.w600,
+ fontSize: 18,
+ letterSpacing: 1.5),
+ ),
+ ),
+ ),
+ const SizedBox(
+ height: tFormHeight - 10.0,
+ ),
+ TextButton(
+ onPressed: () {
+ AuthenticationRepository.instance.sendEmailOtp(email);
+ },
+ child: Text.rich(
+ TextSpan(
+ text: "$tOtpNotSent ",
+ style: Theme.of(context).textTheme.bodyMedium,
+ children: const [
+ TextSpan(
+ text: tResendOtp,
+ style: TextStyle(
+ color: tSecondaryColor,
+ decoration: TextDecoration.underline),
+ )
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ )
+ ],
+ )
+ ],
+ ),
+ )
+ ),
+ );
+ }
+}
diff --git a/ride_share/lib/src/features/authentication/screens/forgot_pwd/forgot_pwd_mail_screen.dart b/ride_share/lib/src/features/authentication/screens/forgot_pwd/forgot_pwd_mail_screen.dart
index 7bb2fa6..7c983f0 100644
--- a/ride_share/lib/src/features/authentication/screens/forgot_pwd/forgot_pwd_mail_screen.dart
+++ b/ride_share/lib/src/features/authentication/screens/forgot_pwd/forgot_pwd_mail_screen.dart
@@ -1,21 +1,57 @@
+import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
+import 'package:get/get.dart';
import 'package:ride_share/src/constants/colors.dart';
import 'package:ride_share/src/constants/image_strings.dart';
import 'package:ride_share/src/constants/text_strings.dart';
+import 'package:ride_share/src/features/authentication/screens/forgot_pwd/email_otp_screen.dart';
+import 'package:ride_share/src/repository/authentication_repository.dart';
import '../../../../common_widgets/form_header_widget.dart';
import '../../../../constants/sizes.dart';
+import 'forgot_pwd_otp_screen.dart';
class ForgotPasswordMailScreen extends StatefulWidget {
const ForgotPasswordMailScreen({super.key});
@override
State createState() => _ForgotPasswordMailScreenState();
+
+
}
+
+
class _ForgotPasswordMailScreenState extends State {
final controller = TextEditingController();
+ @override
+ void dispose(){
+ controller.dispose();
+ super.dispose();
+ }
+
+ // Future passwordReset() async{
+ // try{
+ // await FirebaseAuth.instance.sendPasswordResetEmail(email: controller.text.trim());
+ // showDialog(
+ // context: context,
+ // builder: (context){
+ // return const AlertDialog(
+ // content: Text("A password reset link has been sent to your email."),
+ // );
+ // });
+ // } on FirebaseAuthException catch (e){
+ // showDialog(
+ // context: context,
+ // builder: (context){
+ // return AlertDialog(
+ // content: Text(e.message.toString()),
+ // );
+ // });
+ // }
+ // }
+
@override
Widget build(BuildContext context) {
return SafeArea(
@@ -54,10 +90,11 @@ class _ForgotPasswordMailScreenState extends State {
SizedBox(
width: double.infinity,
child: ElevatedButton(
- onPressed: () {
-
+ onPressed: (){
+ AuthenticationRepository.instance.sendEmailOtp(controller.text.trim());
+ Get.to(() => EmailOTPScreen(email: controller.text.trim()));
},
- child: const Text("Next")
+ child: const Text("Send")
),
)
],
@@ -71,11 +108,6 @@ class _ForgotPasswordMailScreenState extends State {
);
}
- @override
- void dispose() {
- super.dispose();
- controller.dispose();
- }
// Future passwordReset() async{
// try{
diff --git a/ride_share/lib/src/features/authentication/screens/forgot_pwd/forgot_pwd_otp_screen.dart b/ride_share/lib/src/features/authentication/screens/forgot_pwd/forgot_pwd_otp_screen.dart
new file mode 100644
index 0000000..5f34fd2
--- /dev/null
+++ b/ride_share/lib/src/features/authentication/screens/forgot_pwd/forgot_pwd_otp_screen.dart
@@ -0,0 +1,136 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_otp_text_field/flutter_otp_text_field.dart';
+import 'package:get/get.dart';
+import 'package:ride_share/src/constants/colors.dart';
+import 'package:ride_share/src/constants/image_strings.dart';
+import 'package:ride_share/src/repository/authentication_repository.dart';
+
+import '../../../../constants/sizes.dart';
+import '../../../../constants/text_strings.dart';
+import '../../controllers/otp_controller.dart';
+
+class ForgotPwdOTPScreen extends StatelessWidget {
+ const ForgotPwdOTPScreen({super.key, required this.phoneNo});
+
+ final String phoneNo;
+
+ @override
+ Widget build(BuildContext context) {
+ var otpController = Get.put(OTPController());
+ String otp = "";
+ final size = MediaQuery.of(context).size;
+ return SafeArea(
+ child: Scaffold(
+ body: SingleChildScrollView(
+ child: Stack(
+ children: [
+ Column(
+ children: [
+ Container(
+ decoration: const BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.bottomCenter,
+ end: Alignment.topCenter,
+ colors: [
+ Colors.orange,
+ Colors.deepOrange,
+ ],
+ ),
+ ),
+ height: size.height * 0.45,
+ alignment: Alignment.center,
+ child: Image(
+ alignment: Alignment.bottomCenter,
+ image: const AssetImage(tOTPScreenImage),
+ height: size.height * 0.5,
+ ),
+ ),
+ Container(
+ height: size.height * 0.45,
+ padding: const EdgeInsets.all(tDefaultSize),
+ child: Column(
+ children: [
+ Text(
+ tOtpTitle,
+ textAlign: TextAlign.center,
+ style: Theme.of(context).textTheme.headlineMedium,
+ textScaleFactor: 1.8,
+ ),
+ Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(10),
+ color: tSecondaryColor,
+ ),
+ height: 5,
+ width: size.width * 0.3,
+ ),
+ const SizedBox(height: 20.0),
+ Text(
+ "$tOtpMessage $phoneNo",
+ textAlign: TextAlign.center,
+ ),
+ const SizedBox(height: 20.0),
+ OtpTextField(
+ mainAxisAlignment: MainAxisAlignment.center,
+ numberOfFields: 6,
+ fillColor: Colors.black.withOpacity(0.1),
+ filled: true,
+ cursorColor: tSecondaryColor,
+ focusedBorderColor: tSecondaryColor,
+ onSubmit: (code) {
+ otp = code;
+ otpController.verifyForgotPwdOTP(otp, phoneNo);
+ },
+ ),
+ const SizedBox(
+ height: 30.0,
+ ),
+ SizedBox(
+ width: size.width * 0.75,
+ child: ElevatedButton(
+ onPressed: () {
+ otpController.verifyForgotPwdOTP(otp, phoneNo);
+ },
+ child: const Text(
+ "Next",
+ style: TextStyle(
+ fontWeight: FontWeight.w600,
+ fontSize: 18,
+ letterSpacing: 1.5),
+ ),
+ ),
+ ),
+ const SizedBox(
+ height: tFormHeight - 10.0,
+ ),
+ TextButton(
+ onPressed: () {
+ AuthenticationRepository.instance.phoneAuthentication(phoneNo);
+ },
+ child: Text.rich(
+ TextSpan(
+ text: "$tOtpNotSent ",
+ style: Theme.of(context).textTheme.bodyMedium,
+ children: const [
+ TextSpan(
+ text: tResendOtp,
+ style: TextStyle(
+ color: tSecondaryColor,
+ decoration: TextDecoration.underline),
+ )
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ )
+ ],
+ )
+ ],
+ ),
+ )
+ ),
+ );
+ }
+}
diff --git a/ride_share/lib/src/features/authentication/screens/forgot_pwd/forgot_pwd_phone_screen.dart b/ride_share/lib/src/features/authentication/screens/forgot_pwd/forgot_pwd_phone_screen.dart
index 1e68cdc..1561c04 100644
--- a/ride_share/lib/src/features/authentication/screens/forgot_pwd/forgot_pwd_phone_screen.dart
+++ b/ride_share/lib/src/features/authentication/screens/forgot_pwd/forgot_pwd_phone_screen.dart
@@ -1,4 +1,7 @@
import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:ride_share/src/features/authentication/screens/forgot_pwd/forgot_pwd_otp_screen.dart';
+import 'package:ride_share/src/repository/authentication_repository.dart';
import '../../../../common_widgets/form_header_widget.dart';
import '../../../../constants/colors.dart';
@@ -54,8 +57,9 @@ class _ForgotPasswordPhoneScreenState extends State {
SizedBox(
width: double.infinity,
child: ElevatedButton(
- onPressed: () {
-
+ onPressed: () async{
+ await AuthenticationRepository.instance.phoneAuthentication(controller.text.trim());
+ Get.to(() => ForgotPwdOTPScreen(phoneNo: controller.text.trim()));
},
child: const Text("Next")
),
diff --git a/ride_share/lib/src/features/authentication/screens/forgot_pwd/otp_screen.dart b/ride_share/lib/src/features/authentication/screens/forgot_pwd/otp_screen.dart
index bad4035..47f5448 100644
--- a/ride_share/lib/src/features/authentication/screens/forgot_pwd/otp_screen.dart
+++ b/ride_share/lib/src/features/authentication/screens/forgot_pwd/otp_screen.dart
@@ -1,150 +1,13 @@
-// import 'package:flutter/material.dart';
-// import 'package:flutter_otp_text_field/flutter_otp_text_field.dart';
-// import 'package:ride_share/src/constants/colors.dart';
-// import 'package:ride_share/src/constants/image_strings.dart';
-// import 'package:ride_share/src/utils/theme/widgets_themes/text_theme.dart';
-// import '../../../../constants/sizes.dart';
-// import '../../../../constants/text_strings.dart';
-//
-// class OTPScreen extends StatefulWidget {
-// const OTPScreen({super.key, required this.phoneNo});
-//
-// final String phoneNo;
-//
-// @override
-// State createState() => _OTPScreenState();
-// }
-//
-// class _OTPScreenState extends State {
-// @override
-// Widget build(BuildContext context) {
-// final size = MediaQuery.of(context).size;
-// var otp;
-// return Scaffold(
-// body: Stack(
-// alignment: Alignment.center,
-// clipBehavior: Clip.none,
-// children: [
-// Positioned(
-// child: Container(
-// decoration: const BoxDecoration(
-// gradient: LinearGradient(
-// begin: Alignment.bottomCenter,
-// end: Alignment.topCenter,
-// colors: [
-// Colors.orange,
-// Colors.deepOrange,
-// ],
-// )),
-// height: size.height * 0.45,
-// alignment: Alignment.center,
-// ),
-// ),
-// Positioned(
-// top: size.height * 0.5,
-// child: Container(
-// height: size.height - (size.height * 0.5),
-// width: size.width,
-// child: Padding(
-// padding: EdgeInsets.only(left: 15, right: 15),
-// child: Column(
-// mainAxisAlignment: MainAxisAlignment.start,
-// crossAxisAlignment: CrossAxisAlignment.center,
-// children: [
-// // Text(
-// // tOtpTitle,
-// // textAlign: TextAlign.center,
-// // style: Theme.of(context).textTheme.headlineMedium,
-// // textScaleFactor: 1.8,
-// // ),
-// // Container(
-// // decoration: BoxDecoration(
-// // borderRadius: BorderRadius.circular(10),
-// // color: tSecondaryColor,
-// // ),
-// // height: 5,
-// // width: size.width * 0.3,
-// // ),
-// // const SizedBox(height: 20.0),
-// // Text(
-// // "$tOtpMessage $phoneNo",
-// // textAlign: TextAlign.center,
-// // ),
-// // const SizedBox(height: 20.0),
-// OtpTextField(
-// mainAxisAlignment: MainAxisAlignment.center,
-// numberOfFields: 6,
-// fillColor: Colors.black.withOpacity(0.1),
-// filled: true,
-// keyboardType: TextInputType.number,
-// onSubmit: (code) {
-// otp = code;
-// // OTPController.instance.verifyOTP(otp);
-// },
-// ),
-// // const SizedBox(
-// // height: 30.0,
-// // ),
-// // SizedBox(
-// // width: size.width * 0.75,
-// // child: ElevatedButton(
-// // onPressed: () {
-// // // OTPController.instance.verifyOTP(otp);
-// // },
-// // child: const Text(
-// // "Next",
-// // style: TextStyle(
-// // fontWeight: FontWeight.w600,
-// // fontSize: 18,
-// // letterSpacing: 1.5),
-// // ),
-// // ),
-// // ),
-// // const SizedBox(
-// // height: tFormHeight - 10.0,
-// // ),
-// // TextButton(
-// // onPressed: () {},
-// // child: Text.rich(TextSpan(
-// // text: "$tOtpNotSent ",
-// // style: Theme.of(context).textTheme.bodyMedium,
-// // children: const [
-// // TextSpan(
-// // text: tResendOtp,
-// // style: TextStyle(
-// // color: tSecondaryColor,
-// // decoration: TextDecoration.underline),
-// // )
-// // ],
-// // ),
-// // ),
-// // ),
-// ],
-// ),
-// ),
-// ),
-// ),
-// // Positioned(
-// // top: 26,
-// // child: Image(
-// // image: const AssetImage(tOTPScreenImage),
-// // height: size.height * 0.45,
-// // ),
-// // ),
-// ],
-// ),
-// );
-// }
-// }
-
import 'package:flutter/material.dart';
import 'package:flutter_otp_text_field/flutter_otp_text_field.dart';
-import 'package:google_fonts/google_fonts.dart';
+import 'package:get/get.dart';
import 'package:ride_share/src/constants/colors.dart';
import 'package:ride_share/src/constants/image_strings.dart';
+import 'package:ride_share/src/repository/authentication_repository.dart';
import '../../../../constants/sizes.dart';
import '../../../../constants/text_strings.dart';
+import '../../controllers/otp_controller.dart';
class OTPScreen extends StatelessWidget {
const OTPScreen({super.key, required this.phoneNo});
@@ -153,8 +16,8 @@ class OTPScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
- // var otpController = Get.put(OTPController());
- var otp;
+ var otpController = Get.put(OTPController());
+ String otp = "";
final size = MediaQuery.of(context).size;
return SafeArea(
child: Scaffold(
@@ -215,8 +78,8 @@ class OTPScreen extends StatelessWidget {
cursorColor: tSecondaryColor,
focusedBorderColor: tSecondaryColor,
onSubmit: (code) {
- // otp = code;
- // OTPController.instance.verifyOTP(otp);
+ otp = code;
+ otpController.verifyOTP(otp);
},
),
const SizedBox(
@@ -226,7 +89,7 @@ class OTPScreen extends StatelessWidget {
width: size.width * 0.75,
child: ElevatedButton(
onPressed: () {
- // OTPController.instance.verifyOTP(otp);
+ otpController.verifyOTP(otp);
},
child: const Text(
"Next",
@@ -241,7 +104,9 @@ class OTPScreen extends StatelessWidget {
height: tFormHeight - 10.0,
),
TextButton(
- onPressed: () {},
+ onPressed: () {
+ AuthenticationRepository.instance.phoneAuthentication(phoneNo);
+ },
child: Text.rich(
TextSpan(
text: "$tOtpNotSent ",
@@ -264,7 +129,8 @@ class OTPScreen extends StatelessWidget {
)
],
),
- )),
+ )
+ ),
);
}
}
diff --git a/ride_share/lib/src/features/authentication/screens/forgot_pwd/password_reset_screen.dart b/ride_share/lib/src/features/authentication/screens/forgot_pwd/password_reset_screen.dart
new file mode 100644
index 0000000..22468da
--- /dev/null
+++ b/ride_share/lib/src/features/authentication/screens/forgot_pwd/password_reset_screen.dart
@@ -0,0 +1,132 @@
+import 'package:dbcrypt/dbcrypt.dart';
+import 'package:firebase_auth/firebase_auth.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:ride_share/src/constants/colors.dart';
+import 'package:ride_share/src/constants/image_strings.dart';
+import 'package:ride_share/src/constants/text_strings.dart';
+import 'package:ride_share/src/features/authentication/controllers/otp_controller.dart';
+import 'package:ride_share/src/features/authentication/screens/login/login_screen.dart';
+import 'package:ride_share/src/models/user_model.dart';
+import 'package:ride_share/src/repository/authentication_repository.dart';
+import 'package:ride_share/src/repository/user_repository.dart';
+
+import '../../../../common_widgets/form_header_widget.dart';
+import '../../../../constants/sizes.dart';
+
+class PasswordResetScreen extends StatefulWidget {
+ const PasswordResetScreen({super.key, required this.phoneNo});
+
+ final String phoneNo;
+
+ @override
+ State createState() => _PasswordResetScreenState();
+}
+
+class _PasswordResetScreenState extends State {
+
+ @override
+ Widget build(BuildContext context) {
+ final controller = Get.put(OTPController());
+ final _formKey = GlobalKey();
+
+
+ return SafeArea(
+ child: Scaffold(
+ body: SingleChildScrollView(
+ child: Container(
+ padding: const EdgeInsets.all(tDefaultSize),
+ child: Column(
+ children: [
+ const SizedBox(
+ height: tDefaultSize * 4,
+ ),
+ const FormHeaderWidget(
+ image: tResetPwdScreenImage,
+ title: tResetMailTitle,
+ subTitle: tViaEmailSubtitle,
+ heightBetween: 7.0,
+ imageHeight: 0.3,
+ lineWidth: 0.2,
+ ),
+ const SizedBox(height: tFormHeight),
+
+ Form(
+ key: _formKey,
+ child: Column(
+ children: [
+ TextFormField(
+ cursorColor: tSecondaryColor,
+ controller: controller.newPassword,
+ obscureText: true,
+ decoration: const InputDecoration(
+ label: Text(tPassword),
+ hintText: tPassword,
+ prefixIcon: Icon(Icons.password),
+ ),
+ ),
+ TextFormField(
+ cursorColor: tSecondaryColor,
+ controller: controller.confirmPassword,
+ obscureText: true,
+ decoration: const InputDecoration(
+ label: Text(tCFMPassword),
+ hintText: tCFMPassword,
+ prefixIcon: Icon(Icons.gpp_good_outlined),
+ ),
+ ),
+ const SizedBox(height: 35.0),
+ SizedBox(
+ width: double.infinity,
+ child: ElevatedButton(
+ onPressed: () async {
+ if (_formKey.currentState!.validate()) {
+ UserModel user = await UserRepository.instance.getUserDetails(widget.phoneNo);
+ // user.password = encryptPassword(controller.newPassword.text.trim());
+ print(user.fullName);
+ user.password = controller.newPassword.text.trim();
+ UserRepository.instance.updateUser(user);
+ Get.offAll(() => const LoginScreen());
+ }
+ },
+ child: const Text("Reset")
+ ),
+ )
+ ],
+ )
+ )
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ String encryptPassword(String pwd) {
+ return DBCrypt().hashpw(pwd, DBCrypt().gensalt());
+ }
+
+
+// Future passwordReset() async{
+// try{
+// await FirebaseAuth.instance.sendPasswordResetEmail(email: controller.text.trim());
+// showDialog(
+// context: context,
+// builder: (context){
+// return AlertDialog(
+// content: Text("An email with a link to reset your password has been sent to your account"),
+// );
+// },
+// );
+// }on FirebaseAuthException catch (e){
+// showDialog(
+// context: context,
+// builder: (context){
+// return AlertDialog(content: Text(e.message.toString()),
+// );
+// },
+// );
+// }
+// }
+}
\ No newline at end of file
diff --git a/ride_share/lib/src/features/authentication/screens/login/login_form_widget.dart b/ride_share/lib/src/features/authentication/screens/login/login_form_widget.dart
index 2d990a4..b5cde54 100644
--- a/ride_share/lib/src/features/authentication/screens/login/login_form_widget.dart
+++ b/ride_share/lib/src/features/authentication/screens/login/login_form_widget.dart
@@ -3,9 +3,12 @@ import 'package:get/get.dart';
import 'package:line_awesome_flutter/line_awesome_flutter.dart';
import 'package:ride_share/src/constants/colors.dart';
import 'package:ride_share/src/features/authentication/screens/forgot_pwd/forgot_password_bottom_modal.dart';
+import 'package:ride_share/src/features/home/screens/home_screen.dart';
+import 'package:ride_share/src/features/home/screens/new_home_screen.dart';
import '../../../../constants/sizes.dart';
import '../../../../constants/text_strings.dart';
+import '../../../../repository/authentication_repository.dart';
import '../../controllers/login_controller.dart';
class LoginForm extends StatefulWidget {
@@ -35,17 +38,17 @@ class _LoginFormState extends State {
children: [
TextFormField(
cursorColor: tSecondaryColor,
- controller: controller.email,
+ controller: controller.phone,
validator: (value) {
if (value == null || value.isEmpty) {
- return '*Please enter an email address';
+ return '*Please enter your phone number';
}
return null;
},
decoration: const InputDecoration(
- prefixIcon: Icon(Icons.email_outlined),
- labelText: tEmail,
- hintText: tEmail,
+ prefixIcon: Icon(Icons.phone),
+ labelText: tPhoneNo,
+ hintText: tPhoneHintText,
border: OutlineInputBorder()),
),
const SizedBox(
@@ -63,34 +66,36 @@ class _LoginFormState extends State {
obscureText: obscurePassword,
autocorrect: false,
decoration: InputDecoration(
- prefixIcon: const Icon(Icons.password_outlined),
- labelText: tPassword,
- hintText: tPassword,
- border: const OutlineInputBorder(),
- suffixIcon: IconButton(
- icon: Icon(obscurePassword
- ? LineAwesomeIcons.eye
- : LineAwesomeIcons.eye_slash),
- color: Colors.grey,
- onPressed: () {
- setState(() {
- obscurePassword = !obscurePassword;
- });
- },
- )),
+ prefixIcon: const Icon(Icons.password_outlined),
+ labelText: tPassword,
+ hintText: tPassword,
+ border: const OutlineInputBorder(),
+ suffixIcon: IconButton(
+ icon: Icon(obscurePassword
+ ? LineAwesomeIcons.eye
+ : LineAwesomeIcons.eye_slash),
+ color: Colors.grey,
+ onPressed: () {
+ setState(() {
+ obscurePassword = !obscurePassword;
+ });
+ },
+ ),
+ ),
),
const SizedBox(
height: tFormHeight,
),
Visibility(
- visible: invalidCredentials,
- maintainSize: true,
- maintainAnimation: true,
- maintainState: true,
- child: const Text(
- "Invalid Credentials. Please try again.",
- style: TextStyle(color: Colors.red),
- )),
+ visible: invalidCredentials,
+ maintainSize: true,
+ maintainAnimation: true,
+ maintainState: true,
+ child: const Text(
+ "Invalid Credentials. Please try again.",
+ style: TextStyle(color: Colors.red),
+ ),
+ ),
SizedBox(
width: double.infinity,
child: ElevatedButton(
@@ -99,21 +104,28 @@ class _LoginFormState extends State {
borderRadius: BorderRadius.circular(7.0))),
),
onPressed: () async {
- // if (_formKey.currentState!.validate()) {
- // bool result = await AuthenticationRepository.instance
- // .loginUserWithEmailAndPassword(
- // controller.email.text.trim(),
- // controller.password.text.trim());
- // if (result == true) {
- // Get.offAll(() => const DashboardScreen());
- // } else {
- // setState(() {
- // invalidCredentials = true;
- // });
- // }
- // }
+ print("pressed");
+ if (_formKey.currentState!.validate()) {
+ bool result = await AuthenticationRepository.instance
+ .loginUserWithPhoneAndPassword(
+ controller.phone.text.trim(),
+ controller.password.text.trim());
+ if (result == true) {
+ Get.offAll(() => const NewHomeScreen());
+ } else {
+ setState(() {
+ invalidCredentials = true;
+ });
+ }
+ }
},
- child: Text(tLogin.toUpperCase(), style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500, letterSpacing: 0.5))),
+ child: Text(tLogin.toUpperCase(),
+ style: const TextStyle(
+ fontSize: 15,
+ fontWeight: FontWeight.w500,
+ letterSpacing: 0.5)
+ )
+ ),
),
Align(
alignment: Alignment.centerRight,
@@ -121,10 +133,15 @@ class _LoginFormState extends State {
onPressed: () {
ForgotPasswordModal.buildShowModalSheet(context);
},
- child: const Text(tForgotPassword, style: TextStyle(color: tSecondaryColor),)),
+ child: const Text(
+ tForgotPassword,
+ style: TextStyle(color: tSecondaryColor),
+ ),
+ ),
),
],
),
- ));
+ ),
+ );
}
-}
\ No newline at end of file
+}
diff --git a/ride_share/lib/src/features/authentication/screens/signup/signup_form_widget.dart b/ride_share/lib/src/features/authentication/screens/signup/signup_form_widget.dart
index 4faeff0..62327dc 100644
--- a/ride_share/lib/src/features/authentication/screens/signup/signup_form_widget.dart
+++ b/ride_share/lib/src/features/authentication/screens/signup/signup_form_widget.dart
@@ -1,3 +1,4 @@
+import 'package:dbcrypt/dbcrypt.dart';
import 'package:email_validator/email_validator.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
@@ -6,8 +7,8 @@ import 'package:ride_share/src/constants/text_strings.dart';
import '../../../../constants/colors.dart';
import '../../../../constants/sizes.dart';
+import '../../../../models/user_model.dart';
import '../../controllers/signup_controller.dart';
-import '../forgot_pwd/otp_screen.dart';
class SignUpFormWidget extends StatefulWidget {
const SignUpFormWidget({
@@ -20,152 +21,156 @@ class SignUpFormWidget extends StatefulWidget {
class _SignUpFormWidgetState extends State {
bool obscurePassword = true;
-
+ bool obscureCfmPassword = true;
@override
Widget build(BuildContext context) {
-
final controller = Get.put(SignUpController());
final _formKey = GlobalKey();
return Container(
padding: const EdgeInsets.symmetric(vertical: tFormHeight - 10),
child: Form(
- key: _formKey,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- TextFormField(
- cursorColor: tSecondaryColor,
- controller: controller.firstName,
- decoration: const InputDecoration(
- label: Text(tFullName),
- prefixIcon: Icon(Icons.person_outline_outlined),
- ),
- validator: (value) {
- if (value == null || value.isEmpty) {
- return '*This field is required';
- }
- return null;
- },
+ key: _formKey,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ TextFormField(
+ cursorColor: tSecondaryColor,
+ controller: controller.fullName,
+ decoration: const InputDecoration(
+ label: Text(tFullName),
+ prefixIcon: Icon(Icons.person_outline_outlined),
),
- const SizedBox(height: tFormHeight - 20),
- TextFormField(
- cursorColor: tSecondaryColor,
- controller: controller.email,
- decoration: const InputDecoration(
- label: Text(tEmail),
- prefixIcon: Icon(Icons.email_outlined),
- ),
- onFieldSubmitted: (val) {
- validateEmail(val);
- },
- validator: (email) {
- bool result = validateEmail(email!);
- if (result) {
- return null;
- }
- return '*Invalid email address';
- },
+ validator: (value) {
+ if (value == null || value.isEmpty) {
+ return '*This field is required';
+ }
+ return null;
+ },
+ ),
+ const SizedBox(height: tFormHeight - 20),
+ TextFormField(
+ cursorColor: tSecondaryColor,
+ controller: controller.email,
+ decoration: const InputDecoration(
+ label: Text(tEmail),
+ prefixIcon: Icon(Icons.email_outlined),
),
- const SizedBox(height: tFormHeight - 20),
-
- TextFormField(
- cursorColor: tSecondaryColor,
- controller: controller.phoneNo,
- decoration: const InputDecoration(
- label: Text(tPhoneNo),
- prefixIcon: Icon(Icons.phone),
- ),
- validator: (value) {
- if (value == null || value.isEmpty) {
- return '*Phone Number is required';
- }
- return null;
- },
- ),
-
- const SizedBox(height: tFormHeight - 20),
- TextFormField(
- cursorColor: tSecondaryColor,
- controller: controller.password,
- obscureText: obscurePassword,
- autocorrect: false,
- decoration: InputDecoration(
- label: const Text(tPassword),
- prefixIcon: const Icon(Icons.password_outlined),
- suffixIcon: IconButton(
- icon: Icon(obscurePassword
- ? LineAwesomeIcons.eye
- : LineAwesomeIcons.eye_slash),
- color: Colors.grey,
- onPressed: () {
- setState(() {
- obscurePassword = !obscurePassword;
- });
- },
- )
- ),
- validator: (value) {
- if (value == null || value.isEmpty) {
- return '*This field is required';
- } else if (value.isNotEmpty) {
- bool result = validatePassword(value);
- return result ? null : 'Please use a strong password';
- }
+ onFieldSubmitted: (val) {
+ validateEmail(val);
+ },
+ validator: (email) {
+ bool result = validateEmail(email!);
+ if (result) {
return null;
- },
+ }
+ return '*Invalid email address';
+ },
+ ),
+ const SizedBox(height: tFormHeight - 20),
+ TextFormField(
+ cursorColor: tSecondaryColor,
+ controller: controller.phoneNo,
+ decoration: const InputDecoration(
+ label: Text(tPhoneNo),
+ prefixIcon: Icon(Icons.phone),
),
- const SizedBox(height: tFormHeight - 20),
- TextFormField(
- cursorColor: tSecondaryColor,
- obscureText: obscurePassword,
- autocorrect: false,
- decoration: const InputDecoration(
- label: Text(tCFMPassword),
- prefixIcon: Icon(Icons.gpp_good_outlined),
- ),
- validator: (value) {
- if (value == null || value.isEmpty) {
- return '*Please Confirm Your Password';
- } else if (value != controller.password.text.trim()) {
- return 'Passwords do not Match';
- }
- return null;
- },
+ validator: (value) {
+ if (value == null || value.isEmpty) {
+ return '*Phone Number is required';
+ }
+ return null;
+ },
+ ),
+ const SizedBox(height: tFormHeight - 20),
+ TextFormField(
+ cursorColor: tSecondaryColor,
+ controller: controller.password,
+ obscureText: obscurePassword,
+ autocorrect: false,
+ decoration: InputDecoration(
+ label: const Text(tPassword),
+ prefixIcon: const Icon(Icons.password_outlined),
+ suffixIcon: IconButton(
+ icon: Icon(obscurePassword
+ ? LineAwesomeIcons.eye
+ : LineAwesomeIcons.eye_slash),
+ color: Colors.grey,
+ onPressed: () {
+ setState(() {
+ obscurePassword = !obscurePassword;
+ });
+ },
+ )
),
- const SizedBox(height: tFormHeight - 10),
- SizedBox(
- width: double.infinity,
- child: ElevatedButton(
- style: ButtonStyle(
- shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(7.0))),
- ),
+ validator: (value) {
+ if (value == null || value.isEmpty) {
+ return '*This field is required';
+ } else if (value.isNotEmpty) {
+ bool result = validatePassword(value);
+ return result ? null : 'Please use a strong password';
+ }
+ return null;
+ },
+ ),
+ const SizedBox(height: tFormHeight - 20),
+ TextFormField(
+ cursorColor: tSecondaryColor,
+ obscureText: obscurePassword,
+ autocorrect: false,
+ enableInteractiveSelection: false,
+ decoration: InputDecoration(
+ label: const Text(tCFMPassword),
+ prefixIcon: const Icon(Icons.gpp_good_outlined),
+ suffixIcon: IconButton(
+ icon: Icon(obscureCfmPassword
+ ? LineAwesomeIcons.eye
+ : LineAwesomeIcons.eye_slash),
+ color: Colors.grey,
onPressed: () {
- Get.to(() => OTPScreen(phoneNo: controller.phoneNo.text.trim()));
- // Get.to(() => const OTPScreen());
- // if (_formKey.currentState!.validate()) {
- // SignUpController.instance.phoneAuthentication(controller.phoneNo.text.trim());
- // Get.to(() => const OTPScreen());
- // // var now = DateTime.now();
- // // var formatter = DateFormat('dd-MM-yyyy');
- // // String formattedDate = formatter.format(now);
- // //
- // // final user = UserModel(
- // // email: controller.email.text.trim(),
- // // password: controller.password.text.trim(),
- // // fullName: controller.fullName.text.trim(),
- // // phoneVerified: false,
- // // joinedOn: formattedDate,
- // // );
- // //
- // // SignUpController.instance.createUser(user);
- // }
+ setState(() {
+ obscureCfmPassword = !obscureCfmPassword;
+ });
},
- child: Text(tSignup.toUpperCase())),
+ )
),
- ],
- )),
+ validator: (value) {
+ if (value == null || value.isEmpty) {
+ return '*Please Confirm Your Password';
+ } else if (value != controller.password.text.trim()) {
+ return 'Passwords do not Match';
+ }
+ return null;
+ },
+ ),
+ const SizedBox(height: tFormHeight - 10),
+ SizedBox(
+ width: double.infinity,
+ child: ElevatedButton(
+ style: ButtonStyle(
+ shape: MaterialStateProperty.all(RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(7.0))),
+ ),
+ onPressed: () {
+ if (_formKey.currentState!.validate()) {
+ final user = UserModel(
+ email: controller.email.text.trim(),
+ password: controller.password.text.trim(),
+ // encryptPassword(controller.password.text.trim()),
+ fullName: controller.fullName.text.trim(),
+ emailVerified: false,
+ phoneNo: controller.phoneNo.text.trim(),
+ );
+
+ SignUpController.instance.createUser(user);
+ }
+ },
+ child: Text(tSignup.toUpperCase())),
+ ),
+ ],
+ ),
+ ),
);
}
@@ -179,10 +184,14 @@ class _SignUpFormWidgetState extends State {
}
}
+ String encryptPassword(String pwd) {
+ return DBCrypt().hashpw(pwd, DBCrypt().gensalt());
+ }
+
bool validatePassword(String password) {
String pattern =
r'^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[!@#\$&*~]).{8,}$';
RegExp regExp = RegExp(pattern);
return regExp.hasMatch(password);
}
-}
\ No newline at end of file
+}
diff --git a/ride_share/lib/src/features/authentication/screens/welcome/welcome_screen.dart b/ride_share/lib/src/features/authentication/screens/welcome/welcome_screen.dart
index 2fbf39b..48a98a9 100644
--- a/ride_share/lib/src/features/authentication/screens/welcome/welcome_screen.dart
+++ b/ride_share/lib/src/features/authentication/screens/welcome/welcome_screen.dart
@@ -29,14 +29,14 @@ class WelcomeScreen extends StatelessWidget{
RichText(
text: TextSpan(
text: "Welcome to ",
- style: TextStyle(fontFamily: GoogleFonts.montserrat().fontFamily, fontWeight: FontWeight.bold, fontSize: 30.0, color: isDarkMode ? tWhiteColor : tBlackColor,),
+ style: TextStyle(fontFamily: GoogleFonts.montserrat().fontFamily, fontWeight: FontWeight.bold, fontSize: 25.0, color: isDarkMode ? tWhiteColor : tBlackColor,),
children: const [
TextSpan(text: "Ride"),
TextSpan(text: "Share", style: TextStyle(color: tSecondaryColor)),
],
),
),
- Text(tWelcomeSubtitle, style: Theme.of(context).textTheme.bodyLarge, textAlign: TextAlign.center,),
+ Text(tWelcomeSubtitle, style: Theme.of(context).textTheme.bodyMedium, textAlign: TextAlign.center,),
],
),
SizedBox(
diff --git a/ride_share/lib/src/features/home/controllers/map_controller.dart b/ride_share/lib/src/features/home/controllers/map_controller.dart
new file mode 100644
index 0000000..7a982e1
--- /dev/null
+++ b/ride_share/lib/src/features/home/controllers/map_controller.dart
@@ -0,0 +1,26 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter_google_places/flutter_google_places.dart';
+import 'package:get/get.dart';
+import 'package:google_maps_webservice/places.dart';
+
+import '../../../../auth/secrets.dart';
+
+class MapController extends GetxController{
+ Future showGoogleAutoComplete(BuildContext context) async {
+ Prediction? p = await PlacesAutocomplete.show(
+ offset: 0,
+ radius: 1000,
+ strictbounds: false,
+ region: "ke",
+ language: "en",
+ context: context,
+ mode: Mode.overlay,
+ apiKey: mapsAPIKey,
+ components: [Component(Component.country, "ke")],
+ types: [],
+ hint: "Search City",
+ );
+
+ return p;
+ }
+}
\ No newline at end of file
diff --git a/ride_share/lib/src/features/home/controllers/profile_controller.dart b/ride_share/lib/src/features/home/controllers/profile_controller.dart
new file mode 100644
index 0000000..b1aeeb0
--- /dev/null
+++ b/ride_share/lib/src/features/home/controllers/profile_controller.dart
@@ -0,0 +1,32 @@
+import 'package:flutter/foundation.dart';
+import 'package:get/get.dart';
+import 'package:ride_share/src/features/authentication/screens/login/login_screen.dart';
+
+import '../../../models/user_model.dart';
+import '../../../repository/authentication_repository.dart';
+import '../../../repository/user_repository.dart';
+
+class ProfileController extends GetxController {
+ static ProfileController get instance => Get.find();
+
+ final _authRepo = Get.put(AuthenticationRepository());
+ final _userRepo = Get.put(UserRepository());
+
+ getUserData() {
+ final phone = _authRepo.firebaseUser.value?.phoneNumber;
+
+ if (phone != null) {
+ if (kDebugMode) {
+ print(_authRepo.firebaseUser.value);
+ }
+ return _userRepo.getUserDetails(phone);
+ } else {
+ Get.snackbar("Error", "Login to continue");
+ Get.to(() => const LoginScreen());
+ }
+ }
+
+ updateRecord(UserModel user) async{
+ await _userRepo.updateUser(user);
+ }
+}
\ No newline at end of file
diff --git a/ride_share/lib/src/features/home/controllers/ride_controller.dart b/ride_share/lib/src/features/home/controllers/ride_controller.dart
new file mode 100644
index 0000000..7a797c5
--- /dev/null
+++ b/ride_share/lib/src/features/home/controllers/ride_controller.dart
@@ -0,0 +1,23 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:ride_share/src/features/home/screens/home_screen.dart';
+import 'package:ride_share/src/features/home/screens/new_home_screen.dart';
+import 'package:ride_share/src/models/offered_ride_model.dart';
+import 'package:ride_share/src/repository/ride_repository.dart';
+
+class RideController extends GetxController{
+ static RideController get instance => Get.find();
+
+ final rideRepo = Get.put(RideRepository());
+
+ final vehicleMake = TextEditingController();
+ final vehicleModel = TextEditingController();
+ final numberPlate = TextEditingController();
+ final vehicleColor = TextEditingController();
+ final seatsAvailable = TextEditingController();
+
+ Future addRide(OfferedRideModel ride) async {
+ await rideRepo.addRide(ride);
+ Get.to(() => const NewHomeScreen());
+ }
+}
\ No newline at end of file
diff --git a/ride_share/lib/src/features/home/screens/home_screen.dart b/ride_share/lib/src/features/home/screens/home_screen.dart
new file mode 100644
index 0000000..06a711f
--- /dev/null
+++ b/ride_share/lib/src/features/home/screens/home_screen.dart
@@ -0,0 +1,197 @@
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:google_maps_flutter/google_maps_flutter.dart';
+import 'package:ride_share/src/common_widgets/nav_drawer.dart';
+import 'package:ride_share/src/constants/text_strings.dart';
+import 'package:ride_share/src/features/home/screens/ride/offer_ride_screen.dart';
+
+import '../../../constants/colors.dart';
+
+List histories = [
+ {'id': 1, 'name': 'Balozi Estate', 'city': 'Nairobi'},
+ {'id': 2, 'name': 'Ridgeways Country Homes', 'city': 'Ridgeways Rd, Nairobi'},
+ {'id': 3, 'name': 'Chalbi Court', 'city': 'Nairobi'}
+];
+
+class HomeScreen extends StatefulWidget {
+ const HomeScreen({super.key});
+
+ @override
+ State createState() => _HomeScreenState();
+}
+
+class _HomeScreenState extends State {
+ late GoogleMapController mapController;
+
+ final LatLng _center = const LatLng(-1.3093299, 36.8099464);
+
+ List options = [Text('Offer'), Text('Request')];
+
+ void _onMapCreated(GoogleMapController controller) {
+ mapController = controller;
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final List _selectedOptions = [false, true];
+ bool vertical = false;
+
+ return MaterialApp(
+ debugShowCheckedModeBanner: false,
+ theme: ThemeData(
+ useMaterial3: true,
+ colorSchemeSeed: Colors.green[700],
+ ),
+ home: Scaffold(
+ drawer: NavDrawer(),
+ extendBodyBehindAppBar: true,
+ appBar: AppBar(
+ backgroundColor: Colors.transparent,
+ elevation: 0,
+ iconTheme: const IconThemeData(size: 45.0, weight: 7, fill: 1, color: Colors.black),
+ actions: [
+ ElevatedButton(onPressed: (){Get.to(() => const OfferRideScreen());}, child: Text("Offer")),
+ // TextButton(
+ // onPressed: () {
+ // Get.to(() => const OfferRideScreen());
+ // },
+ // child: Row(
+ // children: [
+ // Text("Offer"),
+ // ],
+ // ),
+ // ),
+ ],
+ ),
+ body: Stack(
+ children: [
+ LayoutBuilder(
+ builder: (BuildContext context, BoxConstraints constraints) {
+ return SizedBox(
+ height: constraints.maxHeight / 2,
+ child: GoogleMap(
+ onMapCreated: _onMapCreated,
+ initialCameraPosition: CameraPosition(
+ target: _center,
+ zoom: 11.0,
+ ),
+ ),
+ );
+ },
+ ),
+ // Row(
+ // crossAxisAlignment: CrossAxisAlignment.center,
+ // mainAxisAlignment: MainAxisAlignment.center,
+ // children: [
+ // Container(
+ // padding: EdgeInsets.zero,
+ // decoration: BoxDecoration(
+ // color: Colors.white,
+ // border: Border.all(color: Colors.black, width: 1.0),
+ // borderRadius: BorderRadius.all(Radius.circular(5.0)),
+ // ),
+ // child: TextButton(
+ // onPressed: () {
+ // Get.to(() => const OfferRideScreen());
+ // },
+ // child: Row(
+ // children: [
+ // Text("Offer"),
+ // ],
+ // ),
+ // ),
+ // ),
+ // ],
+ // ),
+
+ // Positioned(
+ // top: 50,
+ // left: 10,
+ // child: Padding(
+ // padding: const EdgeInsets.all(7.0),
+ // child: Align(
+ // alignment: Alignment.topLeft,
+ // child: FloatingActionButton(
+ // onPressed: () => NavDrawer(),
+ // materialTapTargetSize: MaterialTapTargetSize.padded,
+ // backgroundColor: Colors.white,
+ // child: const Icon(Icons.menu, size: 30.0),
+ // ),
+ // ),
+ // ),
+ // ),
+ Align(
+ alignment: const Alignment(0.9, 0.75),
+ child: RawMaterialButton(
+ onPressed: () {},
+ elevation: 2.0,
+ fillColor: Colors.white,
+ padding: const EdgeInsets.all(7.0),
+ shape: const CircleBorder(),
+ child: const Icon(
+ Icons.my_location,
+ size: 30.0,
+ ),
+ ),
+ ),
+ DraggableScrollableSheet(
+ initialChildSize: 0.5,
+ minChildSize: 0.5,
+ maxChildSize: 1,
+ snapSizes: const [0.5, 1],
+ snap: true,
+ builder:
+ (BuildContext context, ScrollController scrollController) {
+ return Container(
+ color: Colors.white,
+ child: ListView.builder(
+ physics: const ClampingScrollPhysics(),
+ controller: scrollController,
+ itemCount: histories.length,
+ itemBuilder: (BuildContext context, int index) {
+ final history = histories[index];
+ if (index == 0) {
+ return Padding(
+ padding: const EdgeInsets.all(3),
+ child: Column(
+ children: [
+ const SizedBox(
+ width: 50,
+ child: Divider(
+ thickness: 5,
+ ),
+ ),
+ TextFormField(
+ cursorColor: tSecondaryColor,
+ decoration: const InputDecoration(
+ hintText: tPickUpLocation,
+ border: OutlineInputBorder()),
+ ),
+ ],
+ ),
+ );
+ }
+ return Card(
+ margin: EdgeInsets.zero,
+ color: Colors.white,
+ elevation: 0,
+ child: ListTile(
+ contentPadding: EdgeInsets.all(5),
+ onTap: () {},
+ leading: Icon(Icons.access_time),
+ title: Text(history['name']),
+ subtitle: Text(history['city']),
+ ),
+ );
+ }),
+ );
+ },
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/ride_share/lib/src/features/home/screens/new_home_screen.dart b/ride_share/lib/src/features/home/screens/new_home_screen.dart
new file mode 100644
index 0000000..1ada33a
--- /dev/null
+++ b/ride_share/lib/src/features/home/screens/new_home_screen.dart
@@ -0,0 +1,929 @@
+import 'package:flutter/material.dart';
+
+import 'package:get/get.dart';
+import 'package:google_fonts/google_fonts.dart';
+import 'package:google_maps_flutter/google_maps_flutter.dart';
+import 'package:flutter/services.dart' show rootBundle;
+
+import 'package:geocoding/geocoding.dart' as geoCoding;
+import 'package:google_maps_webservice/places.dart';
+import 'dart:ui' as ui;
+
+import 'package:ride_share/src/constants/colors.dart';
+import 'package:ride_share/src/constants/text_strings.dart';
+import 'package:ride_share/src/features/home/controllers/map_controller.dart';
+import 'package:ride_share/src/features/home/screens/profile/edit_profile.dart';
+import 'package:ride_share/src/features/home/screens/requests/ride_request.dart';
+import 'package:ride_share/src/features/home/screens/ride/ride_registration.dart';
+import 'package:ride_share/src/features/home/screens/schedule/user_schedule.dart';
+import 'package:ride_share/src/models/ride_request_model.dart';
+import 'package:ride_share/src/repository/ride_repository.dart';
+
+import '../../../common_widgets/text_widget.dart';
+import '../../../constants/image_strings.dart';
+import '../../../models/offered_ride_model.dart';
+
+class NewHomeScreen extends StatefulWidget {
+ const NewHomeScreen({Key? key}) : super(key: key);
+
+ @override
+ State createState() => _NewHomeScreenState();
+}
+
+class CarInfo {
+ final String name;
+ final String model;
+ final double cash;
+ final String to;
+
+ CarInfo(this.name, this.model, this.cash, this.to);
+}
+
+var items = [
+ CarInfo('Nathan Mbugua', "Toyota Mark X", 350, "Makini School"),
+ CarInfo('Nathan Mbugua', "Toyota Mark X", 350, "Makini School"),
+ CarInfo('Wayne Asava', "Toyota Vitz", 350, "Starthmore University"),
+];
+
+class _NewHomeScreenState extends State {
+ final rideRepository = Get.put(RideRepository());
+ MapController mapController = Get.put(MapController());
+
+ late LatLng source;
+ late LatLng destination;
+ final Set polyline = {};
+ Set markers = Set();
+
+ @override
+ void initState() {
+ super.initState();
+
+ // loadCustomMarker();
+ }
+
+ // String dropdownValue = '**** **** **** 8789';
+ final CameraPosition _kGooglePlex = const CameraPosition(
+ target: LatLng(-1.3093299, 36.8099464),
+ zoom: 10,
+ );
+
+ GoogleMapController? myMapController;
+
+ @override
+ Widget build(BuildContext context) {
+
+ return Scaffold(
+ drawer: buildDrawer(),
+ appBar: AppBar(
+ backgroundColor: tSecondaryColor,
+ elevation: 0.0,
+ ),
+ // appBar: AppBar(
+ // automaticallyImplyLeading: true,
+ // centerTitle: true,
+ // title: const Text(
+ // 'Navigation Drawer',
+ // ),
+ // backgroundColor: const Color(0xff764abc),
+ // ),
+ body: Stack(
+ children: [
+ Positioned(
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ child: GoogleMap(
+ markers: markers,
+ polylines: polyline,
+ zoomControlsEnabled: true,
+ onMapCreated: (GoogleMapController controller) {
+ myMapController = controller;
+
+ // myMapController!.setMapStyle(_mapStyle);
+ },
+ initialCameraPosition: _kGooglePlex,
+ ),
+ ),
+ buildProfileTile(),
+ buildTextField(),
+ showDestinationField ? buildTextFieldForDestination() : Container(),
+ buildCurrentLocationIcon(),
+ buildBottomSheet(),
+ ],
+ ),
+ );
+ }
+
+ Widget buildProfileTile() {
+ return Positioned(
+ top: 0,
+ left: 0,
+ right: 0,
+ child:
+ // Obx(() => authController.myUser.value.name == null
+ // ? Center(
+ // child: CircularProgressIndicator(),
+ // )
+ // :
+ Container(
+ width: Get.width,
+ height: Get.width * 0.2,
+ padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 0),
+ decoration: const BoxDecoration(color: Colors.white),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ RichText(
+ text: const TextSpan(children: [
+ TextSpan(
+ text: 'Welcome, ',
+ style: TextStyle(color: Colors.black, fontSize: 14)),
+ TextSpan(
+ text: "Nathan Mbugua",
+ style: TextStyle(
+ color: tSecondaryColor,
+ fontSize: 16,
+ fontWeight: FontWeight.bold)),
+ ]),
+ ),
+ ],
+ )
+ ],
+ ),
+ ),
+ );
+ }
+
+ TextEditingController sourceController = TextEditingController();
+ TextEditingController destinationController = TextEditingController();
+
+ bool showDestinationField = false;
+
+ Widget buildTextField() {
+ return Positioned(
+ top: 80,
+ left: 20,
+ right: 20,
+ child: Container(
+ width: Get.width,
+ height: 50,
+ padding: const EdgeInsets.only(left: 15),
+ decoration: BoxDecoration(
+ color: Colors.white,
+ boxShadow: [
+ BoxShadow(
+ color: Colors.black.withOpacity(0.05),
+ spreadRadius: 4,
+ blurRadius: 10)
+ ],
+ borderRadius: BorderRadius.circular(8)),
+ child: TextFormField(
+ controller: sourceController,
+ readOnly: true,
+ onTap: () async {
+ Prediction? p = await mapController.showGoogleAutoComplete(context);
+
+ String selectedPlace = p!.description!;
+
+ sourceController.text = selectedPlace;
+
+ List locations =
+ await geoCoding.locationFromAddress(selectedPlace);
+
+ source =
+ LatLng(locations.first.latitude, locations.first.longitude);
+
+ markers.add(Marker(
+ markerId: MarkerId(selectedPlace),
+ infoWindow: InfoWindow(
+ title: 'Destination: $selectedPlace',
+ ),
+ position: source,
+ // icon: BitmapDescriptor.fromBytes(markIcons),
+ ));
+
+ myMapController!.animateCamera(CameraUpdate.newCameraPosition(
+ CameraPosition(target: source, zoom: 14)
+ //17 is new zoom level
+ ));
+
+ // TODO build driver bottom sheet
+ buildRideConfirmationSheet();
+
+ // setState(() {
+ // showDestinationField = true;
+ // });
+ },
+ style: GoogleFonts.poppins(
+ fontSize: 15, fontWeight: FontWeight.bold, color: Colors.black54),
+ decoration: InputDecoration(
+ hintText: 'Where to?',
+ hintStyle: GoogleFonts.poppins(
+ fontSize: 14,
+ fontWeight: FontWeight.w400,
+ color: Colors.black54,
+ fontStyle: FontStyle.italic),
+ suffixIcon: const Padding(
+ padding: EdgeInsets.only(left: 10),
+ child: Icon(
+ Icons.search,
+ color: Colors.black,
+ ),
+ ),
+ border: InputBorder.none,
+ ),
+ ),
+ ),
+ );
+ }
+
+ Widget buildTextFieldForDestination() {
+ return Positioned(
+ top: 150,
+ left: 20,
+ right: 20,
+ child: Container(
+ width: Get.width,
+ height: 50,
+ padding: const EdgeInsets.only(left: 15),
+ decoration: BoxDecoration(
+ color: Colors.white,
+ boxShadow: [
+ BoxShadow(
+ color: Colors.black.withOpacity(0.05),
+ spreadRadius: 4,
+ blurRadius: 10)
+ ],
+ borderRadius: BorderRadius.circular(8)),
+ child: TextFormField(
+ controller: destinationController,
+ readOnly: true,
+ onTap: () async {
+ // buildSourceSheet();
+ },
+ style: GoogleFonts.poppins(
+ fontSize: 16,
+ fontWeight: FontWeight.bold,
+ ),
+ decoration: InputDecoration(
+ hintText: 'From:',
+ hintStyle: GoogleFonts.poppins(
+ fontSize: 16,
+ fontWeight: FontWeight.bold,
+ ),
+ suffixIcon: const Padding(
+ padding: EdgeInsets.only(left: 10),
+ child: Icon(
+ Icons.search,
+ ),
+ ),
+ border: InputBorder.none,
+ ),
+ ),
+ ),
+ );
+ }
+
+ Widget buildCurrentLocationIcon() {
+ return const Align(
+ alignment: Alignment.bottomRight,
+ child: Padding(
+ padding: EdgeInsets.only(bottom: 30, right: 8),
+ child: CircleAvatar(
+ radius: 20,
+ backgroundColor: Colors.white,
+ child: Icon(
+ Icons.my_location,
+ color: Colors.black87,
+ ),
+ ),
+ ),
+ );
+ }
+
+ Widget buildBottomSheet() {
+ return Align(
+ alignment: Alignment.bottomCenter,
+ child: Container(
+ width: Get.width,
+ height: 25,
+ decoration: BoxDecoration(
+ color: Colors.white,
+ boxShadow: [
+ BoxShadow(
+ color: Colors.black.withOpacity(0.05),
+ spreadRadius: 4,
+ blurRadius: 10)
+ ],
+ borderRadius: const BorderRadius.only(
+ topRight: Radius.circular(12), topLeft: Radius.circular(12))),
+ child: Center(
+ child: Container(
+ width: Get.width * 0.3,
+ height: 4,
+ decoration: const BoxDecoration(
+ color: Colors.black45,
+ borderRadius: BorderRadius.only(
+ topRight: Radius.circular(12),
+ topLeft: Radius.circular(12),
+ bottomLeft: Radius.circular(12),
+ bottomRight: Radius.circular(12)),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ buildDrawerItem(
+ {required String title,
+ required Function onPressed,
+ required Icon icon,
+ Color color = Colors.black,
+ double fontSize = 20,
+ FontWeight fontWeight = FontWeight.w700,
+ double height = 45,
+ bool isVisible = false}) {
+ return SizedBox(
+ height: height,
+ child: ListTile(
+ leading: icon,
+ onTap: () => onPressed(),
+ title: Row(
+ children: [
+ Text(
+ title,
+ ),
+ const SizedBox(
+ width: 5,
+ ),
+ isVisible
+ ? CircleAvatar(
+ backgroundColor: tSecondaryColor,
+ radius: 15,
+ child: Text(
+ '1',
+ style: GoogleFonts.poppins(color: Colors.white),
+ ),
+ )
+ : Container()
+ ],
+ ),
+ ),
+ );
+ }
+
+ buildDrawer() {
+ return Drawer(
+ child: Column(
+ children: [
+ InkWell(
+ onTap: () {
+ Get.to(() => EditProfileScreen());
+ },
+ child: SizedBox(
+ height: 150,
+ child: DrawerHeader(
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
+ children: [
+ Container(
+ width: 80,
+ height: 80,
+ decoration: const BoxDecoration(
+ shape: BoxShape.circle,
+ image: DecorationImage(
+ image: AssetImage(tProfilePic), fit: BoxFit.fill)
+ // : DecorationImage(
+ // image: NetworkImage(
+ // authController.myUser.value.image!),
+ // fit: BoxFit.fill)
+ ),
+ ),
+ const SizedBox(
+ width: 10,
+ ),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text('Good Morning, ',
+ style: GoogleFonts.poppins(
+ color: Colors.black.withOpacity(0.28),
+ fontSize: 14)),
+ Text(
+ // authController.myUser.value.name == null
+ // ? "Mark"
+ // : authController.myUser.value.name!,
+ "Nathan",
+ style: GoogleFonts.poppins(
+ fontSize: 24,
+ fontWeight: FontWeight.bold,
+ color: Colors.black),
+ overflow: TextOverflow.ellipsis,
+ maxLines: 1,
+ )
+ ],
+ ),
+ )
+ ],
+ )),
+ ),
+ ),
+ const SizedBox(
+ height: 20,
+ ),
+ Container(
+ padding: EdgeInsets.symmetric(horizontal: 30),
+ child: Column(
+ children: [
+ // buildDrawerItem(title: 'Payment History', onPressed: () => Get.to(()=> PaymentScreen())),
+ buildDrawerItem(
+ title: tPayment,
+ onPressed: () {},
+ icon: const Icon(Icons.wallet_outlined)),
+ buildDrawerItem(
+ title: tMySchedule,
+ onPressed: () {
+ Get.to(() => UserSchedule());
+ },
+ icon: const Icon(Icons.calendar_month)),
+ buildDrawerItem(
+ title: tTripHistory,
+ onPressed: () {},
+ icon: const Icon(Icons.history)),
+ buildDrawerItem(
+ title: tRideRequests,
+ onPressed: () {
+ Get.to(() => RideRequests());
+ },
+ icon: const Icon(Icons.people_alt_outlined)),
+
+ const Divider(
+ height: 2,
+ thickness: 1,
+ color: Colors.grey,
+ ),
+
+ buildDrawerItem(
+ title: "Offer a Ride",
+ onPressed: () {
+ Get.to(() => const RideRegistrationTemplate());
+ },
+ icon: const Icon(Icons.drive_eta)),
+
+ const Divider(
+ height: 2,
+ thickness: 1,
+ color: Colors.grey,
+ ),
+
+ buildDrawerItem(
+ title: tSupport,
+ onPressed: () {},
+ icon: const Icon(Icons.contact_support_outlined)),
+ buildDrawerItem(
+ title: tAbout,
+ onPressed: () {},
+ icon: const Icon(Icons.info_outlined)),
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+// late Uint8List markIcons;
+
+// loadCustomMarker() async {
+// markIcons = await loadAsset('assets/dest_marker.png', 100);
+// }
+
+// Future loadAsset(String path, int width) async {
+// ByteData data = await rootBundle.load(path);
+// ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(),
+// targetHeight: width);
+// ui.FrameInfo fi = await codec.getNextFrame();
+// return (await fi.image.toByteData(format: ui.ImageByteFormat.png))!
+// .buffer
+// .asUint8List();
+// }
+
+// void drawPolyline(String placeId) {
+// _polyline.clear();
+// _polyline.add(Polyline(
+// polylineId: PolylineId(placeId),
+// visible: true,
+// points: [source, destination],
+// color: AppColors.greenColor,
+// width: 5,
+// ));
+// }
+
+ void buildDestinationSheet() {
+ Get.bottomSheet(Container(
+ width: Get.width,
+ height: Get.height * 0.5,
+ padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
+ decoration: const BoxDecoration(
+ borderRadius: BorderRadius.only(
+ topLeft: Radius.circular(8), topRight: Radius.circular(8)),
+ color: Colors.white),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ const SizedBox(
+ height: 10,
+ ),
+ const Text(
+ "Select Your Location",
+ style: TextStyle(
+ color: Colors.black, fontSize: 20, fontWeight: FontWeight.bold),
+ ),
+ const SizedBox(
+ height: 20,
+ ),
+ const Text(
+ "Home Address",
+ style: TextStyle(
+ color: Colors.black, fontSize: 16, fontWeight: FontWeight.bold),
+ ),
+ const SizedBox(
+ height: 10,
+ ),
+ InkWell(
+ onTap: () async {
+ Get.back();
+ // source = authController.myUser.value.homeAddress!;
+ destination = LatLng(-1.22233, 36.9187);
+ // sourceController.text = authController.myUser.value.hAddress!;
+ destinationController.text = "Home";
+
+ if (markers.length >= 2) {
+ markers.remove(markers.last);
+ }
+ markers.add(Marker(
+ // markerId: MarkerId(authController.myUser.value.hAddress!),
+ markerId: MarkerId("Home"),
+ infoWindow: InfoWindow(
+ // title: 'Source: ${authController.myUser.value.hAddress!}',
+ title: 'Source: Home}',
+ ),
+ position: destination));
+
+ // await getPolylines(source, destination);
+
+ // drawPolyline(place);
+
+ myMapController!.animateCamera(CameraUpdate.newCameraPosition(
+ CameraPosition(target: destination, zoom: 14)));
+ setState(() {});
+
+ buildRideConfirmationSheet();
+ },
+ child: Container(
+ width: Get.width,
+ height: 50,
+ padding: EdgeInsets.symmetric(horizontal: 10),
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(8),
+ boxShadow: [
+ BoxShadow(
+ color: Colors.black.withOpacity(0.04),
+ spreadRadius: 4,
+ blurRadius: 10)
+ ]),
+ child: Row(
+ children: const [
+ Text(
+ // authController.myUser.value.hAddress!,
+ "Home",
+ style: TextStyle(
+ color: Colors.black,
+ fontSize: 12,
+ fontWeight: FontWeight.w600),
+ textAlign: TextAlign.start,
+ ),
+ ],
+ ),
+ ),
+ ),
+ const SizedBox(
+ height: 20,
+ ),
+ const Text(
+ "Business Address",
+ style: TextStyle(
+ color: Colors.black, fontSize: 16, fontWeight: FontWeight.bold),
+ ),
+ const SizedBox(
+ height: 10,
+ ),
+ InkWell(
+ onTap: () async {
+ Get.back();
+ // source = authController.myUser.value.bussinessAddres!;
+ destination = LatLng(-1.23434, 34.272753);
+ // sourceController.text = authController.myUser.value.bAddress!;
+ destinationController.text = "Home";
+
+ if (markers.length >= 2) {
+ markers.remove(markers.last);
+ }
+ markers.add(Marker(
+ // markerId: MarkerId(authController.myUser.value.bAddress!),
+ markerId: MarkerId("hnb"),
+ infoWindow: InfoWindow(
+ // title: 'Source: ${authController.myUser.value.bAddress!}',
+ title: 'Source: ',
+ ),
+ position: destination));
+
+ // await getPolylines(source, destination);
+
+ // drawPolyline(place);
+
+ myMapController!.animateCamera(CameraUpdate.newCameraPosition(
+ CameraPosition(target: destination, zoom: 14)));
+ setState(() {});
+
+ buildRideConfirmationSheet();
+ },
+ child: Container(
+ width: Get.width,
+ height: 50,
+ padding: EdgeInsets.symmetric(horizontal: 10),
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(8),
+ boxShadow: [
+ BoxShadow(
+ color: Colors.black.withOpacity(0.04),
+ spreadRadius: 4,
+ blurRadius: 10)
+ ]),
+ child: Row(
+ children: [
+ Text(
+ // authController.myUser.value.bAddress!,
+ "HOME",
+ style: const TextStyle(
+ color: Colors.black,
+ fontSize: 12,
+ fontWeight: FontWeight.w600),
+ textAlign: TextAlign.start,
+ ),
+ ],
+ ),
+ ),
+ ),
+ const SizedBox(
+ height: 20,
+ ),
+ InkWell(
+ onTap: () async {
+ Get.back();
+ Prediction? p =
+ await mapController.showGoogleAutoComplete(context);
+
+ String place = p!.description!;
+
+ destinationController.text = place;
+
+ // source = await authController.buildLatLngFromAddress(place);
+
+ if (markers.length >= 2) {
+ markers.remove(markers.last);
+ }
+ markers.add(Marker(
+ markerId: MarkerId(place),
+ infoWindow: InfoWindow(
+ title: 'Source: $place',
+ ),
+ position: destination));
+
+ // await getPolylines(source, destination);
+
+ // drawPolyline(place);
+
+ myMapController!.animateCamera(CameraUpdate.newCameraPosition(
+ CameraPosition(target: destination, zoom: 14)));
+ setState(() {});
+ buildRideConfirmationSheet();
+ },
+ child: Container(
+ width: Get.width,
+ height: 50,
+ padding: const EdgeInsets.symmetric(horizontal: 10),
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(8),
+ boxShadow: [
+ BoxShadow(
+ color: Colors.black.withOpacity(0.04),
+ spreadRadius: 4,
+ blurRadius: 10)
+ ]),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: const [
+ Text(
+ "Search for Address",
+ style: TextStyle(
+ color: Colors.black,
+ fontSize: 12,
+ fontWeight: FontWeight.w600),
+ textAlign: TextAlign.start,
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ ));
+ }
+
+ buildRideConfirmationSheet() {
+ Get.bottomSheet(Container(
+ width: Get.width,
+ height: Get.height * 0.5,
+ padding: EdgeInsets.only(left: 20),
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.only(
+ topRight: Radius.circular(12), topLeft: Radius.circular(12)),
+ ),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const SizedBox(
+ height: 10,
+ ),
+ Center(
+ child: Container(
+ width: Get.width * 0.2,
+ height: 8,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(8), color: Colors.grey),
+ ),
+ ),
+ const SizedBox(
+ height: 20,
+ ),
+ textWidget(
+ text: 'We found the following matches',
+ fontSize: 18,
+ fontWeight: FontWeight.bold),
+ textWidget(
+ text: 'Select an option:',
+ fontSize: 16,
+ fontWeight: FontWeight.normal),
+ const SizedBox(
+ height: 20,
+ ),
+ buildDriversList(),
+ ],
+ ),
+ ));
+ }
+
+ int selectedRide = 0;
+
+ buildDriversList() {
+ return Container(
+ height: 250,
+ width: Get.width,
+ // child: FutureBuilder>(
+ // future: rideRepository.allRides(),
+ // builder: (context, snapshot) {
+ // if (snapshot.connectionState == ConnectionState.done) {
+ // if (snapshot.hasData) {
+ // return ListView.builder(
+ // itemBuilder: (ctx, i) {
+ // return InkWell(
+ // onTap: () {},
+ // child: buildDriverCard(snapshot.data!, i),
+ // );
+ // },
+ // itemCount: snapshot.data!.length,
+ // scrollDirection: Axis.horizontal,
+ // );
+ // }
+ // }
+ // return Text("no data");
+ // },
+ // ),
+
+ child: StatefulBuilder(builder: (context, set) {
+ return ListView.builder(
+ itemBuilder: (ctx, i) {
+ return InkWell(
+ onTap: () {
+ set(() {
+ selectedRide = i;
+ });
+ },
+ child: buildDriverCard(selectedRide == i),
+ );
+ },
+ itemCount: 5,
+ scrollDirection: Axis.horizontal,
+ );
+ }
+ ),
+ );
+ }
+
+ buildDriverCard(bool selected) {
+ return Container(
+ margin: EdgeInsets.only(right: 8, left: 8, top: 4, bottom: 4),
+ height: 170,
+ width: 235,
+ decoration: BoxDecoration(boxShadow: const [
+ BoxShadow(
+ color: tSecondaryColor,
+ offset: Offset(0, 5),
+ blurRadius: 5,
+ spreadRadius: 1)
+ ], borderRadius: BorderRadius.circular(12), color: tSecondaryColor),
+ child: Stack(
+ children: [
+ Container(
+ padding: EdgeInsets.only(left: 10, top: 10, bottom: 10, right: 10),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ textWidget(
+ text:"Nathan Mbugua",
+ color: Colors.white,
+ fontWeight: FontWeight.w700),
+ textWidget(
+ text:
+ "Toyota Mark X",
+ color: Colors.white,
+ fontWeight: FontWeight.w500),
+ SizedBox(
+ child: Row(
+ children: [
+ Column(
+ children: [
+ Text("Days"),
+ Text("Mon, Tue"),
+ ],
+ ),
+ Column(
+ children: [
+ Text("Seats"),
+ Text("4"),
+ ],
+ ),
+ Column(
+ children: [
+ Text("Pay"),
+ Text("350"),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ElevatedButton(
+ onPressed: () {
+ // final rideRequest = RideRequestModel(
+ // rideId: rides[index].id!,
+ // ownerId: rides[index].userId,
+ // requestedByUser: rides[index].userId,
+ // pickupLocation: 'Strathmore University, Ole Sangale Rd',
+ // seatsAvailable: rides[index].seatsAvailable,
+ // similarityScore: 1.0,
+ // acceptedStatus: false);
+ // await rideRepository.makeRideRequest(rideRequest);
+ // TODO get to success page or show modal and return
+
+ },
+ child: Text("REQUEST"))
+ ],
+ ),
+ ),
+ // ElevatedButton(onPressed: () {}, child: Text("Request"))
+ // Positioned(
+ // right: -20,
+ // top: 0,
+ // bottom: 0,
+ // child: Image.asset('assets/Mask Group 2.png'))
+ ],
+ ),
+ );
+ }
+}
diff --git a/ride_share/lib/src/features/home/screens/profile/edit_profile.dart b/ride_share/lib/src/features/home/screens/profile/edit_profile.dart
new file mode 100644
index 0000000..42c2fbb
--- /dev/null
+++ b/ride_share/lib/src/features/home/screens/profile/edit_profile.dart
@@ -0,0 +1,185 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:get/get.dart';
+import 'package:line_awesome_flutter/line_awesome_flutter.dart';
+import 'package:local_auth/local_auth.dart';
+
+import '../../../../constants/colors.dart';
+import '../../../../constants/image_strings.dart';
+import '../../../../constants/sizes.dart';
+import '../../../../constants/text_strings.dart';
+import '../../../../models/user_model.dart';
+import '../../../../repository/authentication_repository.dart';
+import '../../controllers/profile_controller.dart';
+
+class EditProfileScreen extends StatelessWidget {
+ EditProfileScreen({super.key});
+
+ final _authRepo = Get.put(AuthenticationRepository());
+
+ late final LocalAuthentication auth;
+
+ bool _supportState = false;
+
+ @override
+ Widget build(BuildContext context) {
+ final controller = Get.put(ProfileController());
+ var isDark = MediaQuery.of(context).platformBrightness == Brightness.dark;
+
+ return Scaffold(
+ appBar: AppBar(
+ leading: IconButton(
+ onPressed: () => Get.back(),
+ icon: const Icon(LineAwesomeIcons.angle_left)),
+ title: Text(tEditProfile,
+ style: Theme.of(context).textTheme.headlineMedium),
+ ),
+ body: SingleChildScrollView(
+ child: Container(
+ padding: const EdgeInsets.all(tDefaultSize),
+ child: FutureBuilder(
+ future: controller.getUserData(),
+ builder: (context, snapshot) {
+ if (snapshot.connectionState == ConnectionState.done) {
+ if (snapshot.hasData) {
+ UserModel userModel = snapshot.data as UserModel;
+
+ final fullName =
+ TextEditingController(text: userModel.fullName);
+ final email = TextEditingController(text: userModel.email);
+ final phoneNo =
+ TextEditingController(text: userModel.phoneNo);
+ final password =
+ TextEditingController(text: userModel.password);
+
+ return Column(
+ children: [
+ Stack(
+ children: [
+ SizedBox(
+ width: 120,
+ height: 120,
+ child: ClipRRect(
+ borderRadius: BorderRadius.circular(100),
+ child:
+ const Image(image: AssetImage(tProfilePic)),
+ ),
+ ),
+ Positioned(
+ bottom: 0,
+ right: 0,
+ child: Container(
+ width: 35,
+ height: 35,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(100),
+ color: tSecondaryColor,
+ ),
+ child: const Icon(LineAwesomeIcons.camera,
+ size: 20.0, color: Colors.black),
+ ),
+ )
+ ],
+ ),
+ const SizedBox(height: 50),
+ Form(
+ child: Column(
+ children: [
+ TextFormField(
+ controller: fullName,
+ decoration: const InputDecoration(
+ label: Text(tFullName),
+ prefixIcon: Icon(LineAwesomeIcons.user),
+ ),
+ ),
+ const SizedBox(height: tFormHeight - 20),
+ TextFormField(
+ controller: email,
+ decoration: const InputDecoration(
+ label: Text(tEmail),
+ prefixIcon: Icon(LineAwesomeIcons.envelope_1),
+ ),
+ ),
+ const SizedBox(height: tFormHeight - 20),
+ TextFormField(
+ controller: phoneNo,
+ decoration: const InputDecoration(
+ label: Text(tPhoneNo),
+ prefixIcon: Icon(LineAwesomeIcons.phone),
+ ),
+ ),
+ const SizedBox(height: tFormHeight - 20),
+ TextFormField(
+ controller: password,
+ obscureText: true,
+ decoration: InputDecoration(
+ label: const Text(tPassword),
+ prefixIcon:
+ const Icon(Icons.fingerprint_outlined),
+ suffixIcon: IconButton(
+ icon:
+ const Icon(LineAwesomeIcons.eye_slash),
+ onPressed: () {})),
+ ),
+ const SizedBox(height: tFormHeight),
+ SizedBox(
+ width: double.infinity,
+ child: ElevatedButton(
+ onPressed: () async {
+ // if(await _authenticate()){
+ final userData = UserModel(
+ email: email.text.trim(),
+ password: password.text.trim(),
+ fullName: fullName.text.trim(),
+ phoneNo: phoneNo.text.trim(),
+ emailVerified: _authRepo
+ .firebaseUser.value?.emailVerified,
+ );
+
+ await controller.updateRecord(userData);
+ // }
+ },
+ style: ElevatedButton.styleFrom(
+ backgroundColor: tPrimaryColor,
+ side: BorderSide.none,
+ shape: const StadiumBorder()),
+ child: const Text(tEditProfile,
+ style: TextStyle(color: tDarkColor)),
+ ),
+ ),
+ const SizedBox(height: tFormHeight),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ ElevatedButton(
+ onPressed: () {},
+ style: ElevatedButton.styleFrom(
+ backgroundColor:
+ Colors.redAccent.withOpacity(0.1),
+ elevation: 0,
+ foregroundColor: Colors.red,
+ shape: const StadiumBorder(),
+ side: BorderSide.none),
+ child: const Text(tDeleteAcc),
+ ),
+ ],
+ )
+ ],
+ ))
+ ],
+ );
+ } else if (snapshot.hasError) {
+ return Center(child: Text(snapshot.error.toString()));
+ } else {
+ return const Center(child: Text("Something went wrong"));
+ }
+ } else {
+ return const Center(child: CircularProgressIndicator());
+ }
+ },
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/ride_share/lib/src/features/home/screens/profile/profile_screen.dart b/ride_share/lib/src/features/home/screens/profile/profile_screen.dart
new file mode 100644
index 0000000..257207f
--- /dev/null
+++ b/ride_share/lib/src/features/home/screens/profile/profile_screen.dart
@@ -0,0 +1,147 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:line_awesome_flutter/line_awesome_flutter.dart';
+import 'package:ride_share/src/features/home/screens/profile/edit_profile.dart';
+
+import '../../../../constants/colors.dart';
+import '../../../../constants/image_strings.dart';
+import '../../../../constants/sizes.dart';
+import '../../../../constants/text_strings.dart';
+
+class ProfileScreen extends StatelessWidget{
+ const ProfileScreen({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ var isDark = MediaQuery.of(context).platformBrightness == Brightness.dark;
+ return Scaffold(
+ appBar: AppBar(
+ leading: IconButton(
+ onPressed: () => Get.back(),
+ icon: const Icon(LineAwesomeIcons.angle_left)),
+ title:
+ Text(tProfile, style: Theme.of(context).textTheme.headlineMedium),
+ actions: [
+ IconButton(
+ onPressed: () {},
+ icon: Icon(isDark ? LineAwesomeIcons.sun : LineAwesomeIcons.moon))
+ ],
+ ),
+ body: SingleChildScrollView(
+ child: Container(
+ padding: const EdgeInsets.all(0),
+ child: Column(
+ children: [
+ Stack(
+ children: [
+ SizedBox(
+ width: 120,
+ height: 120,
+ child: ClipRRect(
+ borderRadius: BorderRadius.circular(100),
+ child: const Image(image: AssetImage(tProfilePic)),
+ ),
+ ),
+ Positioned(
+ bottom: 0,
+ right: 0,
+ child: Container(
+ width: 35,
+ height: 35,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(100),
+ color: tSecondaryColor,
+ ),
+ child: const Icon(LineAwesomeIcons.alternate_pencil,
+ size: 20.0, color: Colors.black),
+ ),
+ )
+ ],
+ ),
+ const SizedBox(height: 10),
+ Text("Nathan kjfjg",
+ style: Theme.of(context).textTheme.headlineMedium),
+ Text("memem@hhhg",
+ style: Theme.of(context).textTheme.bodyMedium),
+ const SizedBox(height: 20),
+ SizedBox(
+ width: 200,
+ child: ElevatedButton(
+ onPressed: () => Get.to(() => EditProfileScreen()),
+ // onPressed: () {},
+ style: ElevatedButton.styleFrom(
+ backgroundColor: tSecondaryColor,
+ side: BorderSide.none,
+ shape: const StadiumBorder()),
+ child: const Text(tEditProfile,
+ style: TextStyle(color: tDarkColor)),
+ ),
+ ),
+ const SizedBox(height: 30),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Icon(Icons.mail_outline_outlined, color: Colors.grey),
+ Text("mmn@hgh"),
+ ElevatedButton(
+ onPressed: () {},
+ style: ElevatedButton.styleFrom(
+ backgroundColor: tSecondaryColor,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(20)
+ )
+ ),
+ child: Text(tVerify)
+ )
+ ],
+ ),
+ const Divider(
+ height: 3,
+ thickness: 3,
+ color: Colors.grey,
+ ),
+ const SizedBox(height: 10),
+ Text(tLocations, style: TextStyle(fontSize: 20),),
+ ListTile(
+ leading: Icon(Icons.home_filled, color: Colors.grey),
+ title: Text(tAddHome),
+ onTap: () => {},
+ ),
+ ListTile(
+ leading: Icon(Icons.location_on_outlined, color: Colors.grey,),
+ title: Text(tAddPickup),
+ onTap: () => {},
+ ),
+ const Divider(
+ height: 3,
+ thickness: 3,
+ color: Colors.grey,
+ ),
+ const SizedBox(height: 10),
+ Text(tLanguage, style: TextStyle(fontSize: 20),),
+ Text(tLanguageUK, style: TextStyle(fontSize: 17),),
+ const SizedBox(height: 10),
+ const Divider(
+ height: 3,
+ thickness: 3,
+ color: Colors.grey,
+ ),
+ ListTile(
+ leading: Icon(Icons.exit_to_app, color: Colors.grey,),
+ title: Text(tLogout),
+ onTap: () => {},
+ ),
+ ListTile(
+ leading: Icon(Icons.delete_outline_outlined, color: Colors.red,),
+ title: Text(tAddPickup),
+ onTap: () => {},
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/ride_share/lib/src/features/home/screens/requests/live_location.dart b/ride_share/lib/src/features/home/screens/requests/live_location.dart
new file mode 100644
index 0000000..75196a6
--- /dev/null
+++ b/ride_share/lib/src/features/home/screens/requests/live_location.dart
@@ -0,0 +1,122 @@
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:flutter_polyline_points/flutter_polyline_points.dart';
+import 'package:google_maps_flutter/google_maps_flutter.dart';
+import 'package:location/location.dart';
+import 'package:ride_share/src/constants/colors.dart';
+import 'package:ride_share/src/constants/image_strings.dart';
+
+import '../../../../../auth/secrets.dart';
+
+class LiveTrackingPage extends StatefulWidget {
+ const LiveTrackingPage({Key? key}) : super(key: key);
+
+ @override
+ State createState() => LiveTrackingPageState();
+}
+
+class LiveTrackingPageState extends State {
+ final Completer _controller = Completer();
+
+ static const LatLng sourceLocation = LatLng(1.3093299,36.8099464);
+ static const LatLng destination = LatLng(-1.2987826,36.7606058);
+ static const LatLng currentLocation = LatLng(1.3093299,36.8099464);
+
+
+ List polylineCoordinates = [];
+
+ BitmapDescriptor sourceIcon = BitmapDescriptor.defaultMarker;
+ BitmapDescriptor destinationIcon = BitmapDescriptor.defaultMarker;
+ BitmapDescriptor currentLocationIcon = BitmapDescriptor.defaultMarker;
+
+ void getCurrentLocation() async {
+
+ }
+
+ void getPolyPoints() async {
+ PolylinePoints polylinePoints = PolylinePoints();
+ PolylineResult result = await polylinePoints.getRouteBetweenCoordinates(
+ mapsAPIKey,
+ PointLatLng(sourceLocation.latitude, sourceLocation.longitude),
+ PointLatLng(destination.latitude, destination.longitude),
+ );
+
+ if (result.points.isNotEmpty) {
+ result.points.forEach(
+ (PointLatLng point) =>
+ polylineCoordinates.add(LatLng(point.latitude, point.longitude)),
+ );
+ setState(() {});
+ }
+ }
+
+ void setCustomMarkerIcons() {
+ BitmapDescriptor.fromAssetImage(
+ ImageConfiguration.empty, tpinsource)
+ .then(
+ (icon) {
+ sourceIcon = icon;
+ },
+ );
+
+ BitmapDescriptor.fromAssetImage(
+ ImageConfiguration.empty, tpindest)
+ .then(
+ (icon) {
+ destinationIcon = icon;
+ },
+ );
+
+ BitmapDescriptor.fromAssetImage(ImageConfiguration.empty, tbadge)
+ .then(
+ (icon) {
+ currentLocationIcon = icon;
+ },
+ );
+ }
+
+ @override
+ void initState() {
+ setCustomMarkerIcons();;
+ getPolyPoints();
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text(
+ "Live Tracking",
+ style: TextStyle(color: Colors.black, fontSize: 16),
+ ),
+ ),
+ body: GoogleMap(
+ initialCameraPosition: CameraPosition(
+ target: LatLng(
+ currentLocation!.latitude!, currentLocation!.longitude!),
+ zoom: 13.5),
+ polylines: {
+ Polyline(
+ polylineId: const PolylineId("route"),
+ points: polylineCoordinates,
+ color: tSecondaryColor,
+ width: 6,
+ ),
+ },
+ markers: {
+ const Marker(
+ markerId: MarkerId("source"), position: sourceLocation),
+ const Marker(
+ markerId: MarkerId("destination"), position: destination),
+ const Marker(
+ markerId: MarkerId("current"), position: currentLocation),
+ },
+ onMapCreated: (mapController) {
+ _controller.complete(mapController);
+ },
+ ),
+ );
+ }
+}
\ No newline at end of file
diff --git a/ride_share/lib/src/features/home/screens/requests/ride_request.dart b/ride_share/lib/src/features/home/screens/requests/ride_request.dart
new file mode 100644
index 0000000..5497599
--- /dev/null
+++ b/ride_share/lib/src/features/home/screens/requests/ride_request.dart
@@ -0,0 +1,113 @@
+import 'dart:ui' as ui;
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:ride_share/src/constants/colors.dart';
+import 'package:ride_share/src/constants/image_strings.dart';
+import 'package:ride_share/src/features/home/screens/requests/live_location.dart';
+import 'package:ride_share/src/models/ride_request_model.dart';
+import 'package:ride_share/src/models/user_model.dart';
+import 'package:ride_share/src/repository/ride_repository.dart';
+import 'package:ride_share/src/repository/user_repository.dart';
+
+class RideRequests extends StatefulWidget {
+ @override
+ _RideRequestsState createState() => _RideRequestsState();
+}
+
+class _RideRequestsState extends State {
+ RideRepository rideRepository = Get.put(RideRepository.instance);
+ final double _borderRadius = 24;
+ // UserModel user =
+ // UserRepository.instance.getUserDetails("+254706446072") as UserModel;
+
+ var items = [
+ PlaceInfo('Mary Jane', 4.4, 'Chalbi Court', 350),
+ PlaceInfo('John Doe', 4.0, 'Riverside', 200),
+ PlaceInfo('Mary Jane', 4.7, 'Junction Mall', 230),
+ PlaceInfo('Mary Jane', 5.0, 'Lavington Mall', 150),
+ ];
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text('Ride Requests'),
+ backgroundColor: tSecondaryColor,
+ ),
+ body: ListView.builder(
+ shrinkWrap: true,
+ itemCount: 4,
+ itemBuilder: (context, index) {
+ return Column(
+ children: [
+ ListTile(
+ tileColor: Colors.white,
+ leading: const Icon(Icons.person),
+ title: Text(items[index].name),
+ trailing: ElevatedButton(onPressed: () {
+ Get.to(() => const LiveTrackingPage());
+ }, child: Text("Pickup"),),
+ subtitle: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(items[index].location),
+ Text("Paying 350"),
+ ],
+ ),
+ )
+ ],
+ );
+ },
+ ),
+ );
+ }
+}
+// child: Container(
+// width: 300,
+// height: 200,
+// padding: new EdgeInsets.all(10.0),
+// child: Card(
+// shape: RoundedRectangleBorder(
+// borderRadius: BorderRadius.circular(15.0),
+// ),
+// color: Colors.red,
+// elevation: 10,
+// child: Column(
+// mainAxisSize: MainAxisSize.min,
+// children: [
+// const ListTile(
+// leading: Icon(Icons.album, size: 60),
+// title: Text(
+// 'Sonu Nigam',
+// style: TextStyle(fontSize: 30.0)
+// ),
+// subtitle: Text(
+// 'Best of Sonu Nigam Music.',
+// style: TextStyle(fontSize: 18.0)
+// ),
+// ),
+// ButtonBar(
+// children: [
+// ElevatedButton(
+// child: const Text('Play'),
+// onPressed: () {/* ... */},
+// ),
+// ElevatedButton(
+// child: const Text('Pause'),
+// onPressed: () {/* ... */},
+// ),
+// ],
+// ),
+// ],
+// ),
+// ),
+// )
+
+class PlaceInfo {
+ final String name;
+ final String location;
+ final double rating;
+ final int paying;
+
+ PlaceInfo(this.name, this.rating, this.location, this.paying);
+}
diff --git a/ride_share/lib/src/features/home/screens/requests/track_location.dart b/ride_share/lib/src/features/home/screens/requests/track_location.dart
new file mode 100644
index 0000000..7ec4acd
--- /dev/null
+++ b/ride_share/lib/src/features/home/screens/requests/track_location.dart
@@ -0,0 +1,148 @@
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:flutter_polyline_points/flutter_polyline_points.dart';
+import 'package:google_maps_flutter/google_maps_flutter.dart';
+import 'package:location/location.dart';
+import 'package:ride_share/src/constants/colors.dart';
+
+import '../../../../../auth/secrets.dart';
+
+class LiveTrackingPage extends StatefulWidget {
+ const LiveTrackingPage({Key? key}) : super(key: key);
+
+ @override
+ State createState() => LiveTrackingPageState();
+}
+
+class LiveTrackingPageState extends State {
+ final Completer _controller = Completer();
+
+ static const LatLng sourceLocation = LatLng(37.4221, -122.0841);
+ static const LatLng destination = LatLng(37.4116, -122.0713);
+
+ List polylineCoordinates = [];
+ LocationData? currentLocation;
+
+ BitmapDescriptor sourceIcon = BitmapDescriptor.defaultMarker;
+ BitmapDescriptor destinationIcon = BitmapDescriptor.defaultMarker;
+ BitmapDescriptor currentLocationIcon = BitmapDescriptor.defaultMarker;
+
+ void getCurrentLocation() async {
+ Location location = Location();
+
+ location.getLocation().then(
+ (location) {
+ currentLocation = location;
+ },
+ );
+
+ GoogleMapController googleMapController = await _controller.future;
+
+ location.onLocationChanged.listen((newLoc) {
+ currentLocation = newLoc;
+ googleMapController.animateCamera(
+ CameraUpdate.newCameraPosition(
+ CameraPosition(
+ zoom: 13.5,
+ target: LatLng(
+ newLoc.latitude!,
+ newLoc.longitude!,
+ ),
+ ),
+ ),
+ );
+ setState(() {});
+ });
+ }
+
+ void getPolyPoints() async {
+ PolylinePoints polylinePoints = PolylinePoints();
+ PolylineResult result = await polylinePoints.getRouteBetweenCoordinates(
+ mapsAPIKey,
+ PointLatLng(sourceLocation.latitude, sourceLocation.longitude),
+ PointLatLng(destination.latitude, destination.longitude),
+ );
+
+ if (result.points.isNotEmpty) {
+ result.points.forEach(
+ (PointLatLng point) =>
+ polylineCoordinates.add(LatLng(point.latitude, point.longitude)),
+ );
+ setState(() {});
+ }
+ }
+
+ void setCustomMarkerIcons() {
+ BitmapDescriptor.fromAssetImage(
+ ImageConfiguration.empty, "asset/Pin_source.png")
+ .then(
+ (icon) {
+ sourceIcon = icon;
+ },
+ );
+
+ BitmapDescriptor.fromAssetImage(
+ ImageConfiguration.empty, "asset/Pin_destination.png")
+ .then(
+ (icon) {
+ destinationIcon = icon;
+ },
+ );
+
+ BitmapDescriptor.fromAssetImage(ImageConfiguration.empty, "asset/Badge.png")
+ .then(
+ (icon) {
+ currentLocationIcon = icon;
+ },
+ );
+ }
+
+ @override
+ void initState() {
+ setCustomMarkerIcons();
+ getCurrentLocation();
+ getPolyPoints();
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text(
+ "Live Tracking",
+ style: TextStyle(color: Colors.black, fontSize: 16),
+ ),
+ ),
+ body: GoogleMap(
+ initialCameraPosition: CameraPosition(
+ target: LatLng(
+ currentLocation!.latitude!, currentLocation!.longitude!),
+ zoom: 13.5),
+ polylines: {
+ Polyline(
+ polylineId: const PolylineId("route"),
+ points: polylineCoordinates,
+ color: tSecondaryColor,
+ width: 6,
+ ),
+ },
+ markers: {
+ const Marker(
+ markerId: MarkerId("source"), position: sourceLocation),
+ const Marker(
+ markerId: MarkerId("destination"), position: destination),
+ Marker(
+ icon: currentLocationIcon,
+ markerId: const MarkerId("currentLocation"),
+ position: LatLng(currentLocation!.latitude!,
+ currentLocation!.longitude!)),
+ },
+ onMapCreated: (mapController) {
+ _controller.complete(mapController);
+ },
+ ),
+ );
+ }
+}
diff --git a/ride_share/lib/src/features/home/screens/ride/offer_ride_screen.dart b/ride_share/lib/src/features/home/screens/ride/offer_ride_screen.dart
new file mode 100644
index 0000000..d652780
--- /dev/null
+++ b/ride_share/lib/src/features/home/screens/ride/offer_ride_screen.dart
@@ -0,0 +1,507 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:google_maps_flutter/google_maps_flutter.dart';
+import 'package:intl/intl.dart';
+import 'package:ride_share/src/features/home/controllers/ride_controller.dart';
+import 'package:ride_share/src/models/network_utility.dart';
+import 'package:ride_share/src/models/place_autocomplete_response.dart';
+import 'package:ride_share/src/models/polyline_generator.dart';
+import 'package:ride_share/src/models/user_model.dart';
+import 'package:ride_share/src/repository/user_repository.dart';
+import 'package:weekday_selector/weekday_selector.dart';
+import 'package:geocoding/geocoding.dart' as geoCoding;
+
+import '../../../../../auth/secrets.dart';
+import '../../../../common_widgets/location_list_tile.dart';
+import '../../../../constants/colors.dart';
+import '../../../../constants/sizes.dart';
+import '../../../../models/autocomplete_prediction.dart';
+import '../../../../models/offered_ride_model.dart';
+import '../../controllers/profile_controller.dart';
+
+printIntAsDay(int day) {
+ print('Received integer: $day. Corresponds to day: ${intDayToEnglish(day)}');
+}
+
+String intDayToEnglish(int day) {
+ if (day % 7 == DateTime.monday % 7) return 'Monday';
+ if (day % 7 == DateTime.tuesday % 7) return 'Tueday';
+ if (day % 7 == DateTime.wednesday % 7) return 'Wednesday';
+ if (day % 7 == DateTime.thursday % 7) return 'Thursday';
+ if (day % 7 == DateTime.friday % 7) return 'Friday';
+ if (day % 7 == DateTime.saturday % 7) return 'Saturday';
+ if (day % 7 == DateTime.sunday % 7) return 'Sunday';
+ throw '🐞 This should never have happened: $day';
+}
+
+class OfferRideScreen extends StatefulWidget {
+ const OfferRideScreen({super.key});
+
+ @override
+ State createState() => _OfferRideScreenState();
+}
+
+class _OfferRideScreenState extends State {
+ final userController = Get.put(ProfileController());
+ // late LatLng destination;
+ // late LatLng source;
+ LatLng drop = const LatLng(-1.2955025, 36.6917596);
+ LatLng source = const LatLng(-1.3261827,36.8396128);
+ List route = [];
+
+ final values = List.filled(7, false);
+ TextEditingController mondayTime = TextEditingController();
+ TextEditingController tuesdayTime = TextEditingController();
+ TextEditingController wednesdayTime = TextEditingController();
+ TextEditingController thursdayTime = TextEditingController();
+ TextEditingController fridayTime = TextEditingController();
+ TextEditingController saturdayTime = TextEditingController();
+
+ TextEditingController destinationController = TextEditingController();
+ TextEditingController sourceController = TextEditingController();
+
+ @override
+ void initState() {
+ mondayTime.text = "";
+ tuesdayTime.text = "";
+ wednesdayTime.text = "";
+ thursdayTime.text = "";
+ fridayTime.text = "";
+ saturdayTime.text = "";
+ super.initState();
+ }
+
+ List placePredictions = [];
+
+ void placeAutocomplete(String query) async {
+ Uri uri = Uri.https(
+ "maps.googleapis.com",
+ 'maps/api/place/autocomplete/json',
+ {"input": query, "key": mapsAPIKey});
+
+ String? response = await NetworkUtility.fetchUrl(uri);
+
+ if (response != null) {
+ PlaceAutocompleteResponse result =
+ PlaceAutocompleteResponse.parseAutocompleteResult(response);
+
+ if (result.predictions != null) {
+ setState(() {
+ placePredictions = result.predictions!;
+ });
+ }
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final controller = Get.put(RideController());
+ final _formKey = GlobalKey();
+
+ return Scaffold(
+ appBar: AppBar(
+ title: Text("Offer a Ride"),
+ iconTheme: const IconThemeData(
+ size: 45.0, weight: 7, fill: 1, color: Colors.black),
+ ),
+ body: Form(
+ key: _formKey,
+ child: Container(
+ padding: const EdgeInsets.symmetric(vertical: tFormHeight - 10.0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ TextFormField(
+ cursorColor: tSecondaryColor,
+ // controller: controller.carMake,
+ validator: (value) {
+ if (value == null || value.isEmpty) {
+ return '*Please enter the car make and model';
+ }
+ return null;
+ },
+ decoration: const InputDecoration(
+ prefixIcon: Icon(Icons.directions_car),
+ labelText: "Car Model",
+ hintText: "Toyota",
+ border: OutlineInputBorder()),
+ ),
+ const SizedBox(
+ height: tFormHeight,
+ ),
+ TextFormField(
+ cursorColor: tSecondaryColor,
+ controller: controller.numberPlate,
+ validator: (value) {
+ if (value == null || value.isEmpty) {
+ return '*Please enter the car model';
+ }
+ return null;
+ },
+ decoration: const InputDecoration(
+ prefixIcon: Icon(Icons.directions_car_filled_outlined),
+ labelText: "Make of Car",
+ hintText: "Mark X",
+ border: OutlineInputBorder()),
+ ),
+ const SizedBox(
+ height: tFormHeight,
+ ),
+ // Text(
+ // 'The days that are currently selected are: '
+ // '${valuesToEnglishDays(values, true)}.',
+ // ),
+ WeekdaySelector(
+ // Just some days you want to display to your users.
+ selectedFillColor: Colors.orange,
+ displayedDays: const {
+ DateTime.sunday,
+ DateTime.monday,
+ DateTime.tuesday,
+ DateTime.wednesday,
+ DateTime.thursday,
+ DateTime.friday,
+ },
+ onChanged: (v) {
+ // printIntAsDay(v);
+ print(values[v % 7]);
+ setState(() {
+ values[v % 7] = !values[v % 7];
+ });
+ },
+ values: values,
+ ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ Expanded(
+ child: SizedBox(
+ child: TextField(
+ controller: mondayTime,
+ readOnly: true,
+ decoration: InputDecoration(
+ border: InputBorder.none,
+ labelText: 'Time',
+ hintText: '6:00'),
+ onTap: () async {
+ TimeOfDay? pickedTime = await showTimePicker(
+ initialTime: TimeOfDay.now(),
+ context: context,
+ );
+ if (pickedTime != null) {
+ print(pickedTime.format(context)); //output 10:51 PM
+ DateTime parsedTime = DateFormat.jm()
+ .parse(pickedTime.format(context).toString());
+ //converting to DateTime so that we can further format on different pattern.
+ print(parsedTime); //output 1970-01-01 22:53:00.000
+ String formattedTime =
+ DateFormat('HH:mm:ss').format(parsedTime);
+ print(formattedTime); //output 14:59:00
+ //DateFormat() is from intl package, you can format the time on any pattern you need.
+
+ setState(() {
+ mondayTime.text = formattedTime;
+ });
+ } else {
+ print("Time is not selected");
+ }
+ },
+ ),
+ ),
+ ),
+ Expanded(
+ child: SizedBox(
+ child: TextField(
+ controller: tuesdayTime,
+ readOnly: true,
+ decoration: InputDecoration(
+ border: InputBorder.none,
+ labelText: 'Time',
+ hintText: '6:00'),
+ onTap: () async {
+ TimeOfDay? pickedTime = await showTimePicker(
+ initialTime: TimeOfDay.now(),
+ context: context,
+ );
+ if (pickedTime != null) {
+ print(pickedTime.format(context)); //output 10:51 PM
+ DateTime parsedTime = DateFormat.jm()
+ .parse(pickedTime.format(context).toString());
+ //converting to DateTime so that we can further format on different pattern.
+ print(parsedTime); //output 1970-01-01 22:53:00.000
+ String formattedTime =
+ DateFormat('HH:mm:ss').format(parsedTime);
+ print(formattedTime); //output 14:59:00
+ //DateFormat() is from intl package, you can format the time on any pattern you need.
+
+ setState(() {
+ tuesdayTime.text = formattedTime;
+ });
+ } else {
+ print("Time is not selected");
+ }
+ },
+ ),
+ ),
+ ),
+ Expanded(
+ child: SizedBox(
+ child: TextField(
+ controller: wednesdayTime,
+ readOnly: true,
+ decoration: InputDecoration(
+ border: InputBorder.none,
+ labelText: 'Time',
+ hintText: '6:00'),
+ onTap: () async {
+ TimeOfDay? pickedTime = await showTimePicker(
+ initialTime: TimeOfDay.now(),
+ context: context,
+ );
+ if (pickedTime != null) {
+ print(pickedTime.format(context)); //output 10:51 PM
+ DateTime parsedTime = DateFormat.jm()
+ .parse(pickedTime.format(context).toString());
+ //converting to DateTime so that we can further format on different pattern.
+ print(parsedTime); //output 1970-01-01 22:53:00.000
+ String formattedTime =
+ DateFormat('HH:mm:ss').format(parsedTime);
+ print(formattedTime); //output 14:59:00
+ //DateFormat() is from intl package, you can format the time on any pattern you need.
+
+ setState(() {
+ wednesdayTime.text = formattedTime;
+ });
+ } else {
+ print("Time is not selected");
+ }
+ },
+ ),
+ ),
+ ),
+ Expanded(
+ child: SizedBox(
+ child: TextField(
+ controller: thursdayTime,
+ readOnly: true,
+ decoration: InputDecoration(
+ border: InputBorder.none,
+ labelText: 'Time',
+ hintText: '6:00'),
+ onTap: () async {
+ TimeOfDay? pickedTime = await showTimePicker(
+ initialTime: TimeOfDay.now(),
+ context: context,
+ );
+ if (pickedTime != null) {
+ print(pickedTime.format(context)); //output 10:51 PM
+ DateTime parsedTime = DateFormat.jm()
+ .parse(pickedTime.format(context).toString());
+ //converting to DateTime so that we can further format on different pattern.
+ print(parsedTime); //output 1970-01-01 22:53:00.000
+ String formattedTime =
+ DateFormat('HH:mm:ss').format(parsedTime);
+ print(formattedTime); //output 14:59:00
+ //DateFormat() is from intl package, you can format the time on any pattern you need.
+
+ setState(() {
+ thursdayTime.text = formattedTime;
+ });
+ } else {
+ print("Time is not selected");
+ }
+ },
+ ),
+ ),
+ ),
+ Expanded(
+ child: SizedBox(
+ child: TextField(
+ controller: fridayTime,
+ readOnly: true,
+ decoration: InputDecoration(
+ border: InputBorder.none,
+ labelText: 'Time',
+ hintText: '6:00'),
+ onTap: () async {
+ TimeOfDay? pickedTime = await showTimePicker(
+ initialTime: TimeOfDay.now(),
+ context: context,
+ );
+ if (pickedTime != null) {
+ print(pickedTime.format(context)); //output 10:51 PM
+ DateTime parsedTime = DateFormat.jm()
+ .parse(pickedTime.format(context).toString());
+ //converting to DateTime so that we can further format on different pattern.
+ print(parsedTime); //output 1970-01-01 22:53:00.000
+ String formattedTime =
+ DateFormat('HH:mm:ss').format(parsedTime);
+ print(formattedTime); //output 14:59:00
+ //DateFormat() is from intl package, you can format the time on any pattern you need.
+
+ setState(() {
+ fridayTime.text = formattedTime;
+ });
+ } else {
+ print("Time is not selected");
+ }
+ },
+ ),
+ ),
+ ),
+ Expanded(
+ child: SizedBox(
+ child: TextField(
+ controller: saturdayTime,
+ readOnly: true,
+ decoration: InputDecoration(
+ border: InputBorder.none,
+ labelText: 'Time',
+ hintText: '6:00'),
+ onTap: () async {
+ TimeOfDay? pickedTime = await showTimePicker(
+ initialTime: TimeOfDay.now(),
+ context: context,
+ );
+ if (pickedTime != null) {
+ print(pickedTime.format(context)); //output 10:51 PM
+ DateTime parsedTime = DateFormat.jm()
+ .parse(pickedTime.format(context).toString());
+ //converting to DateTime so that we can further format on different pattern.
+ print(parsedTime); //output 1970-01-01 22:53:00.000
+ String formattedTime =
+ DateFormat('HH:mm:ss').format(parsedTime);
+ print(formattedTime); //output 14:59:00
+ //DateFormat() is from intl package, you can format the time on any pattern you need.
+
+ setState(() {
+ saturdayTime.text = formattedTime;
+ });
+ } else {
+ print("Time is not selected");
+ }
+ },
+ ),
+ ),
+ ),
+ ],
+ ),
+ //
+ TextFormField(
+ controller: destinationController,
+ onChanged: (value) {
+ placeAutocomplete(value);
+ },
+ textInputAction: TextInputAction.search,
+ decoration: InputDecoration(
+ hintText: "source location",
+ ),
+ ),
+ const Divider(
+ height: 4,
+ thickness: 4,
+ color: Colors.grey,
+ ),
+ Padding(
+ padding: EdgeInsets.all(10),
+ child: ElevatedButton(
+ onPressed: () {
+ // route = getPolylines(source, drop);
+ },
+ child: Text("get lines")),
+ ),
+ const Divider(
+ height: 4,
+ thickness: 4,
+ color: Colors.grey,
+ ),
+ Expanded(
+ child: ListView.builder(
+ itemCount: placePredictions.length,
+ itemBuilder: (context, index) => LocationListTile(
+ press: () async {
+ String selectedPlace =
+ placePredictions[index].description!;
+ destinationController.text = selectedPlace;
+
+ List locations =
+ await geoCoding.locationFromAddress(selectedPlace);
+ source = LatLng(
+ locations.first.latitude, locations.first.longitude);
+ // route = getPolylines(source, drop);
+ },
+ location: placePredictions[index].description!,
+ ),
+ ),
+ ),
+
+ SizedBox(
+ width: double.infinity,
+ child: ElevatedButton(
+ style: ButtonStyle(
+ shape: MaterialStateProperty.all(RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(7.0))),
+ ),
+ onPressed: () async {
+ if (_formKey.currentState!.validate()) {
+ UserModel user = await UserRepository.instance.getUserDetails("+254706446072");
+ String polyline = await getPolylines(source, drop);
+ Map dayAndTime = {};
+ List initialTimes = [
+ mondayTime.text.trim(),
+ tuesdayTime.text.trim(),
+ wednesdayTime.text.trim(),
+ thursdayTime.text.trim(),
+ fridayTime.text.trim(),
+ saturdayTime.text.trim()
+ ];
+ List times = [];
+
+ for(var time in initialTimes){
+ if(time.isNotEmpty){
+ times.add(time);
+ }
+ }
+ List days = valuesToEnglishDays(values, true);
+ Map mappings = {};
+
+ for(int i = 0; i< days.length; i++){
+ Map entry= {days[i]: times[i]};
+ mappings.addAll(entry);
+ }
+
+ // final rideDetails = OfferedRideModel(
+ // userId: user.id!,
+ // // carModel: controller.carMake.text.trim(),
+ // numberPlate: controller.numberPlate.text.trim(),
+ // dayAndTimeAvailable: mappings,
+ // routePolyline: polyline
+ // );
+
+ // RideController.instance.addRide(rideDetails);
+
+ // print(rideDetails);
+ // print(rideDetails.toJson());
+ }
+ },
+ child: Text("ADD RIDE"),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+
+ valuesToEnglishDays(List values, bool? searchedValue) {
+ final days = [];
+ for (int i = 0; i < values.length; i++) {
+ final v = values[i];
+ // Use v == true, as the value could be null, as well (disabled days).
+ if (v == searchedValue) days.add(intDayToEnglish(i));
+ }
+ if (days.isEmpty) return 'NONE';
+ return days;
+ }
+}
diff --git a/ride_share/lib/src/features/home/screens/ride/registration_pages/route_page.dart b/ride_share/lib/src/features/home/screens/ride/registration_pages/route_page.dart
new file mode 100644
index 0000000..1d1e926
--- /dev/null
+++ b/ride_share/lib/src/features/home/screens/ride/registration_pages/route_page.dart
@@ -0,0 +1,178 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:google_fonts/google_fonts.dart';
+import 'package:google_maps_flutter/google_maps_flutter.dart';
+import 'package:google_maps_webservice/places.dart';
+import 'package:geocoding/geocoding.dart' as geoCoding;
+import 'package:ride_share/src/models/polyline_creator.dart';
+
+import '../../../../../models/polyline_generator.dart';
+import '../../../controllers/map_controller.dart';
+
+class RoutePage extends StatefulWidget {
+ const RoutePage(
+ {Key? key,
+ required this.sourceLocation,
+ required this.destinationLocation,
+ required this.encodedPolyline,
+ required this.onSubmit})
+ : super(key: key);
+
+ final String sourceLocation;
+ final String destinationLocation;
+ final String encodedPolyline;
+
+
+ final Function onSubmit;
+
+ @override
+ State createState() => _RoutePageState();
+}
+
+class _RoutePageState extends State {
+ String btnText = "SUBMIT";
+ final sourceController = TextEditingController();
+ MapController mapController = Get.put(MapController());
+ late LatLng source;
+ late LatLng destination;
+ final Set polyline = {};
+ String encodedPolyline = "";
+ Set markers = Set();
+ GoogleMapController? myMapController;
+ final CameraPosition _kGooglePlex = const CameraPosition(
+ target: LatLng(-1.3093299, 36.8099464),
+ zoom: 10,
+ );
+
+
+ @override
+ void initState() {
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ // select location and show polyline on map for later
+ TextFormField(
+ controller: sourceController,
+ readOnly: true,
+ onTap: () async {
+ Prediction? p = await mapController.showGoogleAutoComplete(context);
+
+ String selectedPlace = p!.description!;
+
+ sourceController.text = selectedPlace;
+
+ List locations =
+ await geoCoding.locationFromAddress(selectedPlace);
+
+ source =
+ LatLng(locations.first.latitude, locations.first.longitude);
+
+ markers.add(Marker(
+ markerId: MarkerId(selectedPlace),
+ infoWindow: InfoWindow(
+ title: 'Source: $selectedPlace',
+ ),
+ position: source,
+ // icon: BitmapDescriptor.fromBytes(markIcons),
+ ));
+
+ destination = LatLng(-1.295964, 36.7320972);
+
+ markers.add(Marker(
+ markerId: const MarkerId("Makini School"),
+ infoWindow: const InfoWindow(
+ title: 'Destination: Makini School',
+ ),
+ position: destination,
+ // icon: BitmapDescriptor.fromBytes(markIcons),
+ ));
+
+ myMapController!.animateCamera(CameraUpdate.newCameraPosition(
+ CameraPosition(target: source, zoom: 14)
+ //17 is new zoom level
+ ));
+ encodedPolyline = await getPolylines(source, destination);
+
+
+ // await getPolylinesDrawn(source, destination);
+
+
+ // setState(() {
+ // showDestinationField = true;
+ // });
+ },
+ style: GoogleFonts.poppins(
+ fontSize: 15, fontWeight: FontWeight.bold, color: Colors.black54),
+ decoration: InputDecoration(
+ hintText: 'Pick your source location',
+ hintStyle: GoogleFonts.poppins(
+ fontSize: 14,
+ fontWeight: FontWeight.w400,
+ color: Colors.black54,
+ fontStyle: FontStyle.italic),
+ suffixIcon: const Padding(
+ padding: EdgeInsets.only(left: 10),
+ child: Icon(
+ Icons.search,
+ color: Colors.black,
+ ),
+ ),
+ border: InputBorder.none,
+ ),
+ ),
+ const SizedBox(
+ height: 20,
+ ),
+
+ // The map
+ Container(
+ height: Get.height * 0.45,
+ width: double.infinity,
+ margin: EdgeInsets.only(left: 30, right: 30),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(30),
+ border: Border.all(
+ style: BorderStyle.solid,
+ ),
+ ),
+ child: GoogleMap(
+ markers: markers,
+ polylines: polyline,
+ zoomControlsEnabled: true,
+ onMapCreated: (GoogleMapController controller) {
+ myMapController = controller;
+ },
+ initialCameraPosition: _kGooglePlex,
+ ),
+
+ ),
+
+ const SizedBox(
+ height: 10,
+ ),
+
+ // widget.onSubmit(source, destination, polyline);
+ ElevatedButton(
+ style: ButtonStyle(
+ shape: MaterialStateProperty.all(RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(7.0))),
+ ),
+ onPressed: () async {
+ widget.onSubmit(source.toString(), destination.toString(), encodedPolyline);
+ setState(() {
+ btnText = "SUBMITTED";
+ });
+ },
+ child: Text(btnText),
+ ),
+ ],
+ );
+ }
+}
diff --git a/ride_share/lib/src/features/home/screens/ride/registration_pages/schedule_page.dart b/ride_share/lib/src/features/home/screens/ride/registration_pages/schedule_page.dart
new file mode 100644
index 0000000..c7fa66f
--- /dev/null
+++ b/ride_share/lib/src/features/home/screens/ride/registration_pages/schedule_page.dart
@@ -0,0 +1,313 @@
+import 'package:flutter/material.dart';
+import 'package:intl/intl.dart';
+import 'package:ride_share/src/features/home/screens/ride/ride_registration.dart';
+import 'package:weekday_selector/weekday_selector.dart';
+
+class SchedulePage extends StatefulWidget {
+ SchedulePage({
+ Key? key,
+ required this.onSelect,
+ required this.availableDaysAndTimes,
+ }) : super(key: key);
+
+ final Map availableDaysAndTimes;
+ final Function onSelect;
+
+ @override
+ State createState() => _SchedulePageState();
+}
+
+class _SchedulePageState extends State {
+ final values = List.filled(7, false);
+ String btnText = "SUBMIT";
+
+ TextEditingController mondayTime = TextEditingController();
+ TextEditingController tuesdayTime = TextEditingController();
+ TextEditingController wednesdayTime = TextEditingController();
+ TextEditingController thursdayTime = TextEditingController();
+ TextEditingController fridayTime = TextEditingController();
+ TextEditingController saturdayTime = TextEditingController();
+
+ @override
+ void initState() {
+ mondayTime.text = "";
+ tuesdayTime.text = "";
+ wednesdayTime.text = "";
+ thursdayTime.text = "";
+ fridayTime.text = "";
+ saturdayTime.text = "";
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final _formKey = GlobalKey();
+ DateFormat inputFormat = DateFormat('hh:mm');
+ return Scaffold(
+ body: SingleChildScrollView(
+ child: Form(
+ key: _formKey,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ const Text("Select the days you are available"),
+ const SizedBox(
+ height: 10,
+ ),
+ WeekdaySelector(
+ // Just some days you want to display to your users.
+ selectedFillColor: Colors.orange,
+ displayedDays: const {
+ DateTime.sunday,
+ DateTime.monday,
+ DateTime.tuesday,
+ DateTime.wednesday,
+ DateTime.thursday,
+ DateTime.friday,
+ },
+ onChanged: (v) {
+ setState(() {
+ values[v % 7] = !values[v % 7];
+ });
+ },
+ values: values,
+ ),
+ const SizedBox(
+ height: 20,
+ ),
+ const Text("Select the respective times of departure"),
+ const SizedBox(
+ height: 15,
+ ),
+ Column(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ TextField(
+ controller: mondayTime,
+ readOnly: true,
+ decoration: InputDecoration(
+ border: InputBorder.none,
+ labelText: 'Time on Monday',
+ hintText: '6:00'),
+ onTap: () async {
+ TimeOfDay? pickedTime = await showTimePicker(
+ initialTime: TimeOfDay.now(),
+ context: context,
+ );
+ if (pickedTime != null) {
+ DateTime parsedTime = inputFormat
+ .parse(pickedTime.format(context).toString());
+ String formattedTime =
+ DateFormat('HH:mm').format(parsedTime);
+
+ setState(() {
+ mondayTime.text = formattedTime;
+ });
+ } else {
+ print("Time is not selected");
+ }
+ },
+ ),
+ TextField(
+ controller: tuesdayTime,
+ readOnly: true,
+ decoration: const InputDecoration(
+ border: InputBorder.none,
+ labelText: 'Time on Tuesday',
+ hintText: '6:00'),
+ onTap: () async {
+ TimeOfDay? pickedTime = await showTimePicker(
+ initialTime: TimeOfDay.now(),
+ context: context,
+ );
+ if (pickedTime != null) {
+ DateTime parsedTime = inputFormat
+ .parse(pickedTime.format(context).toString());
+ String formattedTime =
+ DateFormat('HH:mm').format(parsedTime);
+
+ setState(() {
+ tuesdayTime.text = formattedTime;
+ });
+ } else {
+ print("Time is not selected");
+ }
+ },
+ ),
+ TextField(
+ controller: wednesdayTime,
+ readOnly: true,
+ decoration: const InputDecoration(
+ border: InputBorder.none,
+ labelText: 'Time on Wednesday',
+ hintText: '6:00'),
+ onTap: () async {
+ TimeOfDay? pickedTime = await showTimePicker(
+ initialTime: TimeOfDay.now(),
+ context: context,
+ );
+ if (pickedTime != null) {
+ DateTime parsedTime = inputFormat
+ .parse(pickedTime.format(context).toString());
+ String formattedTime =
+ DateFormat('HH:mm').format(parsedTime);
+
+ setState(() {
+ wednesdayTime.text = formattedTime;
+ });
+ } else {
+ print("Time is not selected");
+ }
+ },
+ ),
+ TextField(
+ controller: thursdayTime,
+ readOnly: true,
+ decoration: const InputDecoration(
+ border: InputBorder.none,
+ labelText: 'Time on Thursday',
+ hintText: '6:00'),
+ onTap: () async {
+ TimeOfDay? pickedTime = await showTimePicker(
+ initialTime: TimeOfDay.now(),
+ context: context,
+ );
+ if (pickedTime != null) {
+ DateTime parsedTime = inputFormat
+ .parse(pickedTime.format(context).toString());
+ String formattedTime =
+ DateFormat('HH:mm').format(parsedTime);
+
+ setState(() {
+ thursdayTime.text = formattedTime;
+ });
+ } else {
+ print("Time is not selected");
+ }
+ },
+ ),
+ TextField(
+ controller: fridayTime,
+ readOnly: true,
+ decoration: const InputDecoration(
+ border: InputBorder.none,
+ labelText: 'Time on Friday',
+ hintText: '6:00'),
+ onTap: () async {
+ TimeOfDay? pickedTime = await showTimePicker(
+ initialTime: TimeOfDay.now(),
+ context: context,
+ );
+ if (pickedTime != null) {
+ DateTime parsedTime = inputFormat
+ .parse(pickedTime.format(context).toString());
+ String formattedTime =
+ DateFormat('HH:mm').format(parsedTime);
+
+ setState(() {
+ fridayTime.text = formattedTime;
+ });
+ } else {
+ print("Time is not selected");
+ }
+ },
+ ),
+ TextField(
+ controller: saturdayTime,
+ readOnly: true,
+ decoration: const InputDecoration(
+ border: InputBorder.none,
+ labelText: 'Time on Saturday',
+ hintText: '6:00'),
+ onTap: () async {
+ TimeOfDay? pickedTime = await showTimePicker(
+ initialTime: TimeOfDay.now(),
+ context: context,
+ );
+ if (pickedTime != null) {
+ DateTime parsedTime = inputFormat
+ .parse(pickedTime.format(context).toString());
+ String formattedTime =
+ DateFormat('HH:mm').format(parsedTime);
+
+ setState(() {
+ saturdayTime.text = formattedTime;
+ });
+ } else {
+ print("Time is not selected");
+ }
+ },
+ ),
+ const SizedBox(
+ height: 10,
+ ),
+ ElevatedButton(
+ style: ButtonStyle(
+ shape: MaterialStateProperty.all(RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(7.0))),
+ ),
+ onPressed: () async {
+ Map dayAndTime = {};
+ List initialTimes = [
+ mondayTime.text.trim(),
+ tuesdayTime.text.trim(),
+ wednesdayTime.text.trim(),
+ thursdayTime.text.trim(),
+ fridayTime.text.trim(),
+ saturdayTime.text.trim()
+ ];
+ List times = [];
+
+ for (var time in initialTimes) {
+ if (time.isNotEmpty) {
+ times.add(time);
+ }
+ }
+ List days = valuesToEnglishDays(values, true);
+ Map mappings = {};
+
+ for (int i = 0; i < days.length; i++) {
+ Map entry = {days[i]: times[i]};
+ mappings.addAll(entry);
+ }
+ print(mappings);
+ widget.onSelect(mappings);
+
+ setState(() {
+ btnText = "SUBMITTED";
+ });
+ },
+ child: Text(btnText),
+ ),
+ ],
+ )
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+
+ String intDayToEnglish(int day) {
+ if (day % 7 == DateTime.monday % 7) return 'Monday';
+ if (day % 7 == DateTime.tuesday % 7) return 'Tueday';
+ if (day % 7 == DateTime.wednesday % 7) return 'Wednesday';
+ if (day % 7 == DateTime.thursday % 7) return 'Thursday';
+ if (day % 7 == DateTime.friday % 7) return 'Friday';
+ if (day % 7 == DateTime.saturday % 7) return 'Saturday';
+ if (day % 7 == DateTime.sunday % 7) return 'Sunday';
+ throw 'This should never have happened: $day';
+ }
+
+ valuesToEnglishDays(List values, bool? searchedValue) {
+ final days = [];
+ for (int i = 0; i < values.length; i++) {
+ final v = values[i];
+ // Use v == true, as the value could be null, as well (disabled days).
+ if (v == searchedValue) days.add(intDayToEnglish(i));
+ }
+ if (days.isEmpty) return 'NONE';
+ return days;
+ }
+}
diff --git a/ride_share/lib/src/features/home/screens/ride/registration_pages/vehicle_details_page.dart b/ride_share/lib/src/features/home/screens/ride/registration_pages/vehicle_details_page.dart
new file mode 100644
index 0000000..eebba24
--- /dev/null
+++ b/ride_share/lib/src/features/home/screens/ride/registration_pages/vehicle_details_page.dart
@@ -0,0 +1,131 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+
+import '../../../../../constants/colors.dart';
+import '../../../../../constants/sizes.dart';
+import '../../../controllers/ride_controller.dart';
+
+class VehicleDetailsPage extends StatefulWidget {
+ const VehicleDetailsPage({
+ Key? key,
+ required this.controller
+ }) : super(key: key);
+
+ final RideController controller;
+
+ @override
+ State createState() => _VehicleDetailsPageState();
+}
+
+class _VehicleDetailsPageState extends State {
+ final _formKey = GlobalKey();
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+// resizeToAvoidBottomInset: false,
+ body: SingleChildScrollView(
+
+ child: Form(
+ key: _formKey,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ TextFormField(
+ cursorColor: tSecondaryColor,
+ controller: widget.controller.vehicleMake,
+ validator: (value) {
+ if (value == null || value.isEmpty) {
+ return '*Please enter the vehicle make';
+ }
+ return null;
+ },
+ decoration: const InputDecoration(
+ labelText: "Vehicle Make",
+ hintText: "Toyota",
+ border: OutlineInputBorder()),
+ ),
+
+ const SizedBox(
+ height: tFormHeight-10,
+ ),
+
+ TextFormField(
+ cursorColor: tSecondaryColor,
+ controller: widget.controller.vehicleModel,
+ validator: (value) {
+ if (value == null || value.isEmpty) {
+ return '*Please enter the vehicle model';
+ }
+ return null;
+ },
+ decoration: const InputDecoration(
+ labelText: "Vehicle Model",
+ hintText: "Mark X",
+ border: OutlineInputBorder()),
+ ),
+ const SizedBox(
+ height: tFormHeight-10,
+ ),
+
+ TextFormField(
+ cursorColor: tSecondaryColor,
+ controller: widget.controller.numberPlate,
+ validator: (value) {
+ if (value == null || value.isEmpty) {
+ return '*Please enter the vehicle\'s number plate';
+ }
+ return null;
+ },
+ decoration: const InputDecoration(
+ labelText: "Number plate",
+ hintText: "KXX ***X",
+ border: OutlineInputBorder()),
+ ),
+
+ const SizedBox(
+ height: tFormHeight-10,
+ ),
+
+ TextFormField(
+ cursorColor: tSecondaryColor,
+ controller: widget.controller.vehicleColor,
+ validator: (value) {
+ if (value == null || value.isEmpty) {
+ return '*Please enter the vehicle\'s color';
+ }
+ return null;
+ },
+ decoration: const InputDecoration(
+ labelText: "Color",
+ hintText: "White",
+ border: OutlineInputBorder()),
+ ),
+
+ const SizedBox(
+ height: tFormHeight-10,
+ ),
+
+ TextFormField(
+ cursorColor: tSecondaryColor,
+ keyboardType: TextInputType.number,
+ controller: widget.controller.seatsAvailable,
+ validator: (value) {
+ if (value == null || value.isEmpty) {
+ return '*Please enter the number of seats available';
+ }
+ return null;
+ },
+ decoration: const InputDecoration(
+ labelText: "Seats Available",
+ hintText: "4",
+ border: OutlineInputBorder()),
+ ),
+
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/ride_share/lib/src/features/home/screens/ride/ride_registration.dart b/ride_share/lib/src/features/home/screens/ride/ride_registration.dart
new file mode 100644
index 0000000..fa281b6
--- /dev/null
+++ b/ride_share/lib/src/features/home/screens/ride/ride_registration.dart
@@ -0,0 +1,148 @@
+import 'dart:io';
+
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:ride_share/src/constants/colors.dart';
+import 'package:ride_share/src/features/home/controllers/ride_controller.dart';
+import 'package:ride_share/src/features/home/screens/ride/registration_pages/schedule_page.dart';
+import 'package:ride_share/src/features/home/screens/ride/registration_pages/vehicle_details_page.dart';
+import 'package:ride_share/src/features/home/screens/ride/registration_pages/route_page.dart';
+
+import '../../../../common_widgets/registration_header.dart';
+import '../../../../models/offered_ride_model.dart';
+import '../../../../models/user_model.dart';
+import '../../../../repository/user_repository.dart';
+
+class RideRegistrationTemplate extends StatefulWidget {
+ const RideRegistrationTemplate({Key? key}) : super(key: key);
+
+ @override
+ State createState() =>
+ _RideRegistrationTemplateState();
+}
+
+class _RideRegistrationTemplateState extends State {
+ String sourceLocation = '';
+ String destinationLocation = '';
+ String vehicleMake = '';
+ String vehicleModel = '';
+ String numberPlate = '';
+ String seatsAvailable = '';
+ String vehicleColor = '';
+ String encodedPolyline = '';
+ Map availableDaysAndTimes = {};
+ PageController pageController = PageController();
+
+ RideController rideController = Get.put(RideController());
+ int currentPage = 0;
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ body: Column(
+ children: [
+ regHeaderPlain(
+ title: 'Vehicle Registration',
+ subtitle: 'Fill in your car\'s details'),
+ const SizedBox(height: 20),
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 20),
+ child: PageView(
+ onPageChanged: (int page) {
+ currentPage = page;
+ },
+ controller: pageController,
+ physics: NeverScrollableScrollPhysics(),
+ children: [
+ RoutePage(
+ sourceLocation: sourceLocation,
+ destinationLocation: destinationLocation,
+ encodedPolyline: encodedPolyline,
+ onSubmit:
+ (String source, String destination, String polyline) {
+ setState(() {
+ sourceLocation = source;
+ destinationLocation = destination;
+ encodedPolyline = polyline;
+ });
+ },
+ ),
+ VehicleDetailsPage(
+ controller: rideController,
+ ),
+ SchedulePage(
+ availableDaysAndTimes: availableDaysAndTimes,
+ onSelect: (Map mappings) {
+ setState(() {
+ availableDaysAndTimes = mappings;
+ });
+ },
+ ),
+ ],
+ ),
+ ),
+ ),
+ Align(
+ alignment: Alignment.bottomRight,
+ child: Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: Obx(
+ () => isUploading.value
+ ? Center(
+ child: CircularProgressIndicator(),
+ )
+ : FloatingActionButton(
+ onPressed: () {
+ if (currentPage < 2) {
+ pageController.animateToPage(currentPage + 1,
+ duration: const Duration(seconds: 1),
+ curve: Curves.easeIn);
+ } else {
+ uploadVehicleDetails();
+ }
+ },
+ child: Icon(
+ Icons.arrow_forward,
+ color: Colors.white,
+ ),
+ backgroundColor: tSecondaryColor,
+ ),
+ ),
+ )),
+ ],
+ ),
+ );
+ }
+
+ var isUploading = false.obs;
+
+ uploadVehicleDetails() async {
+ isUploading(true);
+
+ UserModel user =
+ await UserRepository.instance.getUserDetails("+254706446072");
+
+ // TODO: modify ride-details model to accommodate new fields
+
+ final rideDetails = OfferedRideModel(
+ userId: user.id!,
+ userName: user.fullName,
+ source: sourceLocation,
+ destination: destinationLocation,
+ encodedPolyline: encodedPolyline,
+ vehicleMake: rideController.vehicleMake.text.trim(),
+ vehicleModel: rideController.vehicleModel.text.trim(),
+ vehicleColor: rideController.vehicleColor.text.trim(),
+ seatsAvailable: rideController.seatsAvailable.text.trim(),
+ numberPlate: rideController.numberPlate.text.trim(),
+ dayAndTimeAvailable: availableDaysAndTimes,
+ );
+
+
+ RideController.instance.addRide(rideDetails);
+
+ isUploading(false);
+ // Get.off(() => SuccessfulUploadScreen());
+ }
+}
diff --git a/ride_share/lib/src/features/home/screens/schedule/user_schedule.dart b/ride_share/lib/src/features/home/screens/schedule/user_schedule.dart
new file mode 100644
index 0000000..a6376d8
--- /dev/null
+++ b/ride_share/lib/src/features/home/screens/schedule/user_schedule.dart
@@ -0,0 +1,149 @@
+import 'dart:ui' as ui;
+import 'package:flutter/material.dart';
+import 'package:ride_share/src/constants/colors.dart';
+import 'package:ride_share/src/constants/image_strings.dart';
+
+class UserSchedule extends StatefulWidget {
+ @override
+ _UserScheduleState createState() => _UserScheduleState();
+}
+
+class _UserScheduleState extends State {
+ final double _borderRadius = 24;
+
+ var items = [
+ PlaceInfo('Mary Jane', 4.4, 'Chalbi Court', 350),
+ PlaceInfo('John Doe', 4.0, 'Riverside', 200),
+ PlaceInfo('Mary Jane', 4.7, 'Junction Mall', 230),
+ PlaceInfo('Mary Jane', 5.0, 'Lavington Mall', 150),
+ ];
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text('Ride Requests'),
+ backgroundColor: tSecondaryColor,
+ ),
+ body: ListView.builder(
+ itemCount: items.length,
+ itemBuilder: (context, index) {
+ return Center(
+ child: Padding(
+ padding: const EdgeInsets.all(13.0),
+ child: Stack(
+ children: [
+ Container(
+ height: 150,
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(_borderRadius),
+ boxShadow: const [
+ BoxShadow(
+ color: Colors.grey,
+ blurRadius: 12,
+ offset: Offset(0, 6),
+ ),
+ ],
+ ),
+ ),
+ Positioned.fill(
+ child: Row(
+ children: [
+ Expanded(
+ child: Image.asset(
+ tProfilePic,
+ height: 64,
+ width: 64,
+ ),
+ flex: 2,
+ ),
+ Expanded(
+ flex: 3,
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ items[index].name,
+ style: TextStyle(
+ color: Colors.black87,
+ fontFamily: 'Avenir',
+ fontWeight: FontWeight.w700),
+ ),
+ Text(
+ items[index].paying.toString(),
+ style: TextStyle(
+ color: Colors.black87,
+ fontFamily: 'Avenir',
+ ),
+ ),
+ SizedBox(height: 16),
+ Row(
+ children: [
+ Icon(
+ Icons.location_on,
+ color: Colors.black87,
+ size: 16,
+ ),
+ SizedBox(
+ width: 8,
+ ),
+ Flexible(
+ child: Text(
+ items[index].location,
+ style: TextStyle(
+ color: Colors.black87,
+ fontFamily: 'Avenir',
+ ),
+ ),
+ ),
+
+ ],
+ ),
+ ],
+ ),
+ ),
+
+ Expanded(
+ flex: 2,
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ items[index].rating.toString(),
+ style: TextStyle(
+ color: Colors.black87,
+ fontFamily: 'Avenir',
+ fontSize: 18,
+ fontWeight: FontWeight.w700),
+ ),
+ const Icon(
+ Icons.star,
+ color: tSecondaryColor,
+ ),
+ // RatingBar(rating: items[index].rating),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ },
+ ),
+ );
+ }
+}
+
+class PlaceInfo {
+ final String name;
+ final String location;
+ final double rating;
+ final int paying;
+
+ PlaceInfo(this.name, this.rating, this.location, this.paying);
+}
diff --git a/ride_share/lib/src/models/active_ride_model.dart b/ride_share/lib/src/models/active_ride_model.dart
new file mode 100644
index 0000000..c3f5059
--- /dev/null
+++ b/ride_share/lib/src/models/active_ride_model.dart
@@ -0,0 +1,36 @@
+import 'package:cloud_firestore/cloud_firestore.dart';
+import 'package:google_maps_flutter/google_maps_flutter.dart';
+
+class ActiveRide {
+ final String? id;
+ final String destination;
+ final bool completed;
+ final DateTime dateMade;
+
+ ActiveRide({
+ this.id,
+ required this.destination,
+ required this.completed,
+ required this.dateMade,
+ });
+
+ toJson() {
+ return {
+ "Destination": destination,
+ "Completed": completed,
+ "Date": dateMade,
+ };
+ }
+
+ factory ActiveRide.fromSnapshot(
+ DocumentSnapshot