Browse Source

screen forgot password

master
daivph 5 years ago
parent
commit
b116872f09
14 changed files with 561 additions and 10 deletions
  1. +17
    -0
      lib/data/api/rest_client.dart
  2. +91
    -0
      lib/data/api/rest_client.g.dart
  3. +28
    -0
      lib/data/repository/user_repository.dart
  4. +29
    -0
      lib/models/account.dart
  5. +48
    -0
      lib/models/account.g.dart
  6. +15
    -0
      lib/models/password.dart
  7. +20
    -0
      lib/models/password.g.dart
  8. +27
    -0
      lib/presentation/custom_widgets/widget_toast.dart
  9. +117
    -0
      lib/presentation/screens/forgot_password/sc_forgot_password.dart
  10. +25
    -10
      lib/presentation/screens/login/view/login_form.dart
  11. +52
    -0
      lib/utils/formatter.dart
  12. +76
    -0
      lib/utils/validators.dart
  13. +14
    -0
      pubspec.lock
  14. +2
    -0
      pubspec.yaml

+ 17
- 0
lib/data/api/rest_client.dart View File

import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:farm_tpf/models/account.dart';
import 'package:farm_tpf/models/password.dart';
import 'package:farm_tpf/models/user.dart'; import 'package:farm_tpf/models/user.dart';
import 'package:farm_tpf/models/user_request.dart'; import 'package:farm_tpf/models/user_request.dart';
import 'package:retrofit/retrofit.dart'; import 'package:retrofit/retrofit.dart';


@POST("/api/authenticate") @POST("/api/authenticate")
Future<User> login(@Body() UserRequest userRequest); Future<User> login(@Body() UserRequest userRequest);

@GET("/api/account")
Future<Account> getMe();

@POST("/api/account/reset-password/init")
Future<void> forgotPassword(@Body() String email);

@POST("/api/account/reset-password/finish")
Future<void> resetPassword(@Body() Password password);

@POST("/api/account/change-password")
Future<void> changePassword(@Body() Password password);

@PUT("/api/update-my-profile")
Future<Account> updateProfile(@Body() Account account);
} }

+ 91
- 0
lib/data/api/rest_client.g.dart View File

final value = User.fromJson(_result.data); final value = User.fromJson(_result.data);
return value; return value;
} }

@override
getMe() async {
const _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _data = <String, dynamic>{};
final Response<Map<String, dynamic>> _result = await _dio.request(
'/api/account',
queryParameters: queryParameters,
options: RequestOptions(
method: 'GET',
headers: <String, dynamic>{},
extra: _extra,
baseUrl: baseUrl),
data: _data);
final value = Account.fromJson(_result.data);
return value;
}

@override
forgotPassword(email) async {
ArgumentError.checkNotNull(email, 'email');
const _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _data = email;
await _dio.request<void>('/api/account/reset-password/init',
queryParameters: queryParameters,
options: RequestOptions(
method: 'POST',
headers: <String, dynamic>{},
extra: _extra,
baseUrl: baseUrl),
data: _data);
return null;
}

@override
resetPassword(password) async {
ArgumentError.checkNotNull(password, 'password');
const _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _data = <String, dynamic>{};
_data.addAll(password?.toJson() ?? <String, dynamic>{});
await _dio.request<void>('/api/account/reset-password/finish',
queryParameters: queryParameters,
options: RequestOptions(
method: 'POST',
headers: <String, dynamic>{},
extra: _extra,
baseUrl: baseUrl),
data: _data);
return null;
}

@override
changePassword(password) async {
ArgumentError.checkNotNull(password, 'password');
const _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _data = <String, dynamic>{};
_data.addAll(password?.toJson() ?? <String, dynamic>{});
await _dio.request<void>('/api/account/change-password',
queryParameters: queryParameters,
options: RequestOptions(
method: 'POST',
headers: <String, dynamic>{},
extra: _extra,
baseUrl: baseUrl),
data: _data);
return null;
}

@override
updateProfile(account) async {
ArgumentError.checkNotNull(account, 'account');
const _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _data = <String, dynamic>{};
_data.addAll(account?.toJson() ?? <String, dynamic>{});
final Response<Map<String, dynamic>> _result = await _dio.request(
'/api/update-my-profile',
queryParameters: queryParameters,
options: RequestOptions(
method: 'PUT',
headers: <String, dynamic>{},
extra: _extra,
baseUrl: baseUrl),
data: _data);
final value = Account.fromJson(_result.data);
return value;
}
} }

+ 28
- 0
lib/data/repository/user_repository.dart View File

import 'package:farm_tpf/data/api/dio_provider.dart';
import 'package:farm_tpf/data/api/rest_client.dart';
import 'package:farm_tpf/models/account.dart';
import 'package:farm_tpf/models/password.dart';

class UserRepository {
final dio = DioProvider.instance();

Future<Account> getUser() {
final client = RestClient(dio);
return client.getMe();
}

Future<void> forgotPassword(String email) {
final client = RestClient(dio);
return client.forgotPassword(email);
}

Future<void> changePassword(Password password) {
final client = RestClient(dio);
return client.changePassword(password);
}

Future<Account> updateProfile(Account account) {
final client = RestClient(dio);
return client.updateProfile(account);
}
}

+ 29
- 0
lib/models/account.dart View File

import 'package:json_annotation/json_annotation.dart';

part 'account.g.dart';

@JsonSerializable()
class Account {
Account();

num id;
String login;
String firstName;
String lastName;
String midleName;
String fullName;
String telephone;
String address;
String avartar;
num vaiTroId;
String tenVaiTro;
num donViCanhTacId;
String tenDonVi;
String email;
String imageUrl;
bool activated;
List authorities;
factory Account.fromJson(Map<String,dynamic> json) => _$AccountFromJson(json);
Map<String, dynamic> toJson() => _$AccountToJson(this);
}

+ 48
- 0
lib/models/account.g.dart View File

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'account.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

Account _$AccountFromJson(Map<String, dynamic> json) {
return Account()
..id = json['id'] as num
..login = json['login'] as String
..firstName = json['firstName'] as String
..lastName = json['lastName'] as String
..midleName = json['midleName'] as String
..fullName = json['fullName'] as String
..telephone = json['telephone'] as String
..address = json['address'] as String
..avartar = json['avartar'] as String
..vaiTroId = json['vaiTroId'] as num
..tenVaiTro = json['tenVaiTro'] as String
..donViCanhTacId = json['donViCanhTacId'] as num
..tenDonVi = json['tenDonVi'] as String
..email = json['email'] as String
..imageUrl = json['imageUrl'] as String
..activated = json['activated'] as bool
..authorities = json['authorities'] as List;
}

Map<String, dynamic> _$AccountToJson(Account instance) => <String, dynamic>{
'id': instance.id,
'login': instance.login,
'firstName': instance.firstName,
'lastName': instance.lastName,
'midleName': instance.midleName,
'fullName': instance.fullName,
'telephone': instance.telephone,
'address': instance.address,
'avartar': instance.avartar,
'vaiTroId': instance.vaiTroId,
'tenVaiTro': instance.tenVaiTro,
'donViCanhTacId': instance.donViCanhTacId,
'tenDonVi': instance.tenDonVi,
'email': instance.email,
'imageUrl': instance.imageUrl,
'activated': instance.activated,
'authorities': instance.authorities,
};

+ 15
- 0
lib/models/password.dart View File

import 'package:json_annotation/json_annotation.dart';

part 'password.g.dart';

@JsonSerializable()
class Password {
Password();

String key;
String currentPassword;
String newPassword;
factory Password.fromJson(Map<String,dynamic> json) => _$PasswordFromJson(json);
Map<String, dynamic> toJson() => _$PasswordToJson(this);
}

+ 20
- 0
lib/models/password.g.dart View File

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'password.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

Password _$PasswordFromJson(Map<String, dynamic> json) {
return Password()
..key = json['key'] as String
..currentPassword = json['currentPassword'] as String
..newPassword = json['newPassword'] as String;
}

Map<String, dynamic> _$PasswordToJson(Password instance) => <String, dynamic>{
'key': instance.key,
'currentPassword': instance.currentPassword,
'newPassword': instance.newPassword,
};

+ 27
- 0
lib/presentation/custom_widgets/widget_toast.dart View File

import 'package:flutter/material.dart';

class WidgetToast extends StatelessWidget {
String message;
WidgetToast({@required this.message});

@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25.0),
color: Colors.greenAccent,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.check),
SizedBox(
width: 12.0,
),
Text(message),
],
),
);
}
}

