diff --git a/lib/app/routes/app_routes.dart b/lib/app/routes/app_routes.dart index f3129d21..4a8f61b4 100644 --- a/lib/app/routes/app_routes.dart +++ b/lib/app/routes/app_routes.dart @@ -5,18 +5,22 @@ part of 'app_pages.dart'; abstract class Routes { static const HOME = _Paths.HOME; - // static String PROFILE = Screen.PROFILE.fullPath; - // static String SETTINGS = Screen.SETTINGS.fullPath; - static String LOGIN = Screen.LOGIN.route; - static String REGISTER = Screen.REGISTER.route; + static const PROFILE = _Paths.PROFILE; + static const ACTIVITY = _Paths.ACTIVITY; + static const SETTINGS = _Paths.SETTINGS; + static const HEALTH_TIPS = _Paths.HEALTH_TIPS; + static const CHAT = _Paths.CHAT; + static const BOOKING = _Paths.BOOKING; + + // Uncomment and modify these as needed for your app // static String DASHBOARD = Screen.DASHBOARD.fullPath; // static String PRODUCTS = Screen.PRODUCTS.fullPath; // static String CART = Screen.CART.fullPath; // static String CHECKOUT = Screen.CHECKOUT.fullPath; - // static const CATEGORIES = _Paths.HOME + _Paths.CATEGORIES; - // static const TASKS = _Paths.HOME + _Paths.TASKS; - // static const USERS = _Paths.HOME + _Paths.USERS; - // static const MY_PRODUCTS = _Paths.HOME + _Paths.MY_PRODUCTS; + // static String CATEGORIES = _Paths.HOME + _Paths.CATEGORIES; + // static String TASKS = _Paths.HOME + _Paths.TASKS; + // static String USERS = _Paths.HOME + _Paths.USERS; + // static String MY_PRODUCTS = _Paths.HOME + _Paths.MY_PRODUCTS; static String PRODUCT_DETAILS(String productId) => '${Screen.PRODUCTS.route}/$productId'; @@ -32,23 +36,25 @@ abstract class Routes { '${Screen.REGISTER.route}?then=${Uri.encodeQueryComponent(afterSuccessfulLogin)}'; } -// Keeping this as Get_Cli will require it. Any addition can later be added to Screen abstract class _Paths { static const String HOME = '/home'; - // static const DASHBOARD = '/dashboard'; - // static const PRODUCTS = '/products'; - // static const PROFILE = '/profile'; - // static const SETTINGS = '/settings'; - // static const PRODUCT_DETAILS = '/:productId'; - // static const CART_DETAILS = '/:productId'; - // static const LOGIN = '/login'; - // static const CART = '/cart'; - // static const CHECKOUT = '/checkout'; - // static const REGISTER = '/register'; - // static const CATEGORIES = '/categories'; - // static const TASKS = '/tasks'; - // static const TASK_DETAILS = '/:taskId'; - // static const USERS = '/users'; - // static const USER_PROFILE = '/:uId'; - // static const MY_PRODUCTS = '/my-products'; + static const String PROFILE = '/profile'; + static const String ACTIVITY = '/activity'; + static const String SETTINGS = '/settings'; + static const String HEALTH_TIPS = '/health_tips'; + static const String CHAT = '/chat'; + static const String BOOKING = '/booking'; + + // Uncomment and modify these as needed for your app + // static const String DASHBOARD = '/dashboard'; + // static const String PRODUCTS = '/products'; + // static const String CART = '/cart'; + // static const String CHECKOUT = '/checkout'; + // static const String REGISTER = '/register'; + // static const String CATEGORIES = '/categories'; + // static const String TASKS = '/tasks'; + // static const String TASK_DETAILS = '/:taskId'; + // static const String USERS = '/users'; + // static const String USER_PROFILE = '/:uId'; + // static const String MY_PRODUCTS = '/my-products'; } diff --git a/lib/app/widgets/health_card_widget.dart b/lib/app/widgets/health_card_widget.dart new file mode 100644 index 00000000..27a1cfc0 --- /dev/null +++ b/lib/app/widgets/health_card_widget.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; + +class HealthCardWidget extends StatelessWidget { + final String title; + final String value; + final Color color; + + HealthCardWidget({ + required this.title, + required this.value, + this.color = Colors.blueAccent, + }); + + @override + Widget build(BuildContext context) { + return Card( + color: color.withOpacity(0.1), + elevation: 4, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 8), + Text( + value, + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: color, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/app/widgets/health_metric_widget.dart b/lib/app/widgets/health_metric_widget.dart new file mode 100644 index 00000000..61604622 --- /dev/null +++ b/lib/app/widgets/health_metric_widget.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; +import 'health_card_widget.dart'; + +class HealthMetricWidget extends StatelessWidget { + final List> metrics; + + HealthMetricWidget({required this.metrics}); + + @override + Widget build(BuildContext context) { + return Column( + children: metrics.map((metric) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: HealthCardWidget( + title: metric['title']!, + value: metric['value']!, + color: Colors.teal, + ), + ); + }).toList(), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index 30c258f2..e61c47eb 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,6 +9,13 @@ import 'app/routes/app_pages.dart'; import 'firebase_options.dart'; import 'services/auth_service.dart'; +import 'screens/home_screen.dart'; +import 'screens/profile_screen.dart'; +import 'screens/activity_screen.dart'; +import 'screens/settings_screen.dart'; +import 'screens/health_tips_screen.dart'; +import 'screens/chat_screen.dart'; +import 'screens/booking_screen.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await GetStorage.init(); diff --git a/lib/screen/activity_screen.dart b/lib/screen/activity_screen.dart new file mode 100644 index 00000000..de0b2969 --- /dev/null +++ b/lib/screen/activity_screen.dart @@ -0,0 +1,21 @@ +// lib/screens/activity_screen.dart +import 'package:flutter/material.dart'; +import '../widgets/activity_list_widget.dart'; + +class ActivityScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Activities'), + ), + body: ActivityListWidget(), + floatingActionButton: FloatingActionButton( + onPressed: () { + Navigator.pushNamed(context, '/add_activity'); + }, + child: Icon(Icons.add), + ), + ); + } +} diff --git a/lib/screen/booking_screen.dart b/lib/screen/booking_screen.dart new file mode 100644 index 00000000..86815f0c --- /dev/null +++ b/lib/screen/booking_screen.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; +import '../widgets/booking_form_widget.dart'; + +class BookingScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Book an Appointment'), + ), + body: BookingFormWidget(), + ); + } +} diff --git a/lib/screen/chat_screen.dart b/lib/screen/chat_screen.dart new file mode 100644 index 00000000..cd3946c6 --- /dev/null +++ b/lib/screen/chat_screen.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; +import '../widgets/chat_interface_widget.dart'; + +class ChatScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Chat with Doctor'), + ), + body: ChatInterfaceWidget(), + ); + } +} diff --git a/lib/screen/health_tips_screen.dart b/lib/screen/health_tips_screen.dart new file mode 100644 index 00000000..b0873c70 --- /dev/null +++ b/lib/screen/health_tips_screen.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; +import '../widgets/article_list_widget.dart'; + +class HealthTipsScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Health Tips'), + ), + body: ArticleListWidget(), + ); + } +} diff --git a/lib/screen/home_screen.dart b/lib/screen/home_screen.dart new file mode 100644 index 00000000..ec52d179 --- /dev/null +++ b/lib/screen/home_screen.dart @@ -0,0 +1,153 @@ +import 'package:flutter/material.dart'; +import '../widgets/health_summary_widget.dart'; +import '../widgets/notifications_widget.dart'; +import '../widgets/activity_list_widget.dart'; +import '../widgets/health_card_widget.dart'; +import '../widgets/health_metric_widget.dart'; +import '../constants.dart'; // Ensure this contains the necessary constants and configurations. + +class HomeScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text( + 'Health Dashboard', + style: TextStyle(fontWeight: FontWeight.bold), + ), + backgroundColor: Colors.blueAccent, + actions: [ + IconButton( + icon: Icon(Icons.account_circle, size: 30), + onPressed: () { + Navigator.pushNamed(context, '/profile'); + }, + ), + ], + ), + drawer: Drawer( + child: Container( + color: Colors.blueGrey[100], + child: ListView( + padding: EdgeInsets.zero, + children: [ + DrawerHeader( + decoration: BoxDecoration( + color: Colors.blueAccent, + ), + child: Text( + 'Menu', + style: TextStyle( + color: Colors.white, + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + ), + ListTile( + leading: Icon(Icons.person), + title: Text('Profile'), + onTap: () { + Navigator.pushNamed(context, '/profile'); + }, + ), + ListTile( + leading: Icon(Icons.settings), + title: Text('Settings'), + onTap: () { + Navigator.pushNamed(context, '/settings'); + }, + ), + Divider(), + ListTile( + leading: Icon(Icons.logout), + title: Text('Logout'), + onTap: () { + // Handle logout + }, + ), + ], + ), + ), + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Health Summary', + style: Theme.of(context).textTheme.headline6?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 10), + HealthSummaryWidget(), + SizedBox(height: 20), + Text( + 'Health Metrics', + style: Theme.of(context).textTheme.headline6?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 10), + HealthMetricWidget( + metrics: [ + {'title': 'Steps Today', 'value': '8,765'}, + {'title': 'Calories Burned', 'value': '620 kcal'}, + {'title': 'Heart Rate', 'value': '72 bpm'}, + {'title': 'Sleep Duration', 'value': '7 hrs 30 mins'}, + ], + ), + SizedBox(height: 20), + Text( + 'Notifications', + style: Theme.of(context).textTheme.headline6?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 10), + NotificationsWidget(), + SizedBox(height: 20), + Text( + 'Recent Activities', + style: Theme.of(context).textTheme.headline6?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 10), + Expanded( + child: ActivityListWidget(), + ), + ], + ), + ), + bottomNavigationBar: BottomNavigationBar( + items: [ + BottomNavigationBarItem( + icon: Icon(Icons.home), + label: 'Home', + ), + BottomNavigationBarItem( + icon: Icon(Icons.fitness_center), + label: 'Activities', + ), + BottomNavigationBarItem( + icon: Icon(Icons.settings), + label: 'Settings', + ), + ], + currentIndex: 0, + selectedItemColor: Colors.blueAccent, + unselectedItemColor: Colors.grey, + onTap: (index) { + if (index == 1) { + Navigator.pushNamed(context, '/activities'); + } else if (index == 2) { + Navigator.pushNamed(context, '/settings'); + } + }, + ), + ); + } +} diff --git a/lib/screen/profile_screen.dart b/lib/screen/profile_screen.dart new file mode 100644 index 00000000..ca3df52e --- /dev/null +++ b/lib/screen/profile_screen.dart @@ -0,0 +1,33 @@ +// lib/screens/profile_screen.dart +import 'package:flutter/material.dart'; +import '../widgets/profile_picture_widget.dart'; +import '../widgets/user_info_form.dart'; +import '../widgets/health_data_widget.dart'; + +class ProfileScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Profile'), + ), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + ProfilePictureWidget(), + UserInfoForm(), + HealthDataWidget(), + SizedBox(height: 20), + ElevatedButton( + onPressed: () { + // Save changes + }, + child: Text('Save Changes'), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screen/settings_screen.dart b/lib/screen/settings_screen.dart new file mode 100644 index 00000000..c8c887ad --- /dev/null +++ b/lib/screen/settings_screen.dart @@ -0,0 +1,36 @@ +// lib/screens/settings_screen.dart +import 'package:flutter/material.dart'; + +class SettingsScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Settings'), + ), + body: ListView( + children: [ + SwitchListTile( + title: Text('Notifications'), + value: true, // Replace with actual value + onChanged: (bool value) { + // Handle change + }, + ), + ListTile( + title: Text('Privacy Settings'), + onTap: () { + Navigator.pushNamed(context, '/privacy_settings'); + }, + ), + ListTile( + title: Text('Account Settings'), + onTap: () { + Navigator.pushNamed(context, '/account_settings'); + }, + ), + ], + ), + ); + } +} diff --git a/lib/services/auth_service.dart b/lib/services/auth_service.dart index 8bf72aaa..b4125831 100644 --- a/lib/services/auth_service.dart +++ b/lib/services/auth_service.dart @@ -199,3 +199,74 @@ parseEmail(String message) { int j = message.indexOf('"', i); return message.substring(i, j - 1); } + +Future setupTOTP() async { + String secret = OTP.randomSecret(); + await storeTOTPSecret(secret); + + String qrCodeData = OTPAuthURL.google( + 'MyApp', + user!.email!, + secret, + issuer: 'MyApp', + ); + + Get.dialog( + AlertDialog( + title: Text('Scan this QR Code'), + content: QrImage(data: qrCodeData, size: 200), + actions: [ + TextButton( + onPressed: () => Get.back(), + child: Text('Done'), + ), + ], + ), + ); + } + + Future verifyTOTP(String code) async { + String secret = await getStoredTOTPSecret(); + bool isValid = OTP.verifyTotp(secret: secret, otp: code, interval: 30); + + if (isValid) { + Get.snackbar('Success', '2FA verification successful.'); + } else { + Get.snackbar('Error', 'Invalid TOTP code.'); + } + } + + Future storeTOTPSecret(String secret) async { + // Implement secure storage, e.g., in Firestore or Firebase Auth custom claims + } + + Future getStoredTOTPSecret() async { + // Retrieve the stored TOTP secret securely + return 'retrieved_secret'; + } +} + +String? validatePassword(String password) { + final RegExp regex = RegExp(r'^(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$'); + if (password.isEmpty) { + return 'Please enter a password'; + } else if (!regex.hasMatch(password)) { + return 'Password must be at least 8 characters long, include an uppercase letter, a number, and a special character'; + } + return null; +} + +Future register(String email, String password) async { + String? passwordError = validatePassword(password); + if (passwordError != null) { + Get.snackbar('Error', passwordError); + return; + } + + try { + await _auth.createUserWithEmailAndPassword(email: email, password: password); + Get.snackbar('Success', 'Registration successful.'); + } catch (e) { + Get.snackbar('Error', e.toString()); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 2909a374..50042e2c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,6 +10,8 @@ dependencies: get: 4.6.6 flutter: sdk: flutter + otp: ^3.0.0 + qr_flutter: ^4.0.0 firebase_core: ^2.31.0 firebase_ui_auth: ^1.14.0 firebase_auth: ^4.19.5 @@ -24,7 +26,7 @@ dependencies: firebase_ui_localizations: ^1.12.0 firebase_remote_config: ^4.4.7 firebase_analytics: ^10.10.7 - + dev_dependencies: flutter_lints: 3.0.2 flutter_test: