From 9e6f76e309711bef33fb21b9882aaa1c35cc8ee8 Mon Sep 17 00:00:00 2001 From: Felipe Date: Fri, 16 Feb 2024 20:43:51 -0500 Subject: [PATCH] drawer --- lib/blocs/my_user_bloc/my_user_bloc.dart | 14 - .../update_user_info_bloc.dart | 14 + .../update_user_info_event.dart | 10 + .../update_user_info_state.dart | 11 + lib/components/general_drawer.dart | 97 +++++++ lib/components/general_drawer_header.dart | 79 ++++++ lib/components/general_drawer_item.dart | 29 ++ lib/main.dart | 1 - lib/screens/home/home_screen.dart | 115 +------- lib/screens/profile/profile_screen.dart | 262 +++++++++--------- .../lib/src/firebase_user_repository.dart | 20 +- .../user_repository/lib/src/user_repo.dart | 2 - 12 files changed, 391 insertions(+), 263 deletions(-) create mode 100644 lib/components/general_drawer.dart create mode 100644 lib/components/general_drawer_header.dart create mode 100644 lib/components/general_drawer_item.dart diff --git a/lib/blocs/my_user_bloc/my_user_bloc.dart b/lib/blocs/my_user_bloc/my_user_bloc.dart index 1471d31..ed711ef 100644 --- a/lib/blocs/my_user_bloc/my_user_bloc.dart +++ b/lib/blocs/my_user_bloc/my_user_bloc.dart @@ -1,6 +1,3 @@ -import 'dart:async'; -import 'dart:developer'; - import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:user_repository/user_repository.dart'; @@ -22,15 +19,4 @@ class MyUserBloc extends Bloc { } }); } - - void _onUserChanged(UserChanged event, Emitter emit) { - log('UserChanged: ${event.user}'); - if (event.user == null) { - log('UserChanged: ${event.user}'); - emit(const MyUserState.failure()); - } else { - log('UserChanged: ${event.user}'); - emit(MyUserState.success(event.user!)); - } - } } diff --git a/lib/blocs/update_user_info_bloc/update_user_info_bloc.dart b/lib/blocs/update_user_info_bloc/update_user_info_bloc.dart index d111137..aad3e15 100644 --- a/lib/blocs/update_user_info_bloc/update_user_info_bloc.dart +++ b/lib/blocs/update_user_info_bloc/update_user_info_bloc.dart @@ -13,6 +13,7 @@ class UpdateUserInfoBloc : _userRepository = userRepository, super(UpdateUserInfoInitial()) { on(_onUploadPicture); + on(_onUpdateUserInfo); } void _onUploadPicture( @@ -26,4 +27,17 @@ class UpdateUserInfoBloc emit(UploadPictureFailure()); } } + + void _onUpdateUserInfo( + UpdateUserInfo event, Emitter emit) async { + emit(UpdateUserInfoLoading()); + try { + await _userRepository.updateUserInfo(event.userId, { + 'name': event.name, + }); + emit(const UpdateUserInfoSuccess()); + } catch (e) { + emit(UpdateUserInfoFailure()); + } + } } diff --git a/lib/blocs/update_user_info_bloc/update_user_info_event.dart b/lib/blocs/update_user_info_bloc/update_user_info_event.dart index a08943f..1ceff1c 100644 --- a/lib/blocs/update_user_info_bloc/update_user_info_event.dart +++ b/lib/blocs/update_user_info_bloc/update_user_info_event.dart @@ -16,3 +16,13 @@ class UploadPicture extends UpdateUserInfoEvent { @override List get props => [file, userId]; } + +class UpdateUserInfo extends UpdateUserInfoEvent { + final String userId; + final String name; + + const UpdateUserInfo(this.userId, this.name); + + @override + List get props => [userId, name]; +} diff --git a/lib/blocs/update_user_info_bloc/update_user_info_state.dart b/lib/blocs/update_user_info_bloc/update_user_info_state.dart index b765d38..128f4a4 100644 --- a/lib/blocs/update_user_info_bloc/update_user_info_state.dart +++ b/lib/blocs/update_user_info_bloc/update_user_info_state.dart @@ -21,3 +21,14 @@ class UploadPictureSuccess extends UpdateUserInfoState { @override List get props => [userImage]; } + +class UpdateUserInfoFailure extends UpdateUserInfoState {} + +class UpdateUserInfoLoading extends UpdateUserInfoState {} + +class UpdateUserInfoSuccess extends UpdateUserInfoState { + const UpdateUserInfoSuccess(); + + @override + List get props => []; +} diff --git a/lib/components/general_drawer.dart b/lib/components/general_drawer.dart new file mode 100644 index 0000000..a56a6ff --- /dev/null +++ b/lib/components/general_drawer.dart @@ -0,0 +1,97 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:prosappco/blocs/my_user_bloc/my_user_bloc.dart'; +import 'package:prosappco/blocs/update_user_info_bloc/update_user_info_bloc.dart'; +import 'package:prosappco/components/general_drawer_header.dart'; +import 'package:prosappco/components/general_drawer_item.dart'; + +class GeneralDrawer extends StatefulWidget { + const GeneralDrawer({super.key}); + + @override + State createState() => _GeneralDrawerState(); +} + +class _GeneralDrawerState extends State { + @override + Widget build(BuildContext context) { + return BlocListener( + listener: (context, state) { + if (state is UploadPictureSuccess) { + setState(() { + context.read().state.user!.picture = state.userImage; + }); + } + }, + child: Drawer( + backgroundColor: Theme.of(context).colorScheme.background, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const GeneralDrawerHeader(), + Divider( + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.1), + thickness: 0.5, + height: 1, + ), + Expanded( + child: SingleChildScrollView( + child: Column( + children: [ + GeneralDrawerItem( + icon: Icons.person_outline, + label: 'Mi Perfil', + onTap: () { + Navigator.pop(context); + }, + ), + ], + ), + ), + ), + Divider( + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.1), + thickness: 0.5, + height: 1, + ), + const SizedBox(height: 15), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: ElevatedButton( + onPressed: () {}, + style: ElevatedButton.styleFrom( + backgroundColor: Theme.of(context).colorScheme.primary, + padding: const EdgeInsets.symmetric(vertical: 15), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + )), + child: const Text( + 'Modo Profesional', + style: TextStyle(color: Colors.white, fontSize: 18), + ), + ), + ), + const SizedBox(height: 15), + // Container( + // height: MediaQuery.of(context).size.height, + // color: Theme.of(context).colorScheme.background, + // child: ListView( + // children: [ + // ListTile( + // onTap: () { + // context.read().add(const SignOutRequired()); + // }, + // title: const Text( + // 'Cerrar Sesión', + // ), + // ) + // ], + // ), + // ) + ], + ), + ), + ); + } +} diff --git a/lib/components/general_drawer_header.dart b/lib/components/general_drawer_header.dart new file mode 100644 index 0000000..b4c7d60 --- /dev/null +++ b/lib/components/general_drawer_header.dart @@ -0,0 +1,79 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:prosappco/blocs/my_user_bloc/my_user_bloc.dart'; +import 'package:prosappco/blocs/update_user_info_bloc/update_user_info_bloc.dart'; +import 'package:prosappco/screens/profile/profile_screen.dart'; + +class GeneralDrawerHeader extends StatefulWidget { + const GeneralDrawerHeader({super.key}); + + @override + State createState() => _GeneralDrawerHeaderState(); +} + +class _GeneralDrawerHeaderState extends State { + @override + Widget build(BuildContext context) { + return BlocListener( + listener: (context, state) { + if (state is UploadPictureSuccess) { + setState(() { + context.read().state.user!.picture = state.userImage; + }); + } + }, + child: ListTile( + onTap: () { + Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return const ProfileScreen(); + }, + ), + ); + }, + title: Text( + context.read().state.user!.name, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + subtitle: Text( + context.read().state.user!.email, + style: const TextStyle(fontSize: 12), + ), + leading: context.read().state.user!.picture == "" + ? Container( + width: 80, + height: 80, + decoration: BoxDecoration( + color: Colors.grey.shade300, + shape: BoxShape.circle, + ), + child: Icon( + CupertinoIcons.person, + color: Colors.grey.shade400, + size: 35, + ), + ) + : Container( + width: 60, + height: 60, + decoration: BoxDecoration( + color: Colors.grey, + shape: BoxShape.circle, + image: DecorationImage( + image: NetworkImage( + context.read().state.user!.picture!, + ), + fit: BoxFit.cover, + ), + ), + ), + trailing: const Icon(Icons.keyboard_arrow_right, color: Colors.black), + contentPadding: + const EdgeInsets.symmetric(vertical: 10, horizontal: 15), + ), + ); + } +} diff --git a/lib/components/general_drawer_item.dart b/lib/components/general_drawer_item.dart new file mode 100644 index 0000000..b4b8ec1 --- /dev/null +++ b/lib/components/general_drawer_item.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +class GeneralDrawerItem extends StatelessWidget { + final String label; + final IconData icon; + final VoidCallback onTap; + + const GeneralDrawerItem({ + super.key, + required this.label, + required this.icon, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return ListTile( + onTap: onTap, + leading: Icon( + icon, + color: Colors.black, + ), + title: Text( + label, + style: const TextStyle(fontSize: 15), + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index ad063b9..39db09c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -174,7 +174,6 @@ import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:prosappco/dependency_user/user_di.dart'; -import 'package:user_repository/user_repository.dart'; import 'app.dart'; import 'simple_bloc_observer.dart'; diff --git a/lib/screens/home/home_screen.dart b/lib/screens/home/home_screen.dart index 59805df..2f63c51 100644 --- a/lib/screens/home/home_screen.dart +++ b/lib/screens/home/home_screen.dart @@ -2,9 +2,8 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:prosappco/blocs/my_user_bloc/my_user_bloc.dart'; -import 'package:prosappco/blocs/sing_in_bloc/sign_in_bloc.dart'; import 'package:prosappco/blocs/update_user_info_bloc/update_user_info_bloc.dart'; -import 'package:prosappco/screens/profile/profile_screen.dart'; +import 'package:prosappco/components/general_drawer.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @@ -24,112 +23,16 @@ class _HomeScreenState extends State { }); } }, - child: Scaffold( + child: SafeArea( + child: Scaffold( backgroundColor: Theme.of(context).colorScheme.background, - drawer: Drawer( - backgroundColor: Theme.of(context).colorScheme.background, - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Material( - color: Theme.of(context).colorScheme.primary, - child: InkWell( - onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => const ProfileScreen(), - ), - ); - }, - child: Container( - padding: EdgeInsets.only( - top: 20 + MediaQuery.of(context).padding.top, - bottom: 20, - ), - child: Column( - children: [ - context.read().state.user!.picture == "" - ? Container( - width: 80, - height: 80, - decoration: BoxDecoration( - color: Colors.grey.shade300, - shape: BoxShape.circle, - ), - child: Icon( - CupertinoIcons.person, - color: Colors.grey.shade400, - size: 35, - ), - ) - : Container( - width: 80, - height: 80, - decoration: BoxDecoration( - color: Colors.grey, - shape: BoxShape.circle, - image: DecorationImage( - image: NetworkImage( - context - .read() - .state - .user! - .picture!, - ), - fit: BoxFit.cover, - ), - ), - ), - const SizedBox(height: 12), - Text( - context.read().state.user!.name, - style: const TextStyle( - fontSize: 24, - color: Colors.white, - ), - ), - Text( - context.read().state.user!.email, - style: const TextStyle( - fontSize: 15, - color: Colors.white, - ), - ) - ], - ), - ), - ), - ), - Container( - height: MediaQuery.of(context).size.height, - color: Theme.of(context).colorScheme.background, - child: ListView( - children: [ - ListTile( - onTap: () { - context - .read() - .add(const SignOutRequired()); - }, - title: const Text( - 'Cerrar Sesión', - ), - ) - ], - ), - ) - ], - ), - ), - ), + drawer: const GeneralDrawer(), appBar: AppBar(), - body: Center( - child: Text( - context.read().state.user?.name ?? 'null', - ), - )), + body: const Center( + child: Text('Bienvenido'), + ), + ), + ), ); } } diff --git a/lib/screens/profile/profile_screen.dart b/lib/screens/profile/profile_screen.dart index da65bab..9fd3113 100644 --- a/lib/screens/profile/profile_screen.dart +++ b/lib/screens/profile/profile_screen.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:image_picker/image_picker.dart'; -import 'package:prosappco/blocs/authentication_bloc/authentication_bloc.dart'; import 'package:prosappco/blocs/my_user_bloc/my_user_bloc.dart'; import 'package:prosappco/blocs/update_user_info_bloc/update_user_info_bloc.dart'; import 'package:prosappco/src/components/pop_appbar.dart'; @@ -15,28 +14,17 @@ class ProfileScreen extends StatefulWidget { } class _ProfileScreenState extends State { + final TextEditingController _nameController = TextEditingController(); + + @override + void dispose() { + _nameController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { - return - - // MultiBlocProvider( - // providers: [ - // BlocProvider( - // create: (context) => MyUserBloc( - // myUserRepository: - // context.read().userRepository) - // ..add(GetMyUser( - // myUserId: context.read().state.user!.uid)), - // ), - // BlocProvider( - // create: (context) => UpdateUserInfoBloc( - // userRepository: context.read().userRepository, - // ), - // ) - // ], - // child: - - BlocListener( + return BlocListener( listener: (context, state) { if (state is UploadPictureSuccess) { setState(() { @@ -45,126 +33,134 @@ class _ProfileScreenState extends State { } }, child: Scaffold( - appBar: PopAppbar( - onPressed: () { - Navigator.pop(context); - }, - label: 'Perfil', - ), - body: BlocBuilder(builder: (context, state) { - if (state.status == MyUserStatus.success) { - return Padding( - padding: const EdgeInsets.all(20.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - GestureDetector( - onTap: () async { - final ImagePicker picker = ImagePicker(); - final XFile? image = await picker.pickImage( - source: ImageSource.gallery, - maxHeight: 500, - maxWidth: 500, - imageQuality: 40, - ); + appBar: PopAppbar( + onPressed: () { + Navigator.pop(context); + }, + label: 'Perfil', + ), + body: BlocBuilder(builder: (context, state) { + if (state.status == MyUserStatus.success) { + _nameController.text = state.user!.name; - if (image != null) { - context.read().add( - UploadPicture(image.path, state.user!.id), - ); - } - }, - child: state.user!.picture == "" - ? Container( - width: 120, - height: 120, - decoration: BoxDecoration( - color: Colors.grey.shade300, - shape: BoxShape.circle, - ), - child: Icon( - CupertinoIcons.person, - color: Colors.grey.shade400, - size: 40, - ), - ) - : Container( - width: 120, - height: 120, - decoration: BoxDecoration( - color: Colors.grey, - shape: BoxShape.circle, - image: DecorationImage( - image: NetworkImage( - state.user!.picture!, - ), - fit: BoxFit.cover, + return Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + GestureDetector( + onTap: () async { + final ImagePicker picker = ImagePicker(); + final XFile? image = await picker.pickImage( + source: ImageSource.gallery, + maxHeight: 500, + maxWidth: 500, + imageQuality: 40, + ); + + if (image != null) { + context.read().add( + UploadPicture(image.path, state.user!.id), + ); + } + }, + child: state.user!.picture == "" + ? Container( + width: 120, + height: 120, + decoration: BoxDecoration( + color: Colors.grey.shade300, + shape: BoxShape.circle, + ), + child: Icon( + CupertinoIcons.person, + color: Colors.grey.shade400, + size: 40, + ), + ) + : Container( + width: 150, + height: 150, + decoration: BoxDecoration( + color: Colors.grey, + shape: BoxShape.circle, + image: DecorationImage( + image: NetworkImage( + context + .read() + .state + .user! + .picture!, ), + fit: BoxFit.contain, ), ), - ), - const SizedBox(height: 20.0), - TextFormField( - initialValue: state.user?.name, - decoration: const InputDecoration( - labelText: 'Nombre', - prefixIcon: Icon(Icons.person), - hintText: 'Nombre (obligatorio)', - border: OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(25.0), - )), - errorBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.red), - ), - focusedErrorBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.red, width: 2.0), - ), + ), + ), + const SizedBox(height: 20.0), + TextFormField( + controller: _nameController, + decoration: const InputDecoration( + labelText: 'Nombre', + prefixIcon: Icon(Icons.person), + hintText: 'Nombre (obligatorio)', + border: OutlineInputBorder( + borderRadius: BorderRadius.all( + Radius.circular(25.0), + )), + errorBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.red), ), - validator: (value) { - if (value == null || value.isEmpty) { - return 'Por favor, ingrese su nombre'; - } - return null; - }, - ), - const SizedBox(height: 20.0), - TextFormField( - initialValue: state.user?.email, - decoration: const InputDecoration( - labelText: 'Nombre', - prefixIcon: Icon(Icons.person), - hintText: 'Nombre (obligatorio)', - border: OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(25.0), - )), - errorBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.red), - ), - focusedErrorBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.red, width: 2.0), - ), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.red, width: 2.0), ), - validator: (value) { - if (value == null || value.isEmpty) { - return 'Por favor, ingrese su nombre'; - } - return null; - }, ), - ElevatedButton( - onPressed: () {}, - child: const Text('Guardar'), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Por favor, ingrese su nombre'; + } + return null; + }, + ), + const SizedBox(height: 20.0), + TextFormField( + decoration: const InputDecoration( + labelText: 'Nombre', + prefixIcon: Icon(Icons.person), + hintText: 'Nombre (obligatorio)', + border: OutlineInputBorder( + borderRadius: BorderRadius.all( + Radius.circular(25.0), + )), + errorBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.red), + ), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.red, width: 2.0), + ), ), - ], - ), - ); - } else { - return const Center(child: CircularProgressIndicator()); - } - })), - // ), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Por favor, ingrese su nombre'; + } + return null; + }, + ), + ElevatedButton( + onPressed: () { + context.read().add( + UpdateUserInfo(state.user!.id, _nameController.text)); + }, + child: const Text('Guardar'), + ), + ], + ), + ); + } else { + return const Center(child: CircularProgressIndicator()); + } + }), + ), ); } } diff --git a/packages/user_repository/lib/src/firebase_user_repository.dart b/packages/user_repository/lib/src/firebase_user_repository.dart index 6004162..16651ac 100644 --- a/packages/user_repository/lib/src/firebase_user_repository.dart +++ b/packages/user_repository/lib/src/firebase_user_repository.dart @@ -18,13 +18,7 @@ class FirebaseUserRepository implements UserRepository { FirebaseUserRepository(this._firebaseAuth) { _firebaseAuth.userChanges().listen((user) async { if (user != null) { - try { - final myUser = await getMyUser(user.uid); - _userStreamController.add(myUser); - } catch (e) { - log('Error en FirebaseUserRepository ${e.toString()}'); - _userStreamController.add(null); - } + await updateFromFirebase(user.uid); } else { _userStreamController.add(null); } @@ -33,6 +27,16 @@ class FirebaseUserRepository implements UserRepository { // Stream get user => _userStreamController.stream; + Future updateFromFirebase(String userId) async { + try { + final myUser = await getMyUser(userId); + _userStreamController.add(myUser); + } catch (e) { + log('Error en FirebaseUserRepository ${e.toString()}'); + _userStreamController.add(null); + } + } + @override Stream streamUser() { return _userStreamController.stream; @@ -129,6 +133,8 @@ class FirebaseUserRepository implements UserRepository { Future updateUserInfo(String userId, Map data) async { try { await usersCollection.doc(userId).update(data); + + await updateFromFirebase(userId); } catch (e) { log(e.toString()); rethrow; diff --git a/packages/user_repository/lib/src/user_repo.dart b/packages/user_repository/lib/src/user_repo.dart index f505888..145ac68 100644 --- a/packages/user_repository/lib/src/user_repo.dart +++ b/packages/user_repository/lib/src/user_repo.dart @@ -1,5 +1,3 @@ -import 'package:firebase_auth/firebase_auth.dart'; - import '../user_repository.dart'; abstract class UserRepository {