+ 117
- 0
lib/presentation/screens/forgot_password/sc_forgot_password.dart View File

import 'package:farm_tpf/data/repository/user_repository.dart';
import 'package:farm_tpf/presentation/custom_widgets/widget_loading.dart';
import 'package:farm_tpf/presentation/custom_widgets/widget_toast.dart';
import 'package:farm_tpf/utils/const_color.dart';
import 'package:farm_tpf/utils/validators.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:keyboard_dismisser/keyboard_dismisser.dart';

class ForgotPasswordScreen extends StatefulWidget {
@override
_ForgotPasswordScreenState createState() => _ForgotPasswordScreenState();
}

class _ForgotPasswordScreenState extends State<ForgotPasswordScreen> {
UserRepository _userRepository = UserRepository();
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
GlobalKey<FormState> _formKey = GlobalKey();
bool _autoValidate = false;
TextEditingController _emailController = TextEditingController();
String _email = "";
FlutterToast flutterToast;

@override
void initState() {
super.initState();
flutterToast = FlutterToast(context);
}

_validateInputs() async {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
LoadingDialog.showLoadingDialog(context);
_userRepository.forgotPassword(_email).then((value) {
LoadingDialog.hideLoadingDialog(context);
flutterToast.showToast(
child: WidgetToast(message: "Gửi email thành công."));
Navigator.pop(context);
}).catchError((error) {
_scaffoldKey.currentState.showSnackBar(SnackBar(
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Email không tồn tại.'),
Icon(Icons.error),
],
),
backgroundColor: Colors.red,
duration: Duration(seconds: 3),
));
LoadingDialog.hideLoadingDialog(context);
});
} else {
_autoValidate = true;
}
}

Widget _emailField() {
return TextFormField(
keyboardType: TextInputType.text,
decoration: InputDecoration(labelText: "Email"),
controller: _emailController,
validator: (String value) {
return Validators.validateEmail(value);
},
onSaved: (newValue) {
_email = newValue;
},
);
}

Widget _btnSubmit() {
return SizedBox(
width: double.infinity,
height: 55,
child: FlatButton(
onPressed: () {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
_validateInputs();
},
color: COLOR_CONST.DEFAULT,
shape: RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(7.0),
),
child: Text(
'Gửi'.toUpperCase(),
),
),
);
}

@override
Widget build(BuildContext context) => KeyboardDismisser(
child: Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text("Gửi email khôi phục mật khẩu"),
),
body: Form(
key: _formKey,
autovalidate: _autoValidate,
child: SingleChildScrollView(
padding: EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
_emailField(),
SizedBox(
height: 16.0,
),
_btnSubmit()
],
),
))));
}

+ 25
- 10
lib/presentation/screens/login/view/login_form.dart View File

import 'package:farm_tpf/authentication/authentication.dart'; import 'package:farm_tpf/authentication/authentication.dart';
import 'package:farm_tpf/data/repository/authentication_repository.dart'; import 'package:farm_tpf/data/repository/authentication_repository.dart';
import 'package:farm_tpf/presentation/custom_widgets/widget_loading.dart'; import 'package:farm_tpf/presentation/custom_widgets/widget_loading.dart';
import 'package:farm_tpf/presentation/screens/forgot_password/sc_forgot_password.dart';
import 'package:farm_tpf/presentation/screens/login/bloc/login_bloc.dart'; import 'package:farm_tpf/presentation/screens/login/bloc/login_bloc.dart';
import 'package:farm_tpf/utils/const_color.dart'; import 'package:farm_tpf/utils/const_color.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
Scaffold.of(context) Scaffold.of(context)
..hideCurrentSnackBar() ..hideCurrentSnackBar()
..showSnackBar( ..showSnackBar(
const SnackBar(content: Text('Authentication Failure')),
SnackBar(
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Tài khoản hoặc mật khẩu không đúng.'),
Icon(Icons.error),
],
),
backgroundColor: Colors.red),
); );
} }
if (state.status.isSubmissionSuccess) { if (state.status.isSubmissionSuccess) {
const Padding(padding: EdgeInsets.all(16)), const Padding(padding: EdgeInsets.all(16)),
_LoginButton(), _LoginButton(),
const Padding(padding: EdgeInsets.all(6)), const Padding(padding: EdgeInsets.all(6)),
_forgotPasswordButton(),
_FogotPasswordButton(),
_registerButton() _registerButton()
], ],
), ),
} }
} }


Widget _forgotPasswordButton() {
return Align(
alignment: Alignment.centerRight,
child: FlatButton(
child: Text(
'Quên mật khẩu ?',
),
onPressed: () {}));
class _FogotPasswordButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.centerRight,
child: FlatButton(
child: Text(
'Quên mật khẩu ?',
),
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => ForgotPasswordScreen()));
}));
}
} }


Widget _registerButton() { Widget _registerButton() {

+ 52
- 0
lib/utils/formatter.dart View File

import 'dart:developer';

import 'package:intl/intl.dart';

extension HHmm on Duration {
String formatHHmm() {
//1:34:00.000000
final str = this.toString();

final texts = str.split(":");
final textHour = texts[0].padLeft(2, '0');
final textMinute = texts[1].padLeft(2, '0');

return "${textHour}h ${textMinute}m";
}
}

extension FormatNumber on int {
String formatDecimalThousand() {
//1403 -> 1,403
var f = new NumberFormat.decimalPattern("en_US");
return f.format(this);
}
}

extension FormatDate on int {
String MMM_dd_yyyy() {
return DateFormat("MMM dd, yyyy")
.format(DateTime.fromMillisecondsSinceEpoch(this * 1000));
}
}

extension DoubleParsing on String {
double parseDoubleThousand() {
var newValue = this.replaceAll(",", "");
//TODO: CHECK again
if (newValue.endsWith(".0")) {
newValue = newValue.substring(0, newValue.length - 2);
}
return double.tryParse(newValue);
}
}

extension IntParsing on String {
int parseIntThousand() {
var newValue = this.replaceAll(",", "");
if (newValue.endsWith(".0")) {
newValue = newValue.substring(0, newValue.length - 2);
}
return int.tryParse(newValue);
}
}

+ 76
- 0
lib/utils/validators.dart View File

import 'formatter.dart';

class Validators {
static final RegExp _emailRegExp = RegExp(
r'^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$',
);
static final RegExp _passwordRegExp = RegExp(
r'^.{4,8}$',
);

static isValidEmail(String email) {
return _emailRegExp.hasMatch(email);
}

static isValidPassword(String password) {
return _passwordRegExp.hasMatch(password);
}

static isValidName(String name) {
return name.isNotEmpty;
}

static String validateNotNullOrEmpty(String value, String errorMessage) {
if (value.length == 0) {
return errorMessage;
} else {
return null;
}
}

static String validNumber(String value, String errorMessage) {
try {
var doubleValue = value.parseDoubleThousand();
if (doubleValue > 0) {
return null;
} else {
return errorMessage;
}
} catch (_) {
return errorMessage;
}
}

static String validateEmail(String value) {
String 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 regExp = new RegExp(pattern);
if (value.length == 0) {
return "Nhập email";
} else if (!regExp.hasMatch(value)) {
return "Email không đúng";
} else {
return null;
}
}

String validateNewPassword(String value) {
if (value.length == 0) {
return "Nhập mật khẩu mới";
} else {
return null;
}
}

String validateConfirmPassword(String newPassword, String value) {
if (value.length == 0) {
return "Nhập lại mật khẩu mới";
} else if (newPassword != value) {
return "Mật khẩu không trùng khớp";
} else {
return null;
}
}
}

final validators = Validators();

+ 14
- 0
pubspec.lock View File

description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
fluttertoast:
dependency: "direct main"
description:
name: fluttertoast
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.1"
formz: formz:
dependency: "direct main" dependency: "direct main"
description: description:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.2" version: "1.0.2"
pattern_formatter:
dependency: "direct main"
description:
name: pattern_formatter
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
pedantic: pedantic:
dependency: transitive dependency: transitive
description: description:

+ 2
- 0
pubspec.yaml View File

dio: 3.0.9 dio: 3.0.9
formz: ^0.3.0 formz: ^0.3.0
keyboard_dismisser: ^1.0.2 keyboard_dismisser: ^1.0.2
fluttertoast: ^6.0.1
pattern_formatter: ^1.0.2


dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

Loading…
Cancel
Save