diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 7832420..056699b 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -4,8 +4,13 @@
+
+
+
+
diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml
index 1d14ce6..234b328 100644
--- a/android/app/src/profile/AndroidManifest.xml
+++ b/android/app/src/profile/AndroidManifest.xml
@@ -4,4 +4,7 @@
to allow setting breakpoints, to provide hot reload, etc.
-->
+
+
+
diff --git a/lib/features/home/presentation/screens/rating_screen.dart b/lib/features/home/presentation/screens/rating_screen.dart
index 90d4a68..c4ff025 100644
--- a/lib/features/home/presentation/screens/rating_screen.dart
+++ b/lib/features/home/presentation/screens/rating_screen.dart
@@ -78,6 +78,7 @@ class _RatingScreenState extends State {
}
void _submitRating(BuildContext context) {
+ // ignore: unused_local_variable
final String comment = _commentController.text.trim();
// Perform the rating submission logic here
diff --git a/lib/features/home/presentation/widgets/for_you_tab.dart b/lib/features/home/presentation/widgets/for_you_tab.dart
index 7f8cf7c..48cd04d 100644
--- a/lib/features/home/presentation/widgets/for_you_tab.dart
+++ b/lib/features/home/presentation/widgets/for_you_tab.dart
@@ -9,12 +9,12 @@ import 'package:eco_bites/features/food/domain/entities/offer.dart';
import 'package:eco_bites/features/food/presentation/bloc/food_business_bloc.dart';
import 'package:eco_bites/features/food/presentation/bloc/food_business_event.dart';
import 'package:eco_bites/features/food/presentation/bloc/food_business_state.dart';
+import 'package:eco_bites/features/home/presentation/screens/rating_screen.dart';
import 'package:eco_bites/features/profile/presentation/bloc/profile_bloc.dart';
import 'package:eco_bites/features/profile/presentation/bloc/profile_state.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:fluttertoast/fluttertoast.dart';
-import 'package:eco_bites/features/home/presentation/screens/rating_screen.dart';
class ForYouTab extends StatelessWidget {
const ForYouTab({super.key});
@@ -70,7 +70,7 @@ class ForYouTab extends StatelessWidget {
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) => RatingScreen(
- restaurantName: foodBusiness.name),
+ restaurantName: foodBusiness.name,),
),
);
},
diff --git a/lib/features/profile/presentation/bloc/profile_bloc.dart b/lib/features/profile/presentation/bloc/profile_bloc.dart
index d615ced..14f592c 100644
--- a/lib/features/profile/presentation/bloc/profile_bloc.dart
+++ b/lib/features/profile/presentation/bloc/profile_bloc.dart
@@ -1,6 +1,7 @@
// ignore_for_file: unused_element
import 'dart:convert';
+import 'dart:io';
import 'package:eco_bites/core/blocs/internet_connection/internet_connection_bloc.dart';
import 'package:eco_bites/core/blocs/resettable_mixin.dart';
import 'package:eco_bites/core/constants/storage_keys.dart';
@@ -11,6 +12,7 @@ import 'package:eco_bites/features/profile/presentation/bloc/profile_event.dart'
import 'package:eco_bites/features/profile/presentation/bloc/profile_state.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:logger/logger.dart';
+import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
class ProfileBloc extends Bloc
@@ -50,6 +52,13 @@ class ProfileBloc extends Bloc
);
Logger().d('Cached profile: ${event.updatedProfile.toMap()}');
Logger().d('Cached profile: ${prefs.getString('cachedProfile')}');
+
+ final Directory cacheDir = await getTemporaryDirectory();
+ final String imagePath = '${cacheDir.path}/profile_image.png';
+ if (File(imagePath).existsSync()) {
+ // Handle cached profile image
+ }
+
emit(
ProfileError(
'No internet connection. Your data will be saved when you are online.',
diff --git a/lib/features/profile/presentation/screens/profile_screen.dart b/lib/features/profile/presentation/screens/profile_screen.dart
index 4477cef..da9bfce 100644
--- a/lib/features/profile/presentation/screens/profile_screen.dart
+++ b/lib/features/profile/presentation/screens/profile_screen.dart
@@ -1,3 +1,5 @@
+import 'dart:io';
+
import 'package:eco_bites/core/blocs/internet_connection/internet_connection_bloc.dart';
import 'package:eco_bites/core/utils/analytics_service.dart';
import 'package:eco_bites/features/auth/presentation/bloc/auth_bloc.dart';
@@ -11,7 +13,10 @@ import 'package:eco_bites/features/profile/presentation/bloc/profile_state.dart'
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart';
+import 'package:path_provider/path_provider.dart';
+import 'package:permission_handler/permission_handler.dart';
import 'package:shared_preferences/shared_preferences.dart';
class ProfileScreen extends StatefulWidget {
@@ -31,10 +36,12 @@ class ProfileScreenState extends State {
CuisineType? _favoriteCuisine;
DietType? _dietType;
bool _isInitialized = false;
+ File? _profileImage;
@override
void initState() {
super.initState();
+ _loadProfileImage();
final String? userId = FirebaseAuth.instance.currentUser?.uid;
if (userId != null) {
context.read().add(LoadProfileEvent(userId));
@@ -45,6 +52,43 @@ class ProfileScreenState extends State {
}
}
+ Future _pickImage() async {
+ try {
+ // Check storage permission
+ if (await Permission.storage.request().isGranted) {
+ final ImagePicker picker = ImagePicker();
+ final XFile? image = await picker.pickImage(source: ImageSource.gallery);
+
+ if (image != null) {
+ setState(() {
+ _profileImage = File(image.path);
+ });
+
+ // Save the profile image to cache
+ final Directory cacheDir = await getTemporaryDirectory();
+ final String imagePath = '${cacheDir.path}/profile_image.png';
+ await _profileImage!.copy(imagePath);
+ }
+ } else {
+ // ignore: avoid_print
+ print('Storage permission not granted.');
+ }
+ } catch (e) {
+ // ignore: avoid_print
+ print('Error picking image: $e');
+ }
+}
+
+ Future _loadProfileImage() async {
+ final Directory cacheDir = await getTemporaryDirectory();
+ final String imagePath = '${cacheDir.path}/profile_image.png';
+ if (File(imagePath).existsSync()) {
+ setState(() {
+ _profileImage = File(imagePath);
+ });
+ }
+ }
+
// ignore: unused_element
Future _saveProfile(UserProfile updatedProfile) async {
final InternetConnectionBloc internetConnectionBloc =
@@ -72,6 +116,12 @@ class ProfileScreenState extends State {
);
}
}
+ if (_profileImage != null) {
+ // Save the profile image to cache
+ final Directory cacheDir = await getTemporaryDirectory();
+ final String imagePath = '${cacheDir.path}/profile_image.png';
+ await _profileImage!.copy(imagePath);
+ }
}
@override
@@ -120,6 +170,19 @@ class ProfileScreenState extends State {
return Column(
children: [
+ GestureDetector(
+ onTap: _pickImage,
+ child: CircleAvatar(
+ radius: 50,
+ backgroundImage: _profileImage != null
+ ? FileImage(_profileImage!)
+ : null,
+ child: _profileImage == null
+ ? const Icon(Icons.camera_alt, size: 50)
+ : null,
+ ),
+ ),
+ const SizedBox(height: 24),
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/support');
@@ -154,8 +217,7 @@ class ProfileScreenState extends State {
Center(
child: ElevatedButton(
onPressed: () async {
- final String? userId =
- FirebaseAuth.instance.currentUser?.uid;
+ final String? userId = FirebaseAuth.instance.currentUser?.uid;
if (userId != null) {
final UserProfile updatedProfile = UserProfile(
userId: userId,
@@ -164,18 +226,16 @@ class ProfileScreenState extends State {
citizenId: _citizenIdController.text,
email: _emailController.text,
phone: _phoneController.text,
- birthDate: DateFormat('MM/dd/yyyy')
- .parse(_birthDateController.text),
- favoriteCuisine:
- _favoriteCuisine ?? CuisineType.other,
+ birthDate: DateFormat('MM/dd/yyyy').parse(_birthDateController.text),
+ favoriteCuisine: _favoriteCuisine ?? CuisineType.other,
dietType: _dietType ?? DietType.none,
);
- final InternetConnectionBloc internetConnectionBloc =
- context.read();
+ final InternetConnectionBloc internetConnectionBloc = context.read();
if (internetConnectionBloc.state is DisconnectedInternet) {
- final SharedPreferences prefs = await SharedPreferences.getInstance();
- await prefs.setString('cachedProfile', updatedProfile.toMap().toString());
+ final Directory cacheDir = await getTemporaryDirectory();
+ final String profilePath = '${cacheDir.path}/profile.json';
+ await File(profilePath).writeAsString(updatedProfile.toMap().toString());
if (mounted) {
// ignore: use_build_context_synchronously
ScaffoldMessenger.of(context).showSnackBar(
@@ -188,11 +248,11 @@ class ProfileScreenState extends State {
}
} else {
context.read().add(
- UpdateProfileEvent(
- userId: userId,
- updatedProfile: updatedProfile,
- ),
- );
+ UpdateProfileEvent(
+ userId: userId,
+ updatedProfile: updatedProfile,
+ ),
+ );
}
}
},
diff --git a/lib/features/support/presentation/bloc/support_bloc.dart b/lib/features/support/presentation/bloc/support_bloc.dart
index 83c54b9..525746d 100644
--- a/lib/features/support/presentation/bloc/support_bloc.dart
+++ b/lib/features/support/presentation/bloc/support_bloc.dart
@@ -30,7 +30,6 @@ class SupportBloc extends Bloc {
) async {
emit(SupportLoading());
try {
- // Intentar subir el ticket a Firestore
await firestore.collection('support_tickets').add({
'category': event.category,
'subOption': event.subOption,
diff --git a/pubspec.lock b/pubspec.lock
index 1a0fc4a..aab6544 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -41,6 +41,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
+ camera:
+ dependency: "direct main"
+ description:
+ name: camera
+ sha256: "26ff41045772153f222ffffecba711a206f670f5834d40ebf5eed3811692f167"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.11.0+2"
+ camera_android_camerax:
+ dependency: transitive
+ description:
+ name: camera_android_camerax
+ sha256: e3627fdc2132d89212b8a8676679f5b07008c7e3d8ae00cea775c3397f9e742b
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.6.10"
+ camera_avfoundation:
+ dependency: transitive
+ description:
+ name: camera_avfoundation
+ sha256: "2e4c568f70e406ccb87376bc06b53d2f5bebaab71e2fbcc1a950e31449381bcf"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.9.17+5"
+ camera_platform_interface:
+ dependency: transitive
+ description:
+ name: camera_platform_interface
+ sha256: b3ede1f171532e0d83111fe0980b46d17f1aa9788a07a2fbed07366bbdbb9061
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.8.0"
+ camera_web:
+ dependency: transitive
+ description:
+ name: camera_web
+ sha256: "595f28c89d1fb62d77c73c633193755b781c6d2e0ebcd8dc25b763b514e6ba8f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.3.5"
characters:
dependency: transitive
description:
@@ -105,6 +145,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.1"
+ cross_file:
+ dependency: transitive
+ description:
+ name: cross_file
+ sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.3.4+2"
crypto:
dependency: transitive
description:
@@ -177,6 +225,38 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.1"
+ file_selector_linux:
+ dependency: transitive
+ description:
+ name: file_selector_linux
+ sha256: "54cbbd957e1156d29548c7d9b9ec0c0ebb6de0a90452198683a7d23aed617a33"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.9.3+2"
+ file_selector_macos:
+ dependency: transitive
+ description:
+ name: file_selector_macos
+ sha256: "271ab9986df0c135d45c3cdb6bd0faa5db6f4976d3e4b437cf7d0f258d941bfc"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.9.4+2"
+ file_selector_platform_interface:
+ dependency: transitive
+ description:
+ name: file_selector_platform_interface
+ sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.6.2"
+ file_selector_windows:
+ dependency: transitive
+ description:
+ name: file_selector_windows
+ sha256: "8f5d2f6590d51ecd9179ba39c64f722edc15226cc93dcc8698466ad36a4a85a4"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.9.3+3"
firebase_analytics:
dependency: "direct main"
description:
@@ -536,6 +616,70 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.2"
+ image_picker:
+ dependency: "direct main"
+ description:
+ name: image_picker
+ sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.2"
+ image_picker_android:
+ dependency: transitive
+ description:
+ name: image_picker_android
+ sha256: fa8141602fde3f7e2f81dbf043613eb44dfa325fa0bcf93c0f142c9f7a2c193e
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.8.12+18"
+ image_picker_for_web:
+ dependency: transitive
+ description:
+ name: image_picker_for_web
+ sha256: "717eb042ab08c40767684327be06a5d8dbb341fe791d514e4b92c7bbe1b7bb83"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.6"
+ image_picker_ios:
+ dependency: transitive
+ description:
+ name: image_picker_ios
+ sha256: "4f0568120c6fcc0aaa04511cb9f9f4d29fc3d0139884b1d06be88dcec7641d6b"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.8.12+1"
+ image_picker_linux:
+ dependency: transitive
+ description:
+ name: image_picker_linux
+ sha256: "4ed1d9bb36f7cd60aa6e6cd479779cc56a4cb4e4de8f49d487b1aaad831300fa"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.2.1+1"
+ image_picker_macos:
+ dependency: transitive
+ description:
+ name: image_picker_macos
+ sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.2.1+1"
+ image_picker_platform_interface:
+ dependency: transitive
+ description:
+ name: image_picker_platform_interface
+ sha256: "9ec26d410ff46f483c5519c29c02ef0e02e13a543f882b152d4bfd2f06802f80"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.10.0"
+ image_picker_windows:
+ dependency: transitive
+ description:
+ name: image_picker_windows
+ sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.2.1+1"
internet_connection_checker:
dependency: "direct main"
description:
@@ -624,6 +768,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.15.0"
+ mime:
+ dependency: transitive
+ description:
+ name: mime
+ sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.0"
nested:
dependency: "direct main"
description:
@@ -649,7 +801,7 @@ packages:
source: hosted
version: "1.9.0"
path_provider:
- dependency: transitive
+ dependency: "direct main"
description:
name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
@@ -696,6 +848,54 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.0"
+ permission_handler:
+ dependency: "direct main"
+ description:
+ name: permission_handler
+ sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb"
+ url: "https://pub.dev"
+ source: hosted
+ version: "11.3.1"
+ permission_handler_android:
+ dependency: transitive
+ description:
+ name: permission_handler_android
+ sha256: "71bbecfee799e65aff7c744761a57e817e73b738fedf62ab7afd5593da21f9f1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "12.0.13"
+ permission_handler_apple:
+ dependency: transitive
+ description:
+ name: permission_handler_apple
+ sha256: e6f6d73b12438ef13e648c4ae56bd106ec60d17e90a59c4545db6781229082a0
+ url: "https://pub.dev"
+ source: hosted
+ version: "9.4.5"
+ permission_handler_html:
+ dependency: transitive
+ description:
+ name: permission_handler_html
+ sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.1.3+5"
+ permission_handler_platform_interface:
+ dependency: transitive
+ description:
+ name: permission_handler_platform_interface
+ sha256: e9c8eadee926c4532d0305dff94b85bf961f16759c3af791486613152af4b4f9
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.2.3"
+ permission_handler_windows:
+ dependency: transitive
+ description:
+ name: permission_handler_windows
+ sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.2.1"
petitparser:
dependency: transitive
description:
@@ -945,10 +1145,10 @@ packages:
dependency: transitive
description:
name: vm_service
- sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc
+ sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
url: "https://pub.dev"
source: hosted
- version: "14.2.4"
+ version: "14.2.5"
web:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index d06e8f4..82ae4fc 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -28,6 +28,7 @@ environment:
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
+ camera: ^0.11.0+2
cloud_firestore: ^5.4.3
connectivity_plus: ^6.1.0
cupertino_icons: ^1.0.8
@@ -49,6 +50,7 @@ dependencies:
google_maps_flutter: ^2.9.0
google_sign_in: ^6.2.1
http: ^1.2.2
+ image_picker: ^1.1.2
internet_connection_checker: ^1.0.0
intl: ^0.19.0
logger: ^2.4.0
@@ -56,6 +58,8 @@ dependencies:
meta: ^1.15.0
nested: ^1.0.0
path: ^1.9.0
+ path_provider: ^2.1.5
+ permission_handler: ^11.3.1
shared_preferences: ^2.3.2
sign_button: ^2.0.6
sqflite: ^2.4.1