Skip to content

Instantly share code, notes, and snippets.

@denisviana
Created April 19, 2019 23:23
Show Gist options
  • Save denisviana/7aee8049e0214e60e268b9060b01db30 to your computer and use it in GitHub Desktop.
Save denisviana/7aee8049e0214e60e268b9060b01db30 to your computer and use it in GitHub Desktop.
Flutter + Firebase + BLoC (Recover Password)
import 'dart:async';
import 'dart:io';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:hotaru_car/src/models/car_ad.dart';
import 'package:hotaru_car/src/models/collections_mapper.dart';
import 'package:hotaru_car/src/models/response/user_response.dart';
import 'package:hotaru_car/src/models/user.dart';
import 'package:uuid/uuid.dart';
class FirebaseProvider {
//region User
Firestore _firestore = Firestore.instance;
FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
FirebaseStorage _firebaseStorage = FirebaseStorage.instance;
Future<UserResponse> authUser(User user) async {
return await _firebaseAuth
.signInWithEmailAndPassword(email: user.email, password: user.password)
.then((result) async {
User user = await getUserByUid(result.uid);
return user != null
? UserResponse(hasError: false, user: user)
: UserResponse(hasError: true);
}).catchError((error) {
return UserResponse(hasError: true, msg: error.message);
});
}
Future<User> createUser(User user) async {
return await _firebaseAuth
.createUserWithEmailAndPassword(
email: user.email, password: user.password)
.then((result) async {
String imageProfileUrl = user.imageFile != null ? await _uploadProfileImage(user.imageFile, result.uid) : null;
if(imageProfileUrl!=null && imageProfileUrl.isNotEmpty){
UserUpdateInfo userUpdateInfo = UserUpdateInfo();
userUpdateInfo.displayName = user.name;
userUpdateInfo.photoUrl = imageProfileUrl;
result.updateProfile(userUpdateInfo);
user.imageProfileUrl = imageProfileUrl;
}
await registerUser(user, result.uid);
user.uid = result.uid;
return user;
});
}
Future<User> getUserByUid(String uid) async {
return await _firestore
.collection(UserFields.COLLECTION)
.document(uid)
.get()
.then((result) {
var user = User.fromMap(result.data);
if(user!=null)
user.uid = uid;
return user;
}).catchError((error) {
return null;
});
}
Future<User> getUserByEmail(String email) async {
return await _firestore
.collection(UserFields.COLLECTION)
.where(UserFields.EMAIL, isEqualTo: email)
.getDocuments()
.then((result) {
return result.documents.isNotEmpty
? User.fromMap(result.documents.first.data)
: null;
});
}
Future<void> registerUser(User user, String uid) => _firestore
.collection(UserFields.COLLECTION)
.document(uid)
.setData(user.toMap());
Future<UserResponse> recoveryPassword(String email) => _firebaseAuth.sendPasswordResetEmail(email: email);
Future<Null> signOut() async => await _firebaseAuth.signOut();
Future<UserResponse> changePassword(String email, String oldPassword, String newPassword) async{
AuthCredential credential = EmailAuthProvider.getCredential(email: email, password: oldPassword);
FirebaseUser currentUser = await _firebaseAuth.currentUser();
return await currentUser.reauthenticateWithCredential(credential)
.then((_) async{
await currentUser.updatePassword(newPassword);
return UserResponse(hasError: false);
})
.catchError((error){
return UserResponse(hasError: true);
});
}
Future<UserResponse> updateUser(User user) async{
return await _firebaseAuth.currentUser()
.then((result) async{
await result.updateEmail(user.email);
if(user.imageFile!=null){
String imageProfileUrl = await _uploadProfileImage(user.imageFile, result.uid);
imageProfileUrl!=null ? user.imageProfileUrl = imageProfileUrl : user.imageProfileUrl = "";
}
await _firestore.collection(UserFields.COLLECTION)
.document(result.uid)
.updateData(user.toMap());
return UserResponse(user: user, hasError: false);
}).catchError((error){
return UserResponse(hasError: true);
});
}
Future<String> _uploadProfileImage(File file, String uid) async {
var imageName = Uuid().v1();
var ref = _firebaseStorage.ref().child("user_images/$uid/$imageName.jpg");
StorageUploadTask task = ref.putFile(file);
StorageTaskSnapshot storageTaskSnapshot = await task.onComplete;
String url = await storageTaskSnapshot.ref.getDownloadURL();
return url;
}
//endregion
}
import 'dart:async';
import 'package:bloc_provider/bloc_provider.dart';
import 'package:hotaru_car/src/common/constants.dart';
import 'package:hotaru_car/src/models/response/response.dart';
import 'package:hotaru_car/src/repository/recovery_password_repository.dart';
import 'package:rxdart/rxdart.dart';
class RecoveryPasswordBloc extends Bloc{
final repository = RecoveryPasswordRepository();
final _state = StreamController<AppResponse>.broadcast();
final _email = BehaviorSubject<String>();
static final _validateNameField = StreamController<bool>.broadcast();
static final _validateEmailField = StreamController<bool>.broadcast();
Function(String) get changeEmail => _email.sink.add;
Stream get isValidName => _validateNameField.stream;
Stream get isValidEmail => _validateEmailField.stream;
Stream get getState => _state.stream;
Observable<String> get email => _email.stream.transform(_validateEmailString);
Future<bool> recoveryPassword() async {
_state.add(AppResponse(isLoading: true, hasError: false));
return await repository.recoveryPassword(_email.value)
.then((_){
_state.add(AppResponse(isLoading: false, hasError: false));
return true;
}).catchError((error){
_state.add(AppResponse(isLoading: false, hasError: true, msg: error.message));
return false;
});
}
final _validateEmailString =
StreamTransformer<String, String>.fromHandlers(handleData: (email, sink) {
Pattern pattern =
r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regex = new RegExp(pattern);
if (email.isEmpty) {
sink.addError(AppState.EMAIL_IS_EMPTY);
_validateEmailField.sink.add(false);
} else if (!regex.hasMatch(email)) {
sink.addError(AppState.EMAIL_INVALID);
_validateEmailField.sink.add(false);
} else {
sink.add(email);
_validateEmailField.sink.add(true);
}
});
final _validateNameString =
StreamTransformer<String, String>.fromHandlers(
handleData: (name, sink) {
if (name.isEmpty) {
sink.addError(AppState.FIELD_IS_EMPTY);
} else {
sink.add(name);
}
});
@override
void dispose() {
_email.value = "";
_email.drain();
_email.close();
_state.close();
_validateEmailField.close();
_validateNameField.close();
}
}
import 'package:hotaru_car/src/models/response/user_response.dart';
import 'package:hotaru_car/src/repository/base.dart';
class RecoveryPasswordRepository extends BaseRepository{
Future<UserResponse> recoveryPassword(String email) => firebaseProvider.recoveryPassword(email);
}
import 'package:flutter/material.dart';
import 'package:hotaru_car/src/bloc/recoverypassword/recovery_password_bloc.dart';
import 'package:hotaru_car/src/bloc/recoverypassword/recovery_password_bloc_provider.dart';
import 'package:hotaru_car/src/common/constants.dart';
import 'package:hotaru_car/src/common/localization.dart';
import 'package:hotaru_car/src/common/localization_strings.dart';
import 'package:hotaru_car/src/models/response/response.dart';
import 'package:hotaru_car/src/widget/alerts.dart';
import 'package:hotaru_car/src/widget/margin.dart';
class RecoverPasswordScreen extends StatefulWidget {
@override
_RecoverPasswordScreenState createState() => _RecoverPasswordScreenState();
}
class _RecoverPasswordScreenState extends State<RecoverPasswordScreen> {
final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldstate =
new GlobalKey<ScaffoldState>();
RecoveryPasswordBloc _bloc;
@override
void didChangeDependencies() {
super.didChangeDependencies();
_bloc = RecoveryPasswordBlocProvider.of(context);
}
@override
void dispose() {
_bloc.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldstate,
appBar: AppBar(
brightness: Brightness.light,
backgroundColor: Colors.transparent,
elevation: 0.0,
iconTheme: IconThemeData(color: Theme.of(context).accentColor),
),
backgroundColor: Colors.white,
body: Container(
padding: EdgeInsets.fromLTRB(16, 10, 16, 10),
child: Form(
key: _formKey,
child: StreamBuilder<AppResponse>(
stream: _bloc.getState,
initialData: AppResponse(isLoading: false, hasError: false),
builder: (context, snapshot){
if(snapshot.data.isLoading)
return _showLoading();
if(snapshot.data.hasError)
_showSnackBar(snapshot.data.msg);
return ListView(children: <Widget>[
Text(Localization.of(context).getString(RecoverPassword),
style: TextStyle(color: Colors.black, fontSize: 30.0)),
MarginVertically(30.0),
Container(
height: 260.0,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Positioned(
top: 0.0,
left: 0.0,
right: 0.0,
bottom: 25.0,
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0)),
elevation: 4.0,
child: Container(
color: Colors.white,
padding: EdgeInsets.fromLTRB(16, 20, 16, 20),
child: Column(
children: <Widget>[
Text(
Localization.of(context)
.getString(RecoverPasswordText),
style: TextStyle(
color: Colors.black45, fontSize: 17.0)),
MarginVertically(10.0),
StreamBuilder(
stream: _bloc.email,
builder: (context, snapshot) {
return TextField(
autofocus: true,
textInputAction: TextInputAction.done,
keyboardType: TextInputType.emailAddress,
style: TextStyle(color: Colors.black),
onChanged: _bloc.changeEmail,
decoration: InputDecoration(
labelText:
Localization.of(context).getString(Email),
hintText: Localization.of(context)
.getString(EnterYourEmail),
errorText: _getErrorMsg(
snapshot.error as AppState)
),
);
},
),
],
),
),
),
),
Positioned(
bottom: 0.0,
right: 20.0,
child: FloatingActionButton(
backgroundColor: Theme.of(context).accentColor,
onPressed: () {
_submit();
},
child: Icon(
Icons.arrow_forward,
color: Colors.white,
)),
)
],
),
),
MarginVertically(30.0),
]
);
}
),
)
)
);
}
void _submit() async{
if(await _bloc.recoveryPassword()){
_onSuccess();
}
}
Widget _showLoading() {
return Center(
child: SizedBox(
height: 40.0,
width: 40.0,
child: CircularProgressIndicator(),
),
);
}
String _getErrorMsg(AppState errorEnum) {
switch (errorEnum) {
case AppState.FIELD_IS_EMPTY :
return Localization.of(context).getString(FieldIsEmpty);
break;
case AppState.EMAIL_IS_EMPTY :
return Localization.of(context).getString(FieldIsEmpty);
break;
case AppState.EMAIL_INVALID :
return Localization.of(context).getString(EmailInvalid);
break;
case AppState.PASSWORD_IS_EMPTY :
return Localization.of(context).getString(PasswordIsEmpty);
break;
case AppState.PASSWORD_INVALID :
return Localization.of(context).getString(PasswordInvalid);
break;
case AppState.GENERIC_ERROR :
return Localization.of(context).getString(GenericError);
break;
default:
return "";
}
}
void _onSuccess() {
showAlert(
context,
Localization.of(context).getString(RecoverPasswordPopupTitle),
Localization.of(context).getString(RecoverPasswordPopupMsg),
callback: () {});
}
void _showSnackBar(String msg) {
_scaffoldstate.currentState.showSnackBar(SnackBar(content: Text(msg)));
}
}
@Lokeswara-betha
Copy link

superb code mate

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment