import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:provider/provider.dart'; import 'package:zhiying_comm/pages/login_page/invite/login_invite_page.dart'; import 'package:zhiying_comm/pages/login_page/model/login_style_model.dart'; import 'package:zhiying_comm/util/shared_prefe_util.dart'; import 'package:zhiying_comm/zhiying_comm.dart'; import 'bloc/bloc.dart'; import 'bloc/login_account_repository.dart'; import 'login_account_sk.dart'; import 'widget/slide_verify_widget.dart'; import 'package:zhiying_comm/util/empty_util.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'widget/vcode_widget.dart'; /// /// 账号登陆(手机验证码,密码) /// class LoginAccountPage extends StatelessWidget { final Map model; const LoginAccountPage(this.model, {Key key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: false, backgroundColor: HexColor.fromHex('#FFFFFF'), body: BlocProvider( create: (_) => LoginAccountBloc(repository: LoginAccountRepository())..add(LoginAccountInitEvent()), child: LoginAccountPageContianer(), ), ); } } /// 啦啦啦 class LoginAccountPageContianer extends StatefulWidget { @override _LoginAccountPageContianerState createState() => _LoginAccountPageContianerState(); } /// /// 主体逻辑 /// class _LoginAccountPageContianerState extends State implements OnClickListener { TextEditingController _phoneEdController; TextEditingController _vcodeEdController; TextEditingController _passEdController; FocusNode _phoneFN; FocusNode _passFN; FocusNode _vcodeFN; /// 是否使用验证码登陆 默认使用 bool _useVcode = true; /// 是否可以登陆 bool _canSubmit = false; /// 是否同意协议 bool _acceptAgreement = true; /// 是否显示第三方验证码 bool _showOtherVcode = false; /// 是否登录中 bool _isLogging = false; /// 设置苹果审核UI void _settingIosReviewUI() async{ String is_ios_review = await SharedPreferencesUtil.getStringValue(GlobalConfig.IS_IOS_REVIEW, defaultVal: '0'); if(Platform.isIOS && is_ios_review == '1' ){ _useVcode = false; } } /// 跳转到邀请码页面 void _openInvitePage(String mobile, String captcha) { print('跳转到邀请码页面'); RouterUtil.hideKeyboard(context); Navigator.push( context, CupertinoPageRoute( // builder: (_) => PageFactory.create('login_invite', null) builder: (_) => LoginInvitePage({'mobile': mobile, 'captcha': captcha}))); } /// 登陆成功页面 void _openLoginSuccessPage() { RouterUtil.hideKeyboard(context); RouterUtil.goBackHomePage(context); // Navigator.pushAndRemoveUntil(context, MaterialPageRoute(builder: (BuildContext context) => PageFactory.create('homePage', null)), (Route route) => false,); } /// 登陆 void _submitOnClick() { print('登陆'); if (_checkParam(true) && !_isLogging) { setState(() { _isLogging = true; }); if (_useVcode) { BlocProvider.of(context) .add(LoginAccountTypeVcodeEvent(mobile: _phoneEdController?.text?.toString()?.trim() ?? '', captcha: _vcodeEdController?.text?.toString()?.trim() ?? '')); } else { BlocProvider.of(context) .add(LoginAccountTypePasswordEvent(username: _phoneEdController?.text?.toString()?.trim() ?? '', password: _passEdController?.text?.toString()?.trim() ?? '')); } } else { Logger.log('参数有误 或者 正在登录中。。。'); } } /// 切换登陆方式 void _changeLoginTypeOnClick() { print('切换登陆'); _passFN?.unfocus(); _vcodeFN?.unfocus(); _phoneFN?.unfocus(); setState(() { _useVcode = !_useVcode; _canSubmit = false; }); // 清除缓存 if (_useVcode) { _passEdController?.clear(); } else { _vcodeEdController?.clear(); } } /// 同意协议 void _agreeOnClick() { print('同意协议'); setState(() { _acceptAgreement = !_acceptAgreement; }); _checkParam(false); } /// 打开协议 void _openAgreement(String url) { if (!EmptyUtil.isEmpty(url)) { print('打开协议$url'); RouterUtil.openWebview(url, context); } } /// 输入框监听 void _onChange(string) { print('$string'); _checkParam(false); } /// 校验登陆参数 bool _checkParam(bool needToast) { // 验证码 if (_useVcode) { String phone = _phoneEdController?.text?.toString()?.trim() ?? null; String vcode = _vcodeEdController?.text?.toString()?.trim() ?? null; if (EmptyUtil.isEmpty(phone)) { if (needToast) Fluttertoast.showToast(msg: '手机号不能为空!'); return false; } if (phone.length != 11) { if (needToast) Fluttertoast.showToast(msg: '手机号格式有误!'); return false; } if (EmptyUtil.isEmpty(vcode)) { if (needToast) Fluttertoast.showToast(msg: '验证码不能为空!'); return false; } if (vcode.length < 4) { if (needToast) Fluttertoast.showToast(msg: '验证码号格式有误!'); return false; } } else { String phone = _phoneEdController?.text?.toString()?.trim() ?? null; String pass = _passEdController?.text?.toString()?.trim() ?? null; if (EmptyUtil.isEmpty(phone)) { if (needToast) Fluttertoast.showToast(msg: '手机号不能为空!'); return false; } if (phone.length != 11) { if (needToast) Fluttertoast.showToast(msg: '手机号格式有误!'); return false; } if (EmptyUtil.isEmpty(pass)) { if (needToast) Fluttertoast.showToast(msg: '验证码不能为空!'); return false; } if (pass.length < 4) { if (needToast) Fluttertoast.showToast(msg: '验证码号格式有误!'); return false; } } if (!_acceptAgreement) { if (needToast) Fluttertoast.showToast(msg: '请同意用户协议与隐私政策'); return false; } setState(() { _canSubmit = true; }); return true; } /// 检测手机号是否合法 bool _checkPhoneNumParam(bool needToast) { String phone = _phoneEdController?.text?.toString()?.trim() ?? null; if (EmptyUtil.isEmpty(phone)) { if (needToast) Fluttertoast.showToast(msg: '手机号不能为空!'); return false; } if (phone.length != 11) { if (needToast) Fluttertoast.showToast(msg: '手机号格式有误!'); return false; } return true; } @override void initState() { _phoneEdController = TextEditingController(); _passEdController = TextEditingController(); _vcodeEdController = TextEditingController(); _vcodeFN = FocusNode(); _passFN = FocusNode(); _phoneFN = FocusNode(); // 设置苹果审核UI _settingIosReviewUI(); super.initState(); } @override void dispose() { _phoneEdController?.dispose(); _passEdController?.dispose(); _vcodeEdController?.dispose(); _phoneFN?.unfocus(); _passFN?.unfocus(); _vcodeFN?.unfocus(); _phoneFN?.dispose(); _passFN?.dispose(); _vcodeFN?.dispose(); super.dispose(); } @override bool onVcodeClick() { /// 获取验证码 if (_checkPhoneNumParam(true)) { BlocProvider.of(context).add(LoginAccountGetVcodeEvent(mobile: _phoneEdController?.text?.toString()?.trim() ?? '')); return true; } return false; } @override Widget build(BuildContext context) { return BlocConsumer( listener: (context, state) { if (state is LoginAccountLoginSuccessState) {} }, buildWhen: (prev, current) { // 登陆失败 if (current is LoginAccountLoginErrorState) { // Fluttertoast.showToast(msg: '登陆失败'); setState(() { _isLogging = false; }); return false; } // 登陆成功 if (current is LoginAccountLoginSuccessState) { if (current?.model?.registerInviteCodeEnable != '1') { /// 缓存登陆数据 Provider.of(context, listen: false)?.setUserInfo(current.model); Fluttertoast.showToast(msg: '登录成功~'); _isLogging = true; /// 打开登录成功首页 _openLoginSuccessPage(); } else { /// 打开邀请页面 _openInvitePage(current?.model?.mobile, current?.model?.captcha); } return false; } // 获取验证码成功 if (current is LoginAccountGetVcodeSuccessState) { // Fluttertoast.showToast(msg: '验证码下发成功'); return false; } // 获取验证码失败 if (current is LoginAccountGetVcodeErrorState) { // Fluttertoast.showToast(msg: '验证码获取失败~'); return false; } return true; }, builder: (context, state) { print('state = $state'); if (state is LoginAccountLoadedState) { return _getMainWidget(state.model); } // 返回骨架屏 return LoginAccountSkeleton(); }, ); } /// 主页面 Widget _getMainWidget(LoginStyleModel model) { print(model); return Column( children: [ /// appBar _getAppBarWidget(model), /// title Padding(padding: const EdgeInsets.only(left: 27.5, right: 27.5, top: 40), child: _getTitleWidget(model)), /// 手机输入框 Padding(padding: const EdgeInsets.only(left: 27.5, right: 27.5, top: 30), child: _getPhoneWidget(model)), /// 验证码 Visibility(visible: _useVcode, child: Padding(padding: const EdgeInsets.only(left: 27.5, right: 27.5, top: 15), child: _getVcodeWidget(model))), /// 密码 Visibility(visible: !_useVcode, child: Padding(padding: const EdgeInsets.only(left: 27.5, right: 27.5, top: 15), child: _getPassInputWidget(model))), /// 第三方验证码 Visibility(visible: _showOtherVcode, child: Padding(padding: const EdgeInsets.only(left: 27.5, right: 27.5, top: 15), child: _getOtherVcodeInputWidget(model))), /// 切换登陆方式tip Padding(padding: const EdgeInsets.only(top: 15), child: _getChangeTipWidget(model)), /// 登录按钮 Padding(padding: const EdgeInsets.only(left: 27.5, right: 27.5, top: 30), child: _getSubmiBtnWidget(model)), /// 协议 Padding(padding: const EdgeInsets.only(top: 15), child: _getProtoclWidget(model)), /// 底部提示tip Visibility(visible: _useVcode, child: Expanded(child: Align(alignment: Alignment.bottomCenter, child: _getBottomTipWidget(model)))) ], ); } /// appBar Widget _getAppBarWidget(LoginStyleModel model) { return AppBar( backgroundColor: HexColor.fromHex('#FFFFFF'), brightness: Brightness.light, elevation: 0, title: Text( model?.mobile?.appBarTitle ?? '登录', style: TextStyle(color: HexColor.fromHex(model?.mobile?.appBarTitleColor ?? '#333333')), ), centerTitle: true, leading: IconButton( icon: Icon( Icons.arrow_back_ios, size: 22, color: HexColor.fromHex('#333333'), ), onPressed: () => Navigator.maybePop(context), ), ); } /// title Widget _getTitleWidget(LoginStyleModel model) { return Align( alignment: Alignment.centerLeft, child: Text( model?.mobile?.title ?? '您好,欢迎登录', style: TextStyle(color: HexColor.fromHex(model?.mobile?.titleColor ?? '#333333'), fontSize: 25), )); } /// 手机输入框 Widget _getPhoneWidget(LoginStyleModel model) { return _getCustomInputWidget( hint: model?.mobile?.inputMobileHintText ?? '请输入您的手机号', controller: _phoneEdController, focusNode: _phoneFN, onChanged: _onChange, hintColor: model?.mobile?.inputHintColor ?? '#999999', bgColor: model?.mobile?.inputBgColor ?? '#F7F7F7', textColor: model?.mobile?.inputTextColor ?? '#333333', iconUrl: model?.mobile?.inputMobileIcon ?? ''); } /// 验证码输入框 Widget _getVcodeWidget(LoginStyleModel model) { return Container( height: 42, child: Stack( children: [ _getCustomInputWidget( controller: _vcodeEdController, focusNode: _vcodeFN, onChanged: _onChange, hintColor: model?.mobile?.inputHintColor ?? '#999999', hint: model?.mobile?.inputVcodeHintText ?? '请输入您的验证码', bgColor: model?.mobile?.inputBgColor ?? '#F7F7F7', textColor: model?.mobile?.inputTextColor ?? '#333333', iconUrl: model?.mobile?.inputVcodeIcon ?? ''), Align(alignment: Alignment.centerRight, child: _getVcodeButtonWidget(model)), ], ), ); } /// 验证码按钮 Widget _getVcodeButtonWidget(LoginStyleModel model) { return VcodeWidget( onCallBack: this, awaitTime: int.parse(model?.mobile?.vcodeTime ?? '60'), btnAwaitText: '秒', btnText: '获取验证码', btnTextColor: model?.mobile?.btnVcodeTextColor ?? '#FFFFFF', color: model?.mobile?.btnVcodeBgColor ?? '#FF4343', disabledColor: model?.mobile?.btnVcodeBanBgColor ?? '#DDDDDD', disabledTextColor: model?.mobile?.btnVcodeBanTextColor ?? '#FFFFFF'); } /// 第三方验证码输入框 Widget _getOtherVcodeInputWidget(var model) { return Container( width: 240, height: 42, alignment: Alignment.centerLeft, child: SlideVerifyWidget( width: 240, ), // child: Row( // children: [ // // 输入框 // Expanded( // child: _getCustomInputWidget(hint: '请输入右方验证码', hintColor: '#999999', textColor: '#333333', bgColor: '#F7F7F7', iconUrl: null, ) // ), // // 第三方验证码 // Container( // margin: const EdgeInsets.only(left: 5), // width: 100, // height: double.infinity, // decoration: BoxDecoration( // borderRadius: BorderRadius.circular(8), // color: Colors.red // ), // ), // // ], // ), ); } /// 密码输入框 Widget _getPassInputWidget(LoginStyleModel model) { return Container( height: 42, child: _getCustomInputWidget( obscureText: true, keyboardType: TextInputType.text, controller: _passEdController, focusNode: _passFN, onChanged: _onChange, hint: model?.mobile?.inputPassHintText ?? '请输入您的密码', iconUrl: model?.mobile?.inputPassIcon ?? '', hintColor: model?.mobile?.inputHintColor ?? '#999999', textColor: model?.mobile?.inputTextColor ?? '#333333', bgColor: model?.mobile?.inputBgColor ?? '#F7F7F7'), ); } /// 切换登陆方式tip Widget _getChangeTipWidget(LoginStyleModel model) { return GestureDetector( behavior: HitTestBehavior.opaque, onTap: () => _changeLoginTypeOnClick(), child: Text( _useVcode ? model?.mobile?.textUsePassTip ?? '使用密码登录' : model?.mobile?.textUseVcodeTip ?? '使用验证码登陆', style: TextStyle(fontSize: 12, color: HexColor.fromHex(_useVcode ? model?.mobile?.textUsePassTipColor ?? '#999999' : model?.mobile?.textUseVcodeTipColor ?? '#999999')), ), ); } /// 登陆按钮 Widget _getSubmiBtnWidget(LoginStyleModel model) { return Material( child: Container( height: 52, width: double.infinity, color: Colors.white, child: RaisedButton( child: Text( _isLogging ? '登录中...' : model?.mobile?.btnLoginText ?? '立即登录', style: TextStyle(fontSize: 15), ), textColor: HexColor.fromHex(model?.mobile?.btnLoginTextColor ?? '#FFFFFF'), color: HexColor.fromHex(model?.mobile?.btnLoginBgColor ?? '#FF3939'), disabledColor: HexColor.fromHex(model?.mobile?.btnLoginBanBgColor ?? '#F5F5F5'), disabledTextColor: HexColor.fromHex(model?.mobile?.btnLoginBanTextColor ?? '#999999'), elevation: 5, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(52 / 2)), onPressed: _canSubmit ? _submitOnClick : null, ), ), ); } /// 协议 Widget _getProtoclWidget(LoginStyleModel model) { // return Text('同意《嗨如意用户协议》 及《营私政策》', style: TextStyle(fontSize: 11, color: HexColor.fromHex('#C0C0C0'))); return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ /// 图标 GestureDetector( behavior: HitTestBehavior.opaque, onTap: () => _agreeOnClick(), child: Padding( padding: const EdgeInsets.all(8.0), child: CachedNetworkImage( imageUrl: _acceptAgreement ? model?.mobile?.protocolSelectIcon ?? '' : model?.mobile?.protocolUnselectIcon ?? '', width: 12, ))), /// 协议文字 RichText( text: TextSpan( text: '', children: model.mobile.protocol.map((item) { return TextSpan( text: item?.text, style: TextStyle(color: HexColor.fromHex(item?.textColor), fontSize: 10), recognizer: TapGestureRecognizer() ..onTap = () { _openAgreement(item.url); }); }).toList()), ) ], ); } /// 底部提示tip Widget _getBottomTipWidget(LoginStyleModel model) { return Padding( padding: const EdgeInsets.only(bottom: 25), child: Text( model?.mobile?.textBottomTip ?? '未注册过的手机将自动注册', style: TextStyle(fontSize: 11, color: HexColor.fromHex(model?.mobile?.textBottomTipColor ?? '#999999')), ), ); } /// 自定义输入框 Widget _getCustomInputWidget({ String hint, String hintColor, String bgColor, String textColor, String iconUrl, TextEditingController controller, ValueChanged onChanged, FocusNode focusNode, TextInputType keyboardType, bool obscureText = false, }) { var border = OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: HexColor.fromHex(bgColor), width: 0)); return Container( height: 42, padding: const EdgeInsets.symmetric(horizontal: 15), decoration: BoxDecoration( color: HexColor.fromHex(bgColor), borderRadius: BorderRadius.circular(8), ), child: Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ CachedNetworkImage( imageUrl: iconUrl ?? '', width: 10, ), Expanded( child: TextField( obscureText: obscureText ?? false, controller: controller, focusNode: focusNode, onChanged: onChanged, expands: false, style: TextStyle(color: HexColor.fromHex(textColor)), maxLines: 1, keyboardType: keyboardType ?? TextInputType.number, decoration: InputDecoration( contentPadding: EdgeInsets.only(top: 30, left: 7.5), hintText: hint, hintStyle: TextStyle(fontSize: 13, color: HexColor.fromHex(hintColor)), hintMaxLines: 1, filled: true, fillColor: Colors.transparent, border: border, focusedBorder: border, enabledBorder: border, disabledBorder: border, errorBorder: border, focusedErrorBorder: border, ), ), ), ], ), ); } }