diff --git a/android/app/src/main/kotlin/com/example/get_flutter_fire/MainActivity.kt b/android/app/src/main/kotlin/com/example/get_flutter_fire/MainActivity.kt new file mode 100644 index 00000000..e69de29b diff --git a/assets/images/cappuccino.jpg b/assets/images/cappuccino.jpg new file mode 100644 index 00000000..676a14da Binary files /dev/null and b/assets/images/cappuccino.jpg differ diff --git a/assets/images/latte.jpg b/assets/images/latte.jpg new file mode 100644 index 00000000..aaf8c9ba Binary files /dev/null and b/assets/images/latte.jpg differ diff --git a/firebase.json b/firebase.json new file mode 100644 index 00000000..ee78b3b1 --- /dev/null +++ b/firebase.json @@ -0,0 +1,29 @@ +{ + "hosting": { + "public": "build/web", + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**" + ], + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ] + }, + "flutter": { + "platforms": { + "dart": { + "lib/firebase_options.dart": { + "projectId": "sharekhan-prj", + "configurations": { + "web": "1:255918967492:web:c95765475abe4f1efed791" + } + } + } + } + } + } + \ No newline at end of file diff --git a/lib/app/modules/cart/views/cart_view.dart b/lib/app/modules/cart/views/cart_view.dart index 3e048c79..e64b0f7d 100644 --- a/lib/app/modules/cart/views/cart_view.dart +++ b/lib/app/modules/cart/views/cart_view.dart @@ -1,5 +1,16 @@ import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:get_flutter_fire/app/routes/app_pages.dart'; +import '../../../widgets/screen_widget.dart'; +import '../../../../services/auth_service.dart'; +import '../controllers/cart_controller.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import '../../../widgets/screen_widget.dart'; +import '../../../../services/auth_service.dart'; +import '../controllers/cart_controller.dart'; +import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:get_flutter_fire/app/routes/app_pages.dart'; import '../../../widgets/screen_widget.dart'; @@ -8,6 +19,7 @@ import '../controllers/cart_controller.dart'; class CartView extends GetView { const CartView({super.key}); + @override Widget build(BuildContext context) { return ScreenWidget( @@ -15,13 +27,91 @@ class CartView extends GetView { title: Text('${AuthService.to.userName} Cart'), centerTitle: true, ), - body: const Center( - child: Text( - 'CartView is working', - style: TextStyle(fontSize: 20), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Your Cart', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Colors.brown, + ), + ), + const SizedBox(height: 20), + Expanded( + child: ListView( + children: [ + _buildCartItem('Cappuccino', 'Rich and creamy with a smooth finish.', Icons.coffee), + const SizedBox(height: 10), + _buildCartItem('Latte', 'Smooth and creamy, perfect for a relaxing afternoon.', Icons.coffee_maker), + ], + ), + ), + ], ), ), screen: screen!, ); } + + Widget _buildCartItem(String title, String description, IconData icon) { + return Container( + padding: const EdgeInsets.all(12.0), + decoration: BoxDecoration( + color: Colors.orange.shade50, + borderRadius: BorderRadius.circular(10), + boxShadow: [ + BoxShadow( + color: Colors.grey.shade300, + blurRadius: 4, + offset: const Offset(0, 4), + ), + ], + ), + child: Row( + children: [ + Container( + width: 60, + height: 60, + decoration: BoxDecoration( + color: Colors.orange.shade100, + borderRadius: BorderRadius.circular(10), + ), + child: Icon( + icon, + size: 30, + color: Colors.brown.shade600, + ), + ), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.brown.shade700, + ), + ), + const SizedBox(height: 5), + Text( + description, + style: TextStyle( + fontSize: 14, + color: Colors.brown.shade500, + ), + ), + ], + ), + ), + ], + ), + ); + } } diff --git a/lib/app/modules/checkout/views/checkout_view.dart b/lib/app/modules/checkout/views/checkout_view.dart index b8b17072..925eeed3 100644 --- a/lib/app/modules/checkout/views/checkout_view.dart +++ b/lib/app/modules/checkout/views/checkout_view.dart @@ -1,22 +1,168 @@ import 'package:flutter/material.dart'; - import 'package:get/get.dart'; - import '../controllers/checkout_controller.dart'; class CheckoutView extends GetView { const CheckoutView({super.key}); + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('CheckoutView'), + title: const Text('Checkout'), centerTitle: true, ), - body: const Center( + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Checkout Summary', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Colors.brown, + ), + ), + const SizedBox(height: 20), + Expanded( + child: ListView( + children: [ + _buildCheckoutItem('Cappuccino', 'Rich and creamy with a smooth finish.', Icons.coffee), + const SizedBox(height: 10), + _buildCheckoutItem('Latte', 'Smooth and creamy, perfect for a relaxing afternoon.', Icons.coffee_maker), + const SizedBox(height: 20), + _buildTotalAmount(), + ], + ), + ), + _buildCheckoutButton(), + ], + ), + ), + ); + } + + Widget _buildCheckoutItem(String title, String description, IconData icon) { + return Container( + padding: const EdgeInsets.all(12.0), + decoration: BoxDecoration( + color: Colors.orange.shade50, + borderRadius: BorderRadius.circular(10), + boxShadow: [ + BoxShadow( + color: Colors.grey.shade300, + blurRadius: 4, + offset: const Offset(0, 4), + ), + ], + ), + child: Row( + children: [ + Container( + width: 60, + height: 60, + decoration: BoxDecoration( + color: Colors.orange.shade100, + borderRadius: BorderRadius.circular(10), + ), + child: Icon( + icon, + size: 30, + color: Colors.brown.shade600, + ), + ), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.brown.shade700, + ), + ), + const SizedBox(height: 5), + Text( + description, + style: TextStyle( + fontSize: 14, + color: Colors.brown.shade500, + ), + ), + ], + ), + ), + ], + ), + ); + } + + Widget _buildTotalAmount() { + // Placeholder amount + final totalAmount = '\$20.00'; + + return Container( + padding: const EdgeInsets.all(12.0), + decoration: BoxDecoration( + color: Colors.orange.shade50, + borderRadius: BorderRadius.circular(10), + boxShadow: [ + BoxShadow( + color: Colors.grey.shade300, + blurRadius: 4, + offset: const Offset(0, 4), + ), + ], + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Total Amount', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.brown.shade700, + ), + ), + Text( + totalAmount, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.brown.shade700, + ), + ), + ], + ), + ); + } + + Widget _buildCheckoutButton() { + return Padding( + padding: const EdgeInsets.only(top: 20.0), + child: ElevatedButton( + onPressed: () { + // Handle checkout logic + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.orange, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), + ), child: Text( - 'CheckoutView is working', - style: TextStyle(fontSize: 20), + 'Complete Purchase', + style: TextStyle( + fontSize: 16, + color: Colors.white, + ), ), ), ); diff --git a/lib/app/modules/dashboard/views/dashboard_view.dart b/lib/app/modules/dashboard/views/dashboard_view.dart index f475030f..cd08ccee 100644 --- a/lib/app/modules/dashboard/views/dashboard_view.dart +++ b/lib/app/modules/dashboard/views/dashboard_view.dart @@ -1,28 +1,286 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; - import '../controllers/dashboard_controller.dart'; +import 'package:audioplayers/audioplayers.dart'; // Import the audioplayers package class DashboardView extends GetView { - const DashboardView({super.key}); + // Remove 'const' to avoid issues with non-constant fields + DashboardView({Key? key}) : super(key: key); + + final AudioPlayer _audioPlayer = AudioPlayer(); // Create an instance of AudioPlayer @override Widget build(BuildContext context) { return Scaffold( - body: Center( - child: Obx( - () => Column( - mainAxisSize: MainAxisSize.min, + backgroundColor: Colors.orange.shade50, // Light background color + appBar: AppBar( + title: const Text('Cafe App'), + backgroundColor: Colors.orange, + elevation: 0, + ), + body: Obx( + () => SingleChildScrollView( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - 'DashboardView is working', - style: TextStyle(fontSize: 20), - ), - Text('Time: ${controller.now.value.toString()}'), + _buildHeader(), + const SizedBox(height: 20), + _buildFeaturedSection(), + const SizedBox(height: 20), + _buildCafeCards(), + const SizedBox(height: 20), + _buildFooter(), ], ), ), ), ); } + + Widget _buildHeader() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Welcome to Our Cafe!', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Colors.brown.shade700, + ), + ), + Text( + 'Current Time: ${controller.now.value.toLocal().toString()}', + style: TextStyle( + fontSize: 16, + color: Colors.brown.shade500, + ), + ), + ], + ), + IconButton( + icon: const Icon(Icons.notifications, color: Colors.brown), + onPressed: () { + _playNotificationSound(); // Play sound when icon is pressed + }, + ), + ], + ); + } + + void _playNotificationSound() async { + // Load the notification sound and play it + await _audioPlayer.play(AssetSource('sounds/notification.mp3')); + } + + Widget _buildFeaturedSection() { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + gradient: LinearGradient( + colors: [Colors.orange.shade100, Colors.orange.shade300], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + ), + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Featured Specials', + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + color: Colors.brown.shade700, + ), + ), + const SizedBox(height: 10), + _buildFeatureCard('Cappuccino', 'Rich and creamy with a smooth finish.'), + const SizedBox(height: 10), + _buildFeatureCard('Latte', 'Smooth and creamy, perfect for a relaxing afternoon.'), + ], + ), + ); + } + + Widget _buildFeatureCard(String title, String description) { + return Container( + padding: const EdgeInsets.all(12.0), + decoration: BoxDecoration( + color: Colors.orange.shade50, + borderRadius: BorderRadius.circular(10), + boxShadow: [ + BoxShadow( + color: Colors.grey.shade300, + blurRadius: 4, + offset: const Offset(0, 4), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.brown.shade700, + ), + ), + const SizedBox(height: 8), + Text( + description, + style: TextStyle( + fontSize: 14, + color: Colors.brown.shade500, + ), + ), + ], + ), + ); + } + + Widget _buildCafeCards() { + return GridView.count( + crossAxisCount: 2, + shrinkWrap: true, + crossAxisSpacing: 16, + mainAxisSpacing: 16, + children: [ + _buildCafeCard('Cappuccino', 'Our rich and creamy cappuccino.', Icons.coffee), + _buildCafeCard('Latte', 'A smooth and creamy latte.', Icons.coffee_maker), + _buildCafeCard('Espresso', 'A strong and bold espresso shot.', Icons.local_cafe), + _buildCafeCard('Pastry', 'Freshly baked pastries.', Icons.cake), + _buildCafeCard('Cake', 'Delicious cakes for any occasion.', Icons.cake), + _buildCafeCard('Cookies', 'Homemade cookies with a crispy edge.', Icons.cookie), + _buildCafeCard('Muffins', 'Freshly baked muffins in various flavors.', Icons.local_cafe), + _buildCafeCard('Brownies', 'Chewy brownies with a rich chocolate flavor.', Icons.local_pizza), + ], + ); + } + + Widget _buildCafeCard(String title, String description, IconData icon) { + return Card( + elevation: 5, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15), + ), + child: Container( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: double.infinity, + height: 100, + color: Colors.orange.shade100, + child: Center( + child: Icon( + icon, + size: 60, + color: Colors.brown.shade600, + ), + ), + ), + const SizedBox(height: 10), + Text( + title, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.brown.shade700, + ), + ), + const SizedBox(height: 8), + Text( + description, + style: TextStyle( + fontSize: 14, + color: Colors.brown.shade500, + ), + ), + ], + ), + ), + ); + } + + Widget _buildFooter() { + return Container( + padding: const EdgeInsets.all(16.0), + width: double.infinity, + decoration: BoxDecoration( + color: Colors.brown.shade900, + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.3), + blurRadius: 8, + offset: Offset(0, -4), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + 'Contact Us', + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + const SizedBox(height: 12), + Text( + 'Pune Cafe street, Coffee City', + style: TextStyle( + fontSize: 16, + color: Colors.white70, + ), + ), + Text( + 'Phone: +91 111-111-1111', + style: TextStyle( + fontSize: 16, + color: Colors.white70, + ), + ), + Text( + 'Email: punecafeorg@ourcafe.com', + style: TextStyle( + fontSize: 16, + color: Colors.white70, + ), + ), + const SizedBox(height: 16), + ElevatedButton( + onPressed: () { + + }, + style: ElevatedButton.styleFrom( + + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), + ), + child: Text( + 'Get in Touch', + style: TextStyle( + fontSize: 16, + color: const Color.fromARGB(93, 147, 54, 1), + ), + ), + ), + ], + ), + ); + } } diff --git a/lib/app/modules/login/views/login_view.dart b/lib/app/modules/login/views/login_view.dart index 00c3af3f..ccccd664 100644 --- a/lib/app/modules/login/views/login_view.dart +++ b/lib/app/modules/login/views/login_view.dart @@ -55,7 +55,7 @@ class LoginView extends GetView { ? recaptcha() : SignInScreen( providers: [ - GoogleProvider(clientId: DefaultFirebaseOptions.webClientId), + //GoogleProvider(clientId: DefaultFirebaseOptions.webClientId), MyEmailAuthProvider(), ], showAuthActionSwitch: !controller.isRegistered, diff --git a/lib/app/modules/product_details/views/product_details_view.dart b/lib/app/modules/product_details/views/product_details_view.dart index c9290724..92e71df7 100644 --- a/lib/app/modules/product_details/views/product_details_view.dart +++ b/lib/app/modules/product_details/views/product_details_view.dart @@ -1,7 +1,5 @@ import 'package:flutter/material.dart'; - import 'package:get/get.dart'; - import '../controllers/product_details_controller.dart'; class ProductDetailsView extends GetWidget { @@ -10,15 +8,92 @@ class ProductDetailsView extends GetWidget { @override Widget build(BuildContext context) { return Scaffold( - body: Center( + appBar: AppBar( + title: const Text('Product Details'), + backgroundColor: Colors.orange, + elevation: 0, + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(16.0), child: Column( - mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - 'ProductDetailsView is working', - style: TextStyle(fontSize: 20), + // Product Image or Placeholder + Container( + height: 200, + width: double.infinity, + decoration: BoxDecoration( + color: Colors.orange.shade100, + borderRadius: BorderRadius.circular(12), + ), + child: Center( + child: Icon( + Icons.local_cafe, // Same icon as in ProductsView + size: 100, + color: Colors.brown.shade700, + ), + ), + ), + const SizedBox(height: 16.0), + // Product Name + Text( + 'Premium Coffee Blend', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Colors.brown.shade700, + ), + ), + const SizedBox(height: 8.0), + // Product ID + Text( + 'Product ID: ${controller.productId}', + style: TextStyle( + fontSize: 16, + color: Colors.brown.shade500, + ), + ), + const SizedBox(height: 16.0), + // Product Description + Text( + 'Description:', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.brown.shade700, + ), + ), + const SizedBox(height: 4.0), + Text( + 'Our Premium Coffee Blend is crafted from the finest beans sourced from around the world. It features a rich, full-bodied flavor with hints of caramel and chocolate. Perfect for coffee enthusiasts who enjoy a complex and satisfying cup of coffee.', + style: TextStyle( + fontSize: 16, + color: Colors.brown.shade600, + ), + ), + const SizedBox(height: 16.0), + // Additional Product Info + Text( + 'Additional Information:', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.brown.shade700, + ), + ), + const SizedBox(height: 4.0), + Text( + '• Weight: 250g\n' + '• Roasting Level: Medium\n' + '• Origin: Brazil, Colombia, Ethiopia\n' + '• Packaging: Resealable bag\n' + '• Price: \$14.99\n' + '• Available: In Stock', + style: TextStyle( + fontSize: 16, + color: Colors.brown.shade600, + ), ), - Text('ProductId: ${controller.productId}') ], ), ), diff --git a/lib/app/modules/products/controllers/products_controller.dart b/lib/app/modules/products/controllers/products_controller.dart index 118c7dc8..87fcd845 100644 --- a/lib/app/modules/products/controllers/products_controller.dart +++ b/lib/app/modules/products/controllers/products_controller.dart @@ -1,7 +1,7 @@ import 'package:get/get.dart'; import '../../../../models/product.dart'; - + class ProductsController extends GetxController { final products = [].obs; diff --git a/lib/app/modules/products/views/products_view.dart b/lib/app/modules/products/views/products_view.dart index 5b190a6a..f2d2d107 100644 --- a/lib/app/modules/products/views/products_view.dart +++ b/lib/app/modules/products/views/products_view.dart @@ -1,11 +1,12 @@ -// ignore_for_file: inference_failure_on_function_invocation - import 'package:flutter/material.dart'; import 'package:get/get.dart'; - import '../../../../models/role.dart'; import '../../../routes/app_pages.dart'; import '../controllers/products_controller.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + import '../controllers/products_controller.dart'; + import '../../../../models/product.dart'; class ProductsView extends GetView { const ProductsView({super.key}); @@ -14,44 +15,79 @@ class ProductsView extends GetView { Widget build(BuildContext context) { var arg = Get.rootDelegate.arguments(); return Scaffold( - floatingActionButton: - (arg != null && Get.rootDelegate.arguments()["role"] == Role.seller) - ? FloatingActionButton.extended( - onPressed: controller.loadDemoProductsFromSomeWhere, - label: const Text('Add'), - ) - : null, - body: Column( - children: [ - const Hero( - tag: 'heroLogo', - child: FlutterLogo(), + appBar: AppBar( + title: const Text('Products'), + backgroundColor: Colors.orange, + elevation: 0, + ), + floatingActionButton: (arg != null && Get.rootDelegate.arguments()["role"] == Role.seller) + ? FloatingActionButton.extended( + onPressed: controller.loadDemoProductsFromSomeWhere, + label: const Text('Add Product'), + backgroundColor: Colors.orange, + icon: const Icon(Icons.add), + ) + : null, + body: Obx( + () => RefreshIndicator( + onRefresh: () async { + controller.products.clear(); + controller.loadDemoProductsFromSomeWhere(); + }, + child: ListView.builder( + padding: const EdgeInsets.all(16.0), + itemCount: controller.products.length, + itemBuilder: (context, index) { + final item = controller.products[index]; + return _buildProductCard(item); + }, + ), + ), + ), + ); + } + + Widget _buildProductCard(Product item) { + return Container( + margin: const EdgeInsets.only(bottom: 16.0), + child: Card( + elevation: 5, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15), + ), + child: ListTile( + contentPadding: const EdgeInsets.all(16.0), + leading: CircleAvatar( + backgroundColor: Colors.orange.shade100, + child: Icon( + Icons.local_cafe, + size: 40, + color: Colors.brown.shade700, + ), ), - Expanded( - child: Obx( - () => RefreshIndicator( - onRefresh: () async { - controller.products.clear(); - controller.loadDemoProductsFromSomeWhere(); - }, - child: ListView.builder( - itemCount: controller.products.length, - itemBuilder: (context, index) { - final item = controller.products[index]; - return ListTile( - onTap: () { - Get.rootDelegate.toNamed(Routes.PRODUCT_DETAILS( - item.id)); //we could use Get Parameters - }, - title: Text(item.name), - subtitle: Text(item.id), - ); - }, - ), - ), + title: Text( + item.name, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.brown.shade700, ), ), - ], + subtitle: Text( + item.id, + style: TextStyle( + fontSize: 14, + color: Colors.brown.shade500, + ), + ), + onTap: () { + Get.rootDelegate.toNamed(Routes.PRODUCT_DETAILS(item.id)); + }, + tileColor: Colors.orange.shade50, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15), + ), + ), ), ); } diff --git a/lib/app/modules/register/views/register_view.dart b/lib/app/modules/register/views/register_view.dart index 01f73e88..44be69f6 100644 --- a/lib/app/modules/register/views/register_view.dart +++ b/lib/app/modules/register/views/register_view.dart @@ -1,53 +1,83 @@ -// import 'package:firebase_ui_auth/firebase_ui_auth.dart'; import 'package:flutter/material.dart'; - import 'package:get/get.dart'; +import 'package:google_sign_in/google_sign_in.dart'; import '../../../../services/auth_service.dart'; -// import '../../../widgets/login_widgets.dart'; import '../controllers/register_controller.dart'; -//ALso add a form to take additional info such as display name of other customer details mapped with uid in Firestore class RegisterView extends GetView { const RegisterView({super.key}); @override Widget build(BuildContext context) { - // Add pre verification Form if any. Mostly it can be post verification and can be the Profile or Setting screens - try { - // using this is causing an error when we send verification mail from server side - // if it was initiated once, even when no visible. So we need to dispose when not visible - var w = - // EmailVerificationScreen( - // headerBuilder: LoginWidgets.headerBuilder, - // sideBuilder: LoginWidgets.sideBuilder, - // actions: [ - // EmailVerifiedAction(() { - // AuthService.to.register(); - // }), - // ], - // ); - Scaffold( - appBar: AppBar( - title: const Text('Registeration'), - centerTitle: true, - ), - body: Center( - child: Column(children: [ - const Text( - 'Please verify your email (check SPAM folder), and then relogin', - style: TextStyle(fontSize: 20), + return Scaffold( + appBar: AppBar( + title: const Text('Registration'), + centerTitle: true, + backgroundColor: Colors.orange, + elevation: 0, + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon( + Icons.email, + size: 80, + color: Colors.orange, + ), + const SizedBox(height: 20), + const Text( + 'Please verify your email (check SPAM folder), and then relogin', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: Colors.brown, + ), + ), + const SizedBox(height: 20), + ElevatedButton( + onPressed: () => AuthService.to.register(), + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), + textStyle: const TextStyle(fontSize: 16), + ), + child: const Text("Verification Done. Relogin"), + ), + const SizedBox(height: 20), + ElevatedButton( + onPressed: () => _signInWithGoogle(), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, // Google Sign-In button color + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), + textStyle: const TextStyle(fontSize: 16), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Image.asset('assets/images/google_logo.png', width: 24), + const SizedBox(width: 8), + const Text("Sign in with Google"), + ], + ), + ), + ], ), - TextButton( - onPressed: () => AuthService.to.register(), - child: const Text("Verification Done. Relogin"), - ) - ])), - ); - return w; + ), + ), + ); + } + + void _signInWithGoogle() async { + try { + await AuthService.to.signInWithGoogle(); } catch (e) { - // TODO + // Handle errors appropriately, e.g., show an error message + Get.snackbar('Sign-In Error', e.toString(), backgroundColor: Colors.red, colorText: Colors.white); } - return const Scaffold(); } } diff --git a/lib/app/modules/settings/views/settings_view.dart b/lib/app/modules/settings/views/settings_view.dart index 2bb244b6..b70a3f04 100644 --- a/lib/app/modules/settings/views/settings_view.dart +++ b/lib/app/modules/settings/views/settings_view.dart @@ -1,7 +1,5 @@ import 'package:flutter/material.dart'; - import 'package:get/get.dart'; - import '../controllers/settings_controller.dart'; class SettingsView extends GetView { @@ -9,11 +7,130 @@ class SettingsView extends GetView { @override Widget build(BuildContext context) { - return const Scaffold( - body: Center( - child: Text( - 'SettingsView is working', - style: TextStyle(fontSize: 20), + return Scaffold( + appBar: AppBar( + title: Text( + 'Settings', + style: Theme.of(context).textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.w600, + ), + ), + backgroundColor: Colors.teal, + elevation: 4, + ), + body: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildProfileSection(context), + const SizedBox(height: 20), + _buildSettingOption( + context: context, + icon: Icons.notifications_active, + title: 'Notifications', + onTap: () { + // Add your onTap logic here + }, + ), + _buildSettingOption( + context: context, + icon: Icons.lock, + title: 'Security', + onTap: () { + // Add your onTap logic here + }, + ), + _buildSettingOption( + context: context, + icon: Icons.help, + title: 'Help & Support', + onTap: () { + // Add your onTap logic here + }, + ), + const Spacer(), + _buildContactSupportButton(context), + ], + ), + ), + ); + } + + Widget _buildProfileSection(BuildContext context) { + return Container( + padding: const EdgeInsets.all(16.0), + decoration: BoxDecoration( + color: Colors.blueGrey[50], + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 8, + offset: const Offset(0, 4), + ), + ], + ), + child: Row( + children: [ + CircleAvatar( + radius: 32, + backgroundColor: Colors.teal, + child: Icon(Icons.person, size: 32, color: Colors.white), + ), + const SizedBox(width: 16), + Expanded( + child: Text( + 'Jinal Gulhane', + style: Theme.of(context).textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.w500, + ), + ), + ), + ], + ), + ); + } + + Widget _buildSettingOption({ + required BuildContext context, + required IconData icon, + required String title, + required VoidCallback onTap, + }) { + return Card( + margin: const EdgeInsets.symmetric(vertical: 8.0), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 3, + child: ListTile( + contentPadding: const EdgeInsets.symmetric(horizontal: 16.0), + leading: Icon(icon, color: Colors.teal), + title: Text(title), + trailing: Icon(Icons.chevron_right, color: Colors.grey), + onTap: onTap, + ), + ); + } + + Widget _buildContactSupportButton(BuildContext context) { + return ElevatedButton( + onPressed: () { + // Implement contact support logic here + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.teal, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + padding: const EdgeInsets.symmetric(vertical: 16.0), + ), + child: Text( + 'Contact Support', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w600, ), ), ); diff --git a/lib/app/routes/app_pages.dart b/lib/app/routes/app_pages.dart index 7269755d..01f5b44f 100644 --- a/lib/app/routes/app_pages.dart +++ b/lib/app/routes/app_pages.dart @@ -79,7 +79,7 @@ class AppPages { ], children: [ Screen.DASHBOARD.getPage( - page: () => const DashboardView(), + page: () => DashboardView(), binding: DashboardBinding(), ), Screen.USERS.getPage( diff --git a/lib/services/auth_service.dart b/lib/services/auth_service.dart index 8bf72aaa..1e6a1890 100644 --- a/lib/services/auth_service.dart +++ b/lib/services/auth_service.dart @@ -1,10 +1,9 @@ -// ignore_for_file: avoid_print - import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_ui_auth/firebase_ui_auth.dart' as fbui; import 'package:firebase_ui_localizations/firebase_ui_localizations.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:google_sign_in/google_sign_in.dart'; import '../models/screens.dart'; import '../constants.dart'; @@ -14,6 +13,7 @@ class AuthService extends GetxService { static AuthService get to => Get.find(); final FirebaseAuth _auth = FirebaseAuth.instance; + final GoogleSignIn _googleSignIn = GoogleSignIn(); late Rxn credential = Rxn(); final Rxn _firebaseUser = Rxn(); final Rx _userRole = Rx(Role.buyer); @@ -24,7 +24,7 @@ class AuthService extends GetxService { Role get maxRole => _userRole.value; @override - onInit() { + void onInit() { super.onInit(); if (useEmulator) _auth.useAuthEmulator(emulatorHost, 9099); _firebaseUser.bindStream(_auth.authStateChanges()); @@ -52,8 +52,20 @@ class AuthService extends GetxService { ? (user!.displayName ?? user!.email) : 'Guest'; - void login() { - // this is not needed as we are using Firebase UI for the login part + Future signInWithGoogle() async { + try { + final GoogleUser = await _googleSignIn.signIn(); + final GoogleAuth = await GoogleUser!.authentication; + + final credential = GoogleAuthProvider.credential( + accessToken: GoogleAuth.accessToken, + idToken: GoogleAuth.idToken, + ); + + await _auth.signInWithCredential(credential); + } catch (e) { + print('Google sign-in error: ${e.toString()}'); + } } void sendVerificationMail({EmailAuthCredential? emailAuth}) async { @@ -61,13 +73,6 @@ class AuthService extends GetxService { if (_auth.currentUser != null) { await _auth.currentUser?.sendEmailVerification(); } else if (emailAuth != null) { - // Approach 1: sending email auth link requires deep linking which is - // a TODO as the current Flutter methods are deprecated - // sendSingInLink(emailAuth); - - // Approach 2: This is a hack. - // We are using createUser to send the verification link from the server side by adding suffix .verify in the email - // if the user already exists and the password is also same and sign in occurs via custom token on server side try { await _auth.createUserWithEmailAndPassword( email: "${credential.value!.email}.verify", @@ -84,21 +89,11 @@ class AuthService extends GetxService { } } - void sendSingInLink(EmailAuthCredential emailAuth) { + void sendSignInLink(EmailAuthCredential emailAuth) { var acs = ActionCodeSettings( - // URL you want to redirect back to. The domain (www.example.com) for this - // URL must be whitelisted in the Firebase Console. url: '$baseUrl:5001/flutterfast-92c25/us-central1/handleEmailLinkVerification', - // // This must be true if deep linking. - // // If deeplinking. See [https://firebase.google.com/docs/dynamic-links/flutter/receive] handleCodeInApp: true, - // iOSBundleId: '$bundleID.ios', - // androidPackageName: '$bundleID.android', - // // installIfNotAvailable - // androidInstallApp: true, - // // minimumVersion - // androidMinimumVersion: '12' ); _auth .sendSignInLinkToEmail(email: emailAuth.email, actionCodeSettings: acs) @@ -109,15 +104,15 @@ class AuthService extends GetxService { void register() { registered.value = true; - // logout(); // Uncomment if we need to enforce relogin final thenTo = Get.rootDelegate.currentConfiguration!.currentPage!.parameters?['then']; Get.rootDelegate - .offAndToNamed(thenTo ?? Screen.PROFILE.route); //Profile has the forms + .offAndToNamed(thenTo ?? Screen.PROFILE.route); // Profile has the forms } void logout() { _auth.signOut(); + _googleSignIn.signOut(); // Ensure Google Sign-In is also signed out if (isAnon) _auth.currentUser?.delete(); _firebaseUser.value = null; } @@ -158,12 +153,9 @@ class AuthService extends GetxService { (BuildContext context, FirebaseAuthException e) { final defaultLabels = FirebaseUILocalizations.labelsOf(context); - // for verification error, also set a boolean flag to trigger button visibility to resend verification mail String? verification; if (e.code == "internal-error" && e.message!.contains('"status":"UNAUTHENTICATED"')) { - // Note that (possibly in Emulator only) the e.email is always coming as null - // String? email = e.email ?? parseEmail(e.message!); callback(true, credential.value); verification = "Please verify email id by clicking the link on the email sent"; diff --git a/pubspec.lock b/pubspec.lock index 877fc75e..90465048 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -25,6 +25,62 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + audioplayers: + dependency: "direct main" + description: + name: audioplayers + sha256: c346ba5a39dc208f1bab55fc239855f573d69b0e832402114bf0b793622adc4d + url: "https://pub.dev" + source: hosted + version: "6.1.0" + audioplayers_android: + dependency: transitive + description: + name: audioplayers_android + sha256: de576b890befe27175c2f511ba8b742bec83765fa97c3ce4282bba46212f58e4 + url: "https://pub.dev" + source: hosted + version: "5.0.0" + audioplayers_darwin: + dependency: transitive + description: + name: audioplayers_darwin + sha256: e507887f3ff18d8e5a10a668d7bedc28206b12e10b98347797257c6ae1019c3b + url: "https://pub.dev" + source: hosted + version: "6.0.0" + audioplayers_linux: + dependency: transitive + description: + name: audioplayers_linux + sha256: "3d3d244c90436115417f170426ce768856d8fe4dfc5ed66a049d2890acfa82f9" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + audioplayers_platform_interface: + dependency: transitive + description: + name: audioplayers_platform_interface + sha256: "6834dd48dfb7bc6c2404998ebdd161f79cd3774a7e6779e1348d54a3bfdcfaa5" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + audioplayers_web: + dependency: transitive + description: + name: audioplayers_web + sha256: "3609bdf0e05e66a3d9750ee40b1e37f2a622c4edb796cc600b53a90a30a2ace4" + url: "https://pub.dev" + source: hosted + version: "5.0.1" + audioplayers_windows: + dependency: transitive + description: + name: audioplayers_windows + sha256: "8605762dddba992138d476f6a0c3afd9df30ac5b96039929063eceed416795c2" + url: "https://pub.dev" + source: hosted + version: "4.0.0" boolean_selector: dependency: transitive description: @@ -113,6 +169,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" file_picker: dependency: "direct main" description: @@ -329,6 +393,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" flutter: dependency: "direct main" description: flutter @@ -537,18 +609,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -577,18 +649,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" mime: dependency: transitive description: @@ -698,6 +770,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" stack_trace: dependency: transitive description: @@ -722,6 +802,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: a824e842b8a054f91a728b783c177c1e4731f6b124f9192468457a8913371255 + url: "https://pub.dev" + source: hosted + version: "3.2.0" term_glyph: dependency: transitive description: @@ -734,10 +822,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" typed_data: dependency: transitive description: @@ -746,6 +834,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + uuid: + dependency: transitive + description: + name: uuid + sha256: f33d6bb662f0e4f79dcd7ada2e6170f3b3a2530c28fc41f49a411ddedd576a77 + url: "https://pub.dev" + source: hosted + version: "4.5.0" vector_graphics: dependency: transitive description: @@ -782,10 +878,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.4" web: dependency: transitive description: @@ -819,5 +915,5 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.3.4 <4.0.0" + dart: ">=3.5.0 <4.0.0" flutter: ">=3.19.2" diff --git a/pubspec.yaml b/pubspec.yaml index 2909a374..06fdef67 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,13 +2,13 @@ name: get_flutter_fire version: 1.0.0+1 publish_to: none description: Boilerplate for Flutter with GetX, showing sample utilization of Firebase capabilities -environment: +environment: sdk: '>=3.3.4 <4.0.0' -dependencies: +dependencies: cupertino_icons: ^1.0.6 - get: 4.6.6 - flutter: + get: ^4.6.6 + flutter: sdk: flutter firebase_core: ^2.31.0 firebase_ui_auth: ^1.14.0 @@ -24,13 +24,14 @@ dependencies: firebase_ui_localizations: ^1.12.0 firebase_remote_config: ^4.4.7 firebase_analytics: ^10.10.7 + audioplayers: ^6.1.0 -dev_dependencies: - flutter_lints: 3.0.2 - flutter_test: +dev_dependencies: + flutter_lints: ^3.0.2 + flutter_test: sdk: flutter -flutter: +flutter: fonts: - family: SocialIcons fonts: @@ -40,4 +41,3 @@ flutter: - assets/images/dash.png - assets/icons/logo.png uses-material-design: true - diff --git a/web/favicon.jpeg b/web/favicon.jpeg new file mode 100644 index 00000000..404173b9 Binary files /dev/null and b/web/favicon.jpeg differ