import 'package:auto_route/auto_route.dart'; import 'package:eitc_erm_dental_flutter/app_router.gr.dart'; import 'package:eitc_erm_dental_flutter/funcs.dart'; import 'package:eitc_erm_dental_flutter/pages/login/vm/login_view_model.dart'; import 'package:eitc_erm_dental_flutter/pages/patient/vm/patient_view_model.dart'; import 'package:eitc_erm_dental_flutter/vm/global_view_model.dart'; import 'package:eitc_erm_dental_flutter/widget/counting_button.dart'; import 'package:eitc_erm_dental_flutter/widget/main_button.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import '../../global.dart'; @RoutePage(name: "loginRoute") class LoginPage extends ConsumerStatefulWidget { final bool cancelable; const LoginPage({super.key, required this.cancelable}); @override ConsumerState createState() => _LoginPageState(); } class _LoginPageState extends ConsumerState { final TextEditingController _mobileTextController = TextEditingController(); final TextEditingController _captchaTextController = TextEditingController(); final CountingButtonController _countingButtonController = CountingButtonController(); bool _isAgreementChecked = false; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { //刷新本地就诊人列表数据 ref.invalidate(localPatientListProvider); if (!widget.cancelable) { //停止检查连接 ref .read(deviceConnectStatusProvider(videoChannel).notifier) .stopCheck(); } }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( automaticallyImplyLeading: widget.cancelable, forceMaterialTransparency: true, ), body: SafeArea( child: SingleChildScrollView( child: Padding( padding: EdgeInsets.fromLTRB(15.w, 50.h, 15.w, 0.0), child: Column(children: [ Text( getS().registerHint, textAlign: TextAlign.center, style: Theme.of(context).textTheme.titleMedium, ), SizedBox( height: 80.h, ), _getInputs(), SizedBox( height: 10.h, ), _getAgreements(), SizedBox( height: 80.h, ), SizedBox( width: double.infinity, child: MainButton(text: getS().login, onPressed: _onLogin), ), ]), ), )), ); } ///输入框 Widget _getInputs() { //保证内部元素高度一致 return IntrinsicHeight( child: Row( children: [ Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Text(getS().mobileColon), SizedBox( height: 30.h, ), Text(getS().captchaColon) ], ), SizedBox( width: 10.w, ), Expanded( child: Column( children: [ _getMobileInput(), SizedBox( height: 30.h, ), Row( children: [ Expanded(child: _getCaptchInput()), SizedBox( width: 10.w, ), CountingButton( controller: _countingButtonController, onGetChild: (t, isCounting) { String str = getS().send; if (isCounting) { str = "$str(${60 - t})"; } return Text( str, style: TextStyle( color: Theme.of(context).colorScheme.onPrimary, fontWeight: FontWeight.bold), ); }, onPressed: _onSendCaptcha, ) ], ), ], )) ], ), ); } ///手机号输入框 Widget _getMobileInput() { return _getTextInput(_mobileTextController, 11, TextInputType.phone, getS().mobileInputHint, [FilteringTextInputFormatter.digitsOnly]); } ///验证码输入框 Widget _getCaptchInput() { return _getTextInput(_captchaTextController, 6, TextInputType.number, getS().captchaInputHint, [FilteringTextInputFormatter.digitsOnly]); } ///文本输入框 Widget _getTextInput( TextEditingController controller, int? maxLength, TextInputType? keyboardType, String hint, List? inputFormatters) { return Container( padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h), decoration: BoxDecoration( color: Theme.of(context).colorScheme.surfaceContainerHigh, borderRadius: BorderRadius.circular(8.r)), child: TextField( controller: controller, maxLines: 1, maxLength: maxLength, keyboardType: keyboardType, decoration: InputDecoration( isDense: true, contentPadding: EdgeInsets.zero, border: const OutlineInputBorder(borderSide: BorderSide.none), counterText: "", hintText: hint, hintStyle: const TextStyle(color: Color(0xFF999999)), ), inputFormatters: inputFormatters, ), ); } ///用户协议及隐私协议 Widget _getAgreements() { return Row( children: [ Checkbox( value: _isAgreementChecked, onChanged: (bo) { setState(() { _isAgreementChecked = bo ?? false; }); }), Expanded( child: Text.rich( softWrap: true, TextSpan(text: getS().readAndAgree, children: [ getClickTextSpan(getS().userAgreementBookTitle, () => gotoUserAgreement(context, checkWifi: false)), TextSpan(text: "、"), getClickTextSpan(getS().privacyPolicyBookTitle, () => gotoPrivacyPolicy(context, checkWifi: false)), ]), )), ], ); } TextSpan getClickTextSpan(String text, GestureTapCallback onTap) { return TextSpan( text: text, style: TextStyle(color: Theme.of(context).colorScheme.primary), recognizer: TapGestureRecognizer()..onTap = onTap); } ///发送验证码 void _onSendCaptcha() async { if (!validMobile(_mobileTextController.text)) { showToast(text: getS().mobileFormatError); return; } _countingButtonController.start(60); bool bo = await ref .read(captchaProvider.notifier) .sendCaptcha(_mobileTextController.text); showToast(text: bo ? getS().captchaHasSend : getS().captchaSendError); } ///验证输入 Future _validInput() async { ///手机号 if (_mobileTextController.text.isEmpty) { showToast(text: getS().mobileInputHint); return SynchronousFuture(false); } ///手机号格式 if (!validMobile(_mobileTextController.text)) { showToast(text: getS().mobileFormatError); return SynchronousFuture(false); } ///验证码 if (_captchaTextController.text.isEmpty) { showToast(text: getS().captchaInputHint); return SynchronousFuture(false); } ///用户协议及隐私协议 if (!_isAgreementChecked) { showToast(text: getS().needReadAndAgree); return SynchronousFuture(false); } //调用接口验证验证码 logd("验证验证码"); if (!await ref.read(captchaProvider.notifier).checkCaptcha( _captchaTextController.text, _mobileTextController.text)) { showToast(text: getS().captchaError); return SynchronousFuture(false); } return SynchronousFuture(true); } void _onLogin() async { if (!await _validInput()) { return; } logd("登录"); bool bo = await ref .read(loginProvider.notifier) .login(_mobileTextController.text); if (bo) { showToast(text: getS().loginSuccess); if (mounted) { popAllRoutes(context); context.pushRoute(const MainRoute()); //刷新本地就诊人列表数据 ref.invalidate(localPatientListProvider); } } else { showToast(text: getS().loginFailed); } } }