import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:provider/provider.dart'; import 'package:fcs/model/language_model.dart'; import 'package:fcs/model/main_model.dart'; import 'package:fcs/model/user_model.dart'; import 'package:fcs/pages/reset_password.dart'; import 'package:fcs/widget/bubble_indication_painter.dart'; import 'package:fcs/widget/localization/app_translations.dart'; import 'package:fcs/widget/progress.dart'; import '../theme/theme.dart' as Theme; import 'forget_password.dart'; import 'sms_page.dart'; import 'util.dart'; class LoginPage extends StatefulWidget { LoginPage({Key key}) : super(key: key); @override _LoginPageState createState() => new _LoginPageState(); } class _LoginPageState extends State with SingleTickerProviderStateMixin { final GlobalKey _scaffoldKey = new GlobalKey(); final FocusNode myFocusNodeEmailLogin = FocusNode(); final FocusNode myFocusNodePasswordLogin = FocusNode(); final FocusNode myFocusNodePassword = FocusNode(); final FocusNode myFocusNodeEmail = FocusNode(); final FocusNode myFocusNodeName = FocusNode(); TextEditingController loginPhoneController = new TextEditingController(); TextEditingController loginPasswordController = new TextEditingController(); bool _obscureTextLogin = true; bool _obscureTextSignup = true; bool _obscureTextSignupConfirm = true; TextEditingController signupPhoneNumberController = new TextEditingController(); TextEditingController signupNameController = new TextEditingController(); TextEditingController signupPasswordController = new TextEditingController(); TextEditingController signupConfirmPasswordController = new TextEditingController(); PageController _pageController; Color left = Colors.black; Color right = Colors.white; final loginFormKey = GlobalKey(); final signupFormKey = GlobalKey(); bool _isLoading = false; @override Widget build(BuildContext context) { return LocalProgress( inAsyncCall: _isLoading, child: Scaffold( key: _scaffoldKey, appBar: AppBar( backgroundColor: Colors.white, iconTheme: IconThemeData( color: Colors.grey, ), elevation: 0, centerTitle: true, title: new Image( height: 30, fit: BoxFit.scaleDown, image: new AssetImage('assets/img/logo.png')), ), body: SingleChildScrollView( child: Container( width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height >= 775.0 ? MediaQuery.of(context).size.height : 580.0, child: Column( mainAxisSize: MainAxisSize.max, children: [ Padding( padding: EdgeInsets.only(top: 50.0), child: _buildMenuBar(context), ), Expanded( flex: 2, child: PageView( controller: _pageController, onPageChanged: (i) { if (i == 0) { setState(() { right = Colors.white; left = Colors.black; }); } else if (i == 1) { setState(() { right = Colors.black; left = Colors.white; }); } }, children: [ new ConstrainedBox( constraints: const BoxConstraints.expand(), child: _buildLogin(context), ), new ConstrainedBox( constraints: const BoxConstraints.expand(), child: _buildSignUp(context), ), ], ), ), ], ), ), ), ), ); } @override void dispose() { myFocusNodePassword.dispose(); myFocusNodeEmail.dispose(); myFocusNodeName.dispose(); _pageController?.dispose(); super.dispose(); } @override void initState() { super.initState(); // SystemChrome.setPreferredOrientations([ // DeviceOrientation.portraitUp, // DeviceOrientation.portraitDown, // ]); _pageController = PageController(); loginPhoneController.text = "09"; signupPhoneNumberController.text = "09"; } Widget _buildMenuBar(BuildContext context) { return Container( width: 300.0, height: 40.0, decoration: BoxDecoration( color: Color(0x552B2B2B), borderRadius: BorderRadius.all(Radius.circular(25.0)), ), child: CustomPaint( painter: TabIndicationPainter(pageController: _pageController), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Expanded( child: FlatButton( onPressed: _onSignInButtonPress, child: Text( AppTranslations.of(context).text("login.title"), style: Provider.of(context).isEng ? TextStyle( color: left, fontSize: 14.0, fontFamily: "WorkSansSemiBold") : TextStyle( color: left, fontSize: 15.0, fontFamily: "MyanmarUnicode"), ), ), ), //Container(height: 33.0, width: 1.0, color: Colors.white), Expanded( child: FlatButton( onPressed: _onSignUpButtonPress, child: Text( AppTranslations.of(context).text("sing.title"), style: Provider.of(context).isEng ? TextStyle( color: right, fontSize: 14.0, fontFamily: "WorkSansSemiBold") : TextStyle( color: right, fontSize: 15.0, fontFamily: "MyanmarUnicode"), ), ), ), ], ), ), ); } Widget _buildLogin(BuildContext context) { return Container( child: ListView( children: [ Column( children: [ Form( key: loginFormKey, child: Card( elevation: 2.0, color: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.0), ), child: Container( width: 300.0, child: Column( children: [ Padding( padding: EdgeInsets.only(left: 25.0, right: 25.0), child: TextFormField( focusNode: myFocusNodeEmailLogin, controller: loginPhoneController, keyboardType: TextInputType.phone, style: TextStyle( fontFamily: "WorkSansSemiBold", fontSize: 16.0, color: Colors.black), decoration: InputDecoration( border: InputBorder.none, icon: Icon( FontAwesomeIcons.phone, color: Colors.black, size: 22.0, ), labelText: AppTranslations.of(context) .text("login.phone"), labelStyle: Provider.of(context).isEng ? TextStyle( fontFamily: "WorkSansSemiBold", color: Colors.grey) : TextStyle( fontFamily: "MyanmarUnicode", color: Colors.grey), ), validator: _validatePhone, ), ), Container( width: 250.0, height: 1.0, color: Colors.grey[400], ), Padding( padding: EdgeInsets.only(left: 25.0, right: 25.0), child: TextFormField( focusNode: myFocusNodePasswordLogin, controller: loginPasswordController, obscureText: _obscureTextLogin, style: TextStyle( fontFamily: "WorkSansSemiBold", fontSize: 16.0, color: Colors.black), decoration: InputDecoration( border: InputBorder.none, icon: Icon( FontAwesomeIcons.lock, size: 22.0, color: Colors.black, ), labelText: AppTranslations.of(context) .text("login.password"), labelStyle: Provider.of(context).isEng ? TextStyle( fontFamily: "WorkSansSemiBold", color: Colors.grey) : TextStyle( fontFamily: "MyanmarUnicode", color: Colors.grey), suffixIcon: GestureDetector( onTap: _toggleLogin, child: Icon( _obscureTextLogin ? FontAwesomeIcons.eye : FontAwesomeIcons.eyeSlash, size: 15.0, color: Colors.black, ), ), ), validator: _validatePassword, ), ), ], ), ), ), ), Container( decoration: new BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(5.0)), color: Theme.primaryColor, ), child: MaterialButton( child: Padding( padding: const EdgeInsets.symmetric( vertical: 10.0, horizontal: 42.0), child: Text( AppTranslations.of(context).text("login.btn"), style: Provider.of(context).isEng ? TextStyle( color: Colors.white, fontSize: 18.0, fontWeight: FontWeight.bold, fontFamily: "WorkSansBold") : TextStyle( color: Colors.white, fontSize: 16.0, fontWeight: FontWeight.bold, fontFamily: "MyanmarUnicode"), ), ), onPressed: () => _login(context)), ), ], ), Padding( padding: EdgeInsets.only(top: 10.0), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( decoration: BoxDecoration( gradient: new LinearGradient( colors: [ Colors.white10, Colors.white, ], begin: const FractionalOffset(0.0, 0.0), end: const FractionalOffset(1.0, 1.0), stops: [0.0, 1.0], tileMode: TileMode.clamp), ), width: 100.0, height: 1.0, ), Container( decoration: BoxDecoration( gradient: new LinearGradient( colors: [ Colors.white, Colors.white10, ], begin: const FractionalOffset(0.0, 0.0), end: const FractionalOffset(1.0, 1.0), stops: [0.0, 1.0], tileMode: TileMode.clamp), ), width: 100.0, height: 1.0, ), ], ), ), Padding( padding: EdgeInsets.only(top: 10.0), child: FlatButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => ForgetPassword( phoneNumber: loginPhoneController.text, ))); }, child: Text( AppTranslations.of(context).text("login.forgot_password"), style: Provider.of(context).isEng ? TextStyle( decoration: TextDecoration.underline, color: Colors.black, fontSize: 16.0, fontFamily: "WorkSansMedium") : TextStyle( decoration: TextDecoration.underline, color: Colors.black, fontSize: 16.0, fontFamily: "MyanmarUnicode"), )), ), ], ), ); } Widget _buildSignUp(BuildContext context) { return Container( child: ListView( children: [ Column( children: [ Form( key: signupFormKey, child: Card( elevation: 2.0, color: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.0), ), child: Container( width: 300.0, child: Column( children: [ Padding( padding: EdgeInsets.only(left: 25.0, right: 25.0), child: TextFormField( focusNode: myFocusNodeName, controller: signupNameController, keyboardType: TextInputType.text, textCapitalization: TextCapitalization.words, style: TextStyle( fontFamily: "WorkSansSemiBold", fontSize: 16.0, color: Colors.black), decoration: InputDecoration( border: InputBorder.none, icon: Icon( FontAwesomeIcons.user, color: Colors.black, ), labelText: AppTranslations.of(context) .text("login.name"), labelStyle: Provider.of(context).isEng ? TextStyle( fontFamily: "WorkSansSemiBold", color: Colors.grey) : TextStyle( fontFamily: "MyanmarUnicode", color: Colors.grey), hintStyle: TextStyle( fontFamily: "WorkSansSemiBold", fontSize: 16.0), ), validator: (value) { if (value.isEmpty) { return AppTranslations.of(context) .text("login.name_empty"); } return null; }), ), Container( width: 250.0, height: 1.0, color: Colors.grey[400], ), Padding( padding: EdgeInsets.only(left: 25.0, right: 25.0), child: TextFormField( focusNode: myFocusNodeEmail, controller: signupPhoneNumberController, keyboardType: TextInputType.phone, style: TextStyle( fontFamily: "WorkSansSemiBold", fontSize: 16.0, color: Colors.black), decoration: InputDecoration( border: InputBorder.none, icon: Icon( FontAwesomeIcons.phone, color: Colors.black, ), labelText: AppTranslations.of(context) .text("login.phone"), labelStyle: Provider.of(context).isEng ? TextStyle( fontFamily: "WorkSansSemiBold", color: Colors.grey) : TextStyle( fontFamily: "MyanmarUnicode", color: Colors.grey), ), validator: _validatePhone), ), Container( width: 250.0, height: 1.0, color: Colors.grey[400], ), Padding( padding: EdgeInsets.only(left: 25.0, right: 25.0), child: TextFormField( focusNode: myFocusNodePassword, controller: signupPasswordController, obscureText: _obscureTextSignup, style: TextStyle( fontFamily: "WorkSansSemiBold", fontSize: 16.0, color: Colors.black), decoration: InputDecoration( border: InputBorder.none, icon: Icon( FontAwesomeIcons.lock, color: Colors.black, ), labelText: AppTranslations.of(context) .text("login.password"), labelStyle: Provider.of(context).isEng ? TextStyle( fontFamily: "WorkSansSemiBold", color: Colors.grey) : TextStyle( fontFamily: "MyanmarUnicode", color: Colors.grey), suffixIcon: GestureDetector( onTap: _toggleSignup, child: Icon( _obscureTextSignup ? FontAwesomeIcons.eye : FontAwesomeIcons.eyeSlash, size: 15.0, color: Colors.black, ), ), ), validator: _validatePassword, ), ), Container( width: 250.0, height: 1.0, color: Colors.grey[400], ), Padding( padding: EdgeInsets.only(left: 25.0, right: 25.0), child: TextFormField( controller: signupConfirmPasswordController, obscureText: _obscureTextSignupConfirm, style: TextStyle( fontFamily: "WorkSansSemiBold", fontSize: 16.0, color: Colors.black), decoration: InputDecoration( border: InputBorder.none, icon: Icon( FontAwesomeIcons.lock, color: Colors.black, ), labelText: AppTranslations.of(context) .text("login.confirm_password"), labelStyle: Provider.of(context).isEng ? TextStyle( fontFamily: "WorkSansSemiBold", color: Colors.grey) : TextStyle( fontFamily: "MyanmarUnicode", color: Colors.grey), suffixIcon: GestureDetector( onTap: _toggleSignupConfirm, child: Icon( _obscureTextSignupConfirm ? FontAwesomeIcons.eye : FontAwesomeIcons.eyeSlash, size: 15.0, color: Colors.black, ), ), ), validator: _validatePassword, ), ), ], ), ), ), ), Container( // margin: EdgeInsets.only(top: 320.0), decoration: new BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(5.0)), color: Theme.primaryColor, ), child: MaterialButton( highlightColor: Colors.transparent, splashColor: Theme.LoginColors.loginGradientEnd, //shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5.0))), child: Padding( padding: const EdgeInsets.symmetric( vertical: 10.0, horizontal: 42.0), child: Text( AppTranslations.of(context).text("sing.up"), style: Provider.of(context).isEng ? TextStyle( color: Colors.white, fontSize: 18.0, fontFamily: "WorkSansBold") : TextStyle( color: Colors.white, fontSize: 16.0, fontFamily: "MyanmarUnicode"), ), ), onPressed: () => _signup(context)), ), ], ), ], ), ); } void _onSignInButtonPress() { _pageController.animateToPage(0, duration: Duration(milliseconds: 500), curve: Curves.decelerate); } void _onSignUpButtonPress() { _pageController?.animateToPage(1, duration: Duration(milliseconds: 500), curve: Curves.decelerate); } void _toggleLogin() { setState(() { _obscureTextLogin = !_obscureTextLogin; }); } void _toggleSignup() { setState(() { _obscureTextSignup = !_obscureTextSignup; }); } void _toggleSignupConfirm() { setState(() { _obscureTextSignupConfirm = !_obscureTextSignupConfirm; }); } void _signup(BuildContext context) async { if (!signupFormKey.currentState.validate()) { return; } setState(() { _isLoading = true; }); MainModel authModel = Provider.of(context); var name = signupNameController.text; var password = signupPasswordController.text; var confirmPassword = signupConfirmPasswordController.text; var phoneNumber = signupPhoneNumberController.text; try { await authModel.signup(name, password, confirmPassword, phoneNumber); Navigator.push( context, MaterialPageRoute( builder: (context) => SmsCodePage(id: phoneNumber, password: password), ), ); } catch (e) { showMsgDialog(context, "Error", e.toString()); } finally { Future.delayed(Duration(seconds: 1), () { if (mounted) { setState(() { _isLoading = false; }); } }); } } void _login(BuildContext context) async { if (!loginFormKey.currentState.validate()) { return; } setState(() { _isLoading = true; }); MainModel mainModel = Provider.of(context); var phoneNumber = loginPhoneController.text; var password = loginPasswordController.text; try { await mainModel.login(phoneNumber, password); Navigator.pushNamedAndRemoveUntil(context, "/", (r) => false); } catch (e) { showMsgDialog(context, "Error", e.toString()); } Future.delayed(Duration(seconds: 1), () { if (mounted) { setState(() { _isLoading = false; }); } }); } String _validatePassword(value) { if (value.isEmpty) { return AppTranslations.of(context).text("login.password_empty"); } if (value.length < 6) { return AppTranslations.of(context).text("login.password_size"); } return null; } String _validatePhone(value) { if (value.isEmpty) { return AppTranslations.of(context).text("login.phone_empty"); } if (!value.startsWith("09") && !value.startsWith("959")) { return 'Only "09 or 959".'; } return null; } Future _forgetPassword() async { var phoneNumber = loginPhoneController.text; if (phoneNumber.isEmpty) { showMsgDialog(context, "Error", "Please input phone number"); return; } setState(() { _isLoading = true; }); try { UserModel userModel = Provider.of(context); await userModel.forgetPassword(phoneNumber); Navigator.push( context, MaterialPageRoute( builder: (context) => ResetPasswordPage(phoneNumber))); } catch (e) { showMsgDialog(context, "Error", e.toString()); } finally { setState(() { _isLoading = false; }); } } }