From 21aa7bd30f2ff060596171740e61cdaba1767c7b Mon Sep 17 00:00:00 2001 From: mohamadmahdi jebeli Date: Mon, 21 Jul 2025 16:34:52 +0330 Subject: [PATCH] connect to firebase --- android/app/build.gradle.kts | 3 + android/app/google-services.json | 29 +++++ android/app/src/main/AndroidManifest.xml | 1 + android/settings.gradle.kts | 3 + firebase.json | 1 + lib/firebase_options.dart | 75 +++++++++++++ lib/main.dart | 10 +- lib/presentation/auth/bloc/auth_bloc.dart | 10 +- lib/presentation/auth/bloc/auth_state.dart | 10 +- lib/presentation/pages/add_discount_page.dart | 13 ++- lib/presentation/pages/home_page.dart | 15 ++- lib/presentation/pages/otp_page.dart | 90 +++++++-------- lib/presentation/pages/splash_page.dart | 37 +++---- .../pages/working_hours_dialog.dart | 6 +- pubspec.lock | 104 ++++++++++++++++++ pubspec.yaml | 4 + 16 files changed, 319 insertions(+), 92 deletions(-) create mode 100644 android/app/google-services.json create mode 100644 firebase.json create mode 100644 lib/firebase_options.dart diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 3230651..2a92b30 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -1,5 +1,8 @@ plugins { id("com.android.application") + // START: FlutterFire Configuration + id("com.google.gms.google-services") + // END: FlutterFire Configuration id("kotlin-android") // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. id("dev.flutter.flutter-gradle-plugin") diff --git a/android/app/google-services.json b/android/app/google-services.json new file mode 100644 index 0000000..fba7e84 --- /dev/null +++ b/android/app/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "800272350428", + "project_id": "proxibuy-3b5e0", + "storage_bucket": "proxibuy-3b5e0.firebasestorage.app" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:800272350428:android:d6af1e013bae09d9c78819", + "android_client_info": { + "package_name": "com.example.business_panel" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyCMGweIbZBsFNXabKRJJJcVLPwcmqhuSwg" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 8f06452..d59eecf 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ + diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts index a439442..9e2d35c 100644 --- a/android/settings.gradle.kts +++ b/android/settings.gradle.kts @@ -19,6 +19,9 @@ pluginManagement { plugins { id("dev.flutter.flutter-plugin-loader") version "1.0.0" id("com.android.application") version "8.7.0" apply false + // START: FlutterFire Configuration + id("com.google.gms.google-services") version("4.3.15") apply false + // END: FlutterFire Configuration id("org.jetbrains.kotlin.android") version "1.8.22" apply false } diff --git a/firebase.json b/firebase.json new file mode 100644 index 0000000..8deb63f --- /dev/null +++ b/firebase.json @@ -0,0 +1 @@ +{"flutter":{"platforms":{"android":{"default":{"projectId":"proxibuy-3b5e0","appId":"1:800272350428:android:d6af1e013bae09d9c78819","fileOutput":"android/app/google-services.json"}},"dart":{"lib/firebase_options.dart":{"projectId":"proxibuy-3b5e0","configurations":{"android":"1:800272350428:android:d6af1e013bae09d9c78819","ios":"1:800272350428:ios:715e52af5f7d922ac78819","web":"1:800272350428:web:0d3454e8d3783408c78819"}}}}}} \ No newline at end of file diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart new file mode 100644 index 0000000..fabdfa7 --- /dev/null +++ b/lib/firebase_options.dart @@ -0,0 +1,75 @@ +// File generated by FlutterFire CLI. +// ignore_for_file: type=lint +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: 'AIzaSyBR7LPmu-0_pG3aRwP0SAg9Xh1DKFMl76s', + appId: '1:800272350428:web:0d3454e8d3783408c78819', + messagingSenderId: '800272350428', + projectId: 'proxibuy-3b5e0', + authDomain: 'proxibuy-3b5e0.firebaseapp.com', + storageBucket: 'proxibuy-3b5e0.firebasestorage.app', + measurementId: 'G-XVGY4HZ5M6', + ); + + static const FirebaseOptions android = FirebaseOptions( + apiKey: 'AIzaSyCMGweIbZBsFNXabKRJJJcVLPwcmqhuSwg', + appId: '1:800272350428:android:d6af1e013bae09d9c78819', + messagingSenderId: '800272350428', + projectId: 'proxibuy-3b5e0', + storageBucket: 'proxibuy-3b5e0.firebasestorage.app', + ); + + static const FirebaseOptions ios = FirebaseOptions( + apiKey: 'AIzaSyCspVOPpX4shZThL5jaqy-BwOtveVDUsQs', + appId: '1:800272350428:ios:715e52af5f7d922ac78819', + messagingSenderId: '800272350428', + projectId: 'proxibuy-3b5e0', + storageBucket: 'proxibuy-3b5e0.firebasestorage.app', + iosBundleId: 'com.example.businessPanel', + ); +} diff --git a/lib/main.dart b/lib/main.dart index 8dbd866..e0ffce9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,8 +7,16 @@ import 'package:flutter_bloc/flutter_bloc.dart'; // ignore: depend_on_referenced_packages import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:persian_datetime_picker/persian_datetime_picker.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'firebase_options.dart'; + + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform, + ); -void main() { runApp(const MyApp()); } diff --git a/lib/presentation/auth/bloc/auth_bloc.dart b/lib/presentation/auth/bloc/auth_bloc.dart index 5bd65d1..3be247d 100644 --- a/lib/presentation/auth/bloc/auth_bloc.dart +++ b/lib/presentation/auth/bloc/auth_bloc.dart @@ -1,4 +1,4 @@ -import 'dart:io'; // کتابخانه برای SocketException اضافه شد +import 'dart:io'; import 'package:bloc/bloc.dart'; import 'package:business_panel/core/config/api_config.dart'; import 'package:business_panel/core/services/token_storage_service.dart'; @@ -30,24 +30,24 @@ class AuthBloc extends Bloc { return; } - await _dio.get( + final response = await _dio.get( ApiConfig.checkShopStatus, options: Options( headers: {'Authorization': 'Bearer $token'}, ), ); - emit(ShopExists()); + // *** CHANGE IS HERE: Parse shop name from response *** + final shopName = response.data?['data']?['Name'] as String? ?? 'فروشگاه شما'; + emit(ShopExists(shopName)); } on DioException catch (e) { - // --- منطق تشخیص آفلاین بودن بهبود یافت --- final isOffline = e.error is SocketException || e.type == DioExceptionType.connectionError || e.type == DioExceptionType.sendTimeout || e.type == DioExceptionType.receiveTimeout; if (isOffline) { - // اگر کاربر آفلاین باشد، این حالت صادر می‌شود emit(AuthOffline()); } else if (e.response?.statusCode == 404) { emit(NoShop()); diff --git a/lib/presentation/auth/bloc/auth_state.dart b/lib/presentation/auth/bloc/auth_state.dart index 46fea13..c863b23 100644 --- a/lib/presentation/auth/bloc/auth_state.dart +++ b/lib/presentation/auth/bloc/auth_state.dart @@ -6,20 +6,18 @@ class AuthInitial extends AuthState {} class AuthLoading extends AuthState {} -// ADDED: State to indicate the result of the token check class AuthChecked extends AuthState { final bool hasToken; AuthChecked(this.hasToken); } -// ADDED: State for when the user has a shop -class ShopExists extends AuthState {} +class ShopExists extends AuthState { + final String shopName; + ShopExists(this.shopName); +} -// ADDED: State for when the user is logged in but has no shop class NoShop extends AuthState {} -// *** CHANGE IS HERE: Added state for offline mode *** -// ADDED: State for when the user is authenticated but offline class AuthOffline extends AuthState {} class AuthCodeSentSuccess extends AuthState {} diff --git a/lib/presentation/pages/add_discount_page.dart b/lib/presentation/pages/add_discount_page.dart index 1fedd03..351d1f5 100644 --- a/lib/presentation/pages/add_discount_page.dart +++ b/lib/presentation/pages/add_discount_page.dart @@ -5,6 +5,8 @@ import 'package:business_panel/gen/assets.gen.dart'; import 'package:business_panel/presentation/discount/bloc/discount_bloc.dart'; import 'package:business_panel/presentation/discount/bloc/discount_event.dart'; import 'package:business_panel/presentation/discount/bloc/discount_state.dart'; +import 'package:business_panel/presentation/home/bloc/home_bloc.dart'; +import 'package:business_panel/presentation/pages/home_page.dart'; import 'package:business_panel/presentation/widgets/custom_app_bar.dart'; import 'package:business_panel/presentation/widgets/info_popup.dart'; import 'package:flutter/material.dart'; @@ -124,7 +126,16 @@ class _AddDiscountViewState extends State<_AddDiscountView> { "تخفیف با موفقیت ${_isEditMode ? 'ویرایش' : 'ثبت'} شد!"), backgroundColor: Colors.green), ); - Navigator.of(context).pop(true); + // HIGHLIGHT: مسیریابی به صفحه‌ی اصلی و حذف تمام صفحات قبلی + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (_) => BlocProvider( + create: (context) => HomeBloc()..add(FetchDiscounts()), + child: const HomePage(), + ), + ), + (route) => false, + ); } if (state.errorMessage != null && !state.isLoadingDetails) { ScaffoldMessenger.of(context).showSnackBar( diff --git a/lib/presentation/pages/home_page.dart b/lib/presentation/pages/home_page.dart index 94c5d67..1f5a646 100644 --- a/lib/presentation/pages/home_page.dart +++ b/lib/presentation/pages/home_page.dart @@ -9,7 +9,9 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; class HomePage extends StatelessWidget { - const HomePage({super.key}); + // *** CHANGE IS HERE: Added shopName parameter *** + final String? shopName; + const HomePage({super.key, this.shopName}); @override Widget build(BuildContext context) { @@ -17,7 +19,6 @@ class HomePage extends StatelessWidget { appBar: const CustomAppBar(), body: BlocBuilder( builder: (context, state) { - // --- منطق اصلاح‌شده برای مدیریت وضعیت --- if (state is HomeError) { return Center( child: Padding( @@ -29,7 +30,8 @@ class HomePage extends StatelessWidget { if (state is HomeLoaded) { if (state.discounts.isEmpty) { - return _buildEmptyState(context); + // *** CHANGE IS HERE: Pass shopName to empty state *** + return _buildEmptyState(context, shopName); } return RefreshIndicator( onRefresh: () async { @@ -55,14 +57,17 @@ class HomePage extends StatelessWidget { ); } - Widget _buildEmptyState(BuildContext context) { + Widget _buildEmptyState(BuildContext context, String? shopName) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SvgPicture.asset(Assets.icons.emptyHome, height: 300), const SizedBox(height: 35), - const Text("سلام!", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 17)), + Text( + "سلام ${shopName ?? 'کاربر'}", + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 17), + ), const SizedBox(height: 15), Padding( padding: const EdgeInsets.symmetric(horizontal: 24.0), diff --git a/lib/presentation/pages/otp_page.dart b/lib/presentation/pages/otp_page.dart index 9d2d2ad..54ce383 100644 --- a/lib/presentation/pages/otp_page.dart +++ b/lib/presentation/pages/otp_page.dart @@ -144,8 +144,6 @@ class _OtpPageState extends State { ) else const SizedBox(height: 32), - - // *** CHANGE IS HERE *** BlocConsumer( listener: (context, state) { if (state is AuthFailure) { @@ -158,25 +156,21 @@ class _OtpPageState extends State { if (state is ShopExists) { Navigator.of(context).pushReplacement( MaterialPageRoute( - builder: - (_) => BlocProvider( - // بلافاصله پس از ایجاد، رویداد دریافت تخفیف‌ها را ارسال می‌کند - create: - (context) => - HomeBloc()..add(FetchDiscounts()), - child: const HomePage(), - ), + builder: (_) => BlocProvider( + create: (context) => HomeBloc()..add(FetchDiscounts()), + // *** CHANGE IS HERE: Pass shopName to HomePage constructor *** + child: HomePage(shopName: state.shopName), + ), ), ); } if (state is NoShop) { Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( - builder: - (_) => BlocProvider( - create: (context) => StoreInfoBloc(), - child: const StoreInfoPage(), - ), + builder: (_) => BlocProvider( + create: (context) => StoreInfoBloc(), + child: const StoreInfoPage(), + ), ), (route) => false, ); @@ -212,20 +206,19 @@ class _OtpPageState extends State { builder: (context, canResend, child) { return canResend ? TextButton( - onPressed: _resendOtp, - child: const Text( - "ارسال مجدد کد", - style: TextStyle(color: AppColors.active), - ), - ) + onPressed: _resendOtp, + child: const Text( + "ارسال مجدد کد", + style: TextStyle(color: AppColors.active), + ), + ) : ValueListenableBuilder( - valueListenable: _otpTimer.remainingSeconds, - builder: - (context, seconds, child) => Text( - "${_otpTimer.formatTime()} تا دریافت مجدد", - style: const TextStyle(color: Colors.grey), - ), - ); + valueListenable: _otpTimer.remainingSeconds, + builder: (context, seconds, child) => Text( + "${_otpTimer.formatTime()} تا دریافت مجدد", + style: const TextStyle(color: Colors.grey), + ), + ); }, ), ], @@ -263,15 +256,14 @@ class _OtpPageState extends State { enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( - color: - _hasError - ? Colors.red - : (Theme.of(context) - .inputDecorationTheme - .enabledBorder - ?.borderSide - .color ?? - Colors.grey), + color: _hasError + ? Colors.red + : (Theme.of(context) + .inputDecorationTheme + .enabledBorder + ?.borderSide + .color ?? + Colors.grey), ), ), focusedBorder: OutlineInputBorder( @@ -311,12 +303,12 @@ class _OtpPageState extends State { final otpCode = _controllers.map((c) => c.text).join(); if (otpCode.length == 5) { context.read().add( - VerifyOTPEvent( - otp: otpCode, - phoneNumber: widget.phoneNumber, - countryCode: widget.countryCode, - ), - ); + VerifyOTPEvent( + otp: otpCode, + phoneNumber: widget.phoneNumber, + countryCode: widget.countryCode, + ), + ); } } @@ -330,11 +322,11 @@ class _OtpPageState extends State { _isOtpComplete = false; }); context.read().add( - SendOTPEvent( - phoneNumber: widget.phoneNumber, - countryCode: widget.countryCode, - ), - ); + SendOTPEvent( + phoneNumber: widget.phoneNumber, + countryCode: widget.countryCode, + ), + ); _otpTimer.resetTimer(); } -} +} \ No newline at end of file diff --git a/lib/presentation/pages/splash_page.dart b/lib/presentation/pages/splash_page.dart index 78547b7..c2d9953 100644 --- a/lib/presentation/pages/splash_page.dart +++ b/lib/presentation/pages/splash_page.dart @@ -36,36 +36,29 @@ class _SplashPageState extends State { } else if (state is ShopExists) { Navigator.of(context).pushReplacement( MaterialPageRoute( - builder: - (_) => BlocProvider( - // بلافاصله پس از ایجاد، رویداد دریافت تخفیف‌ها را ارسال می‌کند - create: (context) => HomeBloc()..add(FetchDiscounts()), - child: const HomePage(), - ), + builder: (_) => BlocProvider( + create: (context) => HomeBloc()..add(FetchDiscounts()), + // *** CHANGE IS HERE: Pass shopName to HomePage constructor *** + child: HomePage(shopName: state.shopName), + ), ), ); - } - else if (state is AuthOffline) { - // کاربر توکن دارد ولی آفلاین است، به HomePage می‌رود - // HomePage خودش خطای شبکه را مدیریت خواهد کرد + } else if (state is AuthOffline) { Navigator.of(context).pushReplacement( MaterialPageRoute( - builder: - (_) => BlocProvider( - create: (context) => HomeBloc(), - child: const HomePage(), - ), + builder: (_) => BlocProvider( + create: (context) => HomeBloc(), + child: const HomePage(), + ), ), ); } else if (state is NoShop) { - // کاربر توکن دارد ولی فروشگاه نساخته، به صفحه ساخت فروشگاه می‌رود Navigator.of(context).pushReplacement( MaterialPageRoute( - builder: - (_) => BlocProvider( - create: (context) => StoreInfoBloc(), - child: const StoreInfoPage(), - ), + builder: (_) => BlocProvider( + create: (context) => StoreInfoBloc(), + child: const StoreInfoPage(), + ), ), ); } else if (state is AuthFailure) { @@ -77,4 +70,4 @@ class _SplashPageState extends State { child: const Scaffold(body: Center(child: CircularProgressIndicator())), ); } -} +} \ No newline at end of file diff --git a/lib/presentation/pages/working_hours_dialog.dart b/lib/presentation/pages/working_hours_dialog.dart index 6dc849c..9c8d3e1 100644 --- a/lib/presentation/pages/working_hours_dialog.dart +++ b/lib/presentation/pages/working_hours_dialog.dart @@ -53,12 +53,12 @@ class _WorkingHoursDialogState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - _buildTimePicker("پایان", _endTime, (time) { - setState(() => _endTime = time); - }), _buildTimePicker("شروع", _startTime, (time) { setState(() => _startTime = time); }), + _buildTimePicker("پایان", _endTime, (time) { + setState(() => _endTime = time); + }), ], ), ], diff --git a/pubspec.lock b/pubspec.lock index 4d94239..0dd74af 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -9,6 +9,14 @@ packages: url: "https://pub.dev" source: hosted version: "85.0.0" + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + sha256: ff0a84a2734d9e1089f8aedd5c0af0061b82fb94e95260d943404e0ef2134b11 + url: "https://pub.dev" + source: hosted + version: "1.3.59" analyzer: dependency: transitive description: @@ -145,6 +153,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.2" + cloud_firestore: + dependency: "direct main" + description: + name: cloud_firestore + sha256: "2d33da4465bdb81b6685c41b535895065adcb16261beb398f5f3bbc623979e9c" + url: "https://pub.dev" + source: hosted + version: "5.6.12" + cloud_firestore_platform_interface: + dependency: transitive + description: + name: cloud_firestore_platform_interface + sha256: "413c4e01895cf9cb3de36fa5c219479e06cd4722876274ace5dfc9f13ab2e39b" + url: "https://pub.dev" + source: hosted + version: "6.6.12" + cloud_firestore_web: + dependency: transitive + description: + name: cloud_firestore_web + sha256: c1e30fc4a0fcedb08723fb4b1f12ee4e56d937cbf9deae1bda43cbb6367bb4cf + url: "https://pub.dev" + source: hosted + version: "4.4.12" code_builder: dependency: transitive description: @@ -321,6 +353,78 @@ packages: url: "https://pub.dev" source: hosted version: "0.9.3+4" + firebase_auth: + dependency: "direct main" + description: + name: firebase_auth + sha256: "0fed2133bee1369ee1118c1fef27b2ce0d84c54b7819a2b17dada5cfec3b03ff" + url: "https://pub.dev" + source: hosted + version: "5.7.0" + firebase_auth_platform_interface: + dependency: transitive + description: + name: firebase_auth_platform_interface + sha256: "871c9df4ec9a754d1a793f7eb42fa3b94249d464cfb19152ba93e14a5966b386" + url: "https://pub.dev" + source: hosted + version: "7.7.3" + firebase_auth_web: + dependency: transitive + description: + name: firebase_auth_web + sha256: d9ada769c43261fd1b18decf113186e915c921a811bd5014f5ea08f4cf4bc57e + url: "https://pub.dev" + source: hosted + version: "5.15.3" + firebase_core: + dependency: "direct main" + description: + name: firebase_core + sha256: "7be63a3f841fc9663342f7f3a011a42aef6a61066943c90b1c434d79d5c995c5" + url: "https://pub.dev" + source: hosted + version: "3.15.2" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + sha256: "5dbc900677dcbe5873d22ad7fbd64b047750124f1f9b7ebe2a33b9ddccc838eb" + url: "https://pub.dev" + source: hosted + version: "6.0.0" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + sha256: "0ed0dc292e8f9ac50992e2394e9d336a0275b6ae400d64163fdf0a8a8b556c37" + url: "https://pub.dev" + source: hosted + version: "2.24.1" + firebase_storage: + dependency: "direct main" + description: + name: firebase_storage + sha256: "958fc88a7ef0b103e694d30beed515c8f9472dde7e8459b029d0e32b8ff03463" + url: "https://pub.dev" + source: hosted + version: "12.4.10" + firebase_storage_platform_interface: + dependency: transitive + description: + name: firebase_storage_platform_interface + sha256: d2661c05293c2a940c8ea4bc0444e1b5566c79dd3202c2271140c082c8cd8dd4 + url: "https://pub.dev" + source: hosted + version: "5.2.10" + firebase_storage_web: + dependency: transitive + description: + name: firebase_storage_web + sha256: "629a557c5e1ddb97a3666cbf225e97daa0a66335dbbfdfdce113ef9f881e833f" + url: "https://pub.dev" + source: hosted + version: "3.10.17" fixnum: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ea2895e..ed506de 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,6 +49,10 @@ dependencies: dio: ^5.8.0+1 flutter_secure_storage: ^9.2.4 slide_countdown: ^2.0.2 + firebase_core: ^3.15.2 + firebase_auth: ^5.7.0 + cloud_firestore: ^5.6.12 + firebase_storage: ^12.4.10 dev_dependencies: