From c9df43c8fb1b436b67d8b568537673eb20ba5e81 Mon Sep 17 00:00:00 2001 From: Key Date: Sun, 2 Jul 2023 13:50:51 +0800 Subject: [PATCH] feat: 修改个人信息、接口 --- assets/images/bg_button_blue.png | Bin 0 -> 15191 bytes assets/images/bg_button_blue_accent.png | Bin 0 -> 15215 bytes assets/images/bg_steve_write.png | Bin 0 -> 177724 bytes lib/common/core/assets_const.dart | 2 ++ lib/common/request/apis.dart | 2 +- lib/common/widgets/textfield_customer_widget.dart | 46 ++++++++++++++++++++++------------------------ lib/pages/login/loginpage/bloc/login_bloc.dart | 16 ++++++++-------- lib/pages/login/loginpage/bloc/login_event.dart | 4 ++-- lib/pages/login/loginpage/bloc/login_state.dart | 6 +++--- lib/pages/login/loginpage/login_page.dart | 2 +- lib/pages/login/loginpage/time_widget.dart | 2 +- lib/pages/login/setpwd/set_pwd_page.dart | 14 +++++++++----- lib/pages/user/bloc/user_bloc.dart | 33 ++++++++++++++++++++++++++++++++- lib/pages/user/bloc/user_event.dart | 6 ++++++ lib/pages/user/information/user_information_page.dart | 14 +++++++++----- lib/pages/user/modify/modify_user_information_page.dart | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- lib/pages/user/user_page.dart | 19 +++++++++++-------- lib/route/route.dart | 21 ++++++++++++++++----- lib/utils/text_input_formatter.dart | 23 +++++++++++++++++++++++ 19 files changed, 227 insertions(+), 66 deletions(-) create mode 100644 assets/images/bg_button_blue.png create mode 100644 assets/images/bg_button_blue_accent.png create mode 100644 assets/images/bg_steve_write.png create mode 100644 lib/utils/text_input_formatter.dart diff --git a/assets/images/bg_button_blue.png b/assets/images/bg_button_blue.png new file mode 100644 index 0000000..1bf3005 Binary files /dev/null and b/assets/images/bg_button_blue.png differ diff --git a/assets/images/bg_button_blue_accent.png b/assets/images/bg_button_blue_accent.png new file mode 100644 index 0000000..bf389b2 Binary files /dev/null and b/assets/images/bg_button_blue_accent.png differ diff --git a/assets/images/bg_steve_write.png b/assets/images/bg_steve_write.png new file mode 100644 index 0000000..ee491c8 Binary files /dev/null and b/assets/images/bg_steve_write.png differ diff --git a/lib/common/core/assets_const.dart b/lib/common/core/assets_const.dart index 64a5f3b..e078cda 100644 --- a/lib/common/core/assets_const.dart +++ b/lib/common/core/assets_const.dart @@ -5,5 +5,7 @@ class AssetsConst { static const String icNext = '${_assetImagePrefix}ic_next.png'; static const String bgUserInformationText = '${_assetImagePrefix}bg_user_information_text.png'; static const String bgEditUserInformation = '${_assetImagePrefix}bg_edit_information.png'; + static const String bgButtonBlue = '${_assetImagePrefix}bg_button_blue.png'; + static const String bgButtonBlueAccent = '${_assetImagePrefix}bg_button_blue_accent.png'; //static String get icVip2 =>'ic_vip.png'.assetImg; } diff --git a/lib/common/request/apis.dart b/lib/common/request/apis.dart index 70b3217..030d93a 100644 --- a/lib/common/request/apis.dart +++ b/lib/common/request/apis.dart @@ -6,7 +6,7 @@ class Apis { // 接口地址:https://app.apifox.com/link/project/2684751/apis/api-89897678 static const String appConfig = 'system/app/config'; - /// 登陆 + /// 登录 static const String login = 'login'; /// 登出 diff --git a/lib/common/widgets/textfield_customer_widget.dart b/lib/common/widgets/textfield_customer_widget.dart index 3f03510..a435971 100644 --- a/lib/common/widgets/textfield_customer_widget.dart +++ b/lib/common/widgets/textfield_customer_widget.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:wow_english/common/extension/string_extension.dart'; class TextFieldCustomerWidget extends StatefulWidget { - const TextFieldCustomerWidget({super.key, + const TextFieldCustomerWidget({ + super.key, this.controller, this.hitStyle, this.textStyle, @@ -14,7 +16,8 @@ class TextFieldCustomerWidget extends StatefulWidget { this.textAlign, this.textInputType, this.obscureText, - this.onChangeValue + this.onChangeValue, + this.inputFormatters, }); final TextEditingController? controller; @@ -28,6 +31,7 @@ class TextFieldCustomerWidget extends StatefulWidget { final TextInputType? textInputType; final bool? obscureText; final Function(String value)? onChangeValue; + final List? inputFormatters; @override State createState() { @@ -39,38 +43,32 @@ class _TextFieldCustomerWidgetState extends State { @override Widget build(BuildContext context) { return Container( - height: widget.height??45.h, - width: widget.width??double.infinity, + height: widget.height ?? 45.h, + width: widget.width ?? double.infinity, alignment: Alignment.center, decoration: BoxDecoration( image: DecorationImage( - image: AssetImage( - '${widget.bgImageName}'.assetPng - ), - fit: BoxFit.fill, - ) - ), + image: AssetImage('${widget.bgImageName}'.assetPng), + fit: BoxFit.fill, + )), child: TextField( + inputFormatters: widget.inputFormatters, controller: widget.controller, - textAlign: widget.textAlign??TextAlign.center, + textAlign: widget.textAlign ?? TextAlign.center, textInputAction: TextInputAction.done, keyboardType: widget.textInputType, - obscureText: widget.obscureText?? false, + obscureText: widget.obscureText ?? false, decoration: InputDecoration( - hintText: widget.hitText??'', - border: InputBorder.none, - hintStyle: widget.hitStyle?? TextStyle( - fontSize: 16.sp, - color:const Color(0xFF999999) - ) - ), - style: widget.textStyle?? TextStyle( - color: const Color(0xFF333333), - fontSize: 16.sp, - ), + hintText: widget.hitText ?? '', + border: InputBorder.none, + hintStyle: widget.hitStyle ?? TextStyle(fontSize: 16.sp, color: const Color(0xFF999999))), + style: widget.textStyle ?? + TextStyle( + color: const Color(0xFF333333), + fontSize: 16.sp, + ), onChanged: widget.onChangeValue, ), ); } } - diff --git a/lib/pages/login/loginpage/bloc/login_bloc.dart b/lib/pages/login/loginpage/bloc/login_bloc.dart index 8fff7c2..075839b 100644 --- a/lib/pages/login/loginpage/bloc/login_bloc.dart +++ b/lib/pages/login/loginpage/bloc/login_bloc.dart @@ -12,10 +12,10 @@ part 'login_event.dart'; part 'login_state.dart'; enum LoginType { - ///密码登陆 + ///密码登录 pwd, - ///验证码登陆 + ///验证码登录 sms, } @@ -28,7 +28,7 @@ class LoginBloc extends Bloc { ///是否阅读协议 bool _agreement = false; - ///登陆方式 + ///登录方式 //LoginType _loginType = LoginType.sms; bool _isSmsLoginType = true; @@ -54,7 +54,7 @@ class LoginBloc extends Bloc { on(_requestSmsCodeApi); } - ///请求登陆 + ///请求登录 void _requestLoginApi(RequestLoginEvent event, Emitter emitter) async { var phoneNumber = phoneNumController.text; if (phoneNumber.isEmpty) { @@ -75,7 +75,7 @@ class LoginBloc extends Bloc { await loading(() async { var user = await UserDao.login(phoneNumber, type, checkKey, checkNumber); if (kDebugMode) { - print('登陆 UserEntity=$user'); + print('登录 UserEntity=$user'); } emitter.call(LoginResultChangeState()); }); @@ -83,7 +83,7 @@ class LoginBloc extends Bloc { if (kDebugMode) { print(e); } - showToast('登陆失败${(e as ApiException?)?.message?.prefixColon}'); + showToast('登录失败${(e as ApiException?)?.message?.prefixColon}'); } } @@ -107,7 +107,7 @@ class LoginBloc extends Bloc { } } - ///切换登陆方式 + ///切换登录方式 void _changeLoginType(ChangeLoginTypeEvent event, Emitter emitter) async { if (_isSmsLoginType) { _isSmsLoginType = false; @@ -158,7 +158,7 @@ class LoginBloc extends Bloc { } } - ///登陆状态判断 + ///登录状态判断 bool _loginStateChange() { if (_agreement) { if (phoneNumController.text.isNotEmpty && checkNumController.text.isNotEmpty) { diff --git a/lib/pages/login/loginpage/bloc/login_event.dart b/lib/pages/login/loginpage/bloc/login_event.dart index c68ec2a..8f7bbfc 100644 --- a/lib/pages/login/loginpage/bloc/login_event.dart +++ b/lib/pages/login/loginpage/bloc/login_event.dart @@ -2,7 +2,7 @@ part of 'login_bloc.dart'; @immutable abstract class LoginEvent {} -///切换登陆方式 +///切换登录方式 class ChangeLoginTypeEvent extends LoginEvent {} ///输入手机号 class PhoneNumChangeEvent extends LoginEvent {} @@ -12,5 +12,5 @@ class CheckFieldChangeEvent extends LoginEvent {} class AgreementChangeEvent extends LoginEvent {} ///发送验证码 class RequestSmsCodeEvent extends LoginEvent {} -///请求登陆 +///请求登录 class RequestLoginEvent extends LoginEvent {} diff --git a/lib/pages/login/loginpage/bloc/login_state.dart b/lib/pages/login/loginpage/bloc/login_state.dart index e957e1b..705432b 100644 --- a/lib/pages/login/loginpage/bloc/login_state.dart +++ b/lib/pages/login/loginpage/bloc/login_state.dart @@ -5,10 +5,10 @@ abstract class LoginState {} class LoginInitial extends LoginState {} -///登陆按钮状态 +///登录按钮状态 class LoginEventChangeState extends LoginState {} -///切换登陆方式 +///切换登录方式 class LoginTypeChangeState extends LoginState {} ///发送验证码按钮状态 @@ -20,5 +20,5 @@ class AgreementTypeChangeState extends LoginState {} ///获取验证码 class SmsCodeRequestState extends LoginState {} -///登陆请求结果 +///登录请求结果 class LoginResultChangeState extends LoginState {} diff --git a/lib/pages/login/loginpage/login_page.dart b/lib/pages/login/loginpage/login_page.dart index 2e1b776..e7eecaa 100644 --- a/lib/pages/login/loginpage/login_page.dart +++ b/lib/pages/login/loginpage/login_page.dart @@ -57,7 +57,7 @@ class _LoginPageView extends StatelessWidget { ), padding: EdgeInsets.symmetric(horizontal: 18.w, vertical: 5.h), child: Text( - bloc.isSmsLoginType ? '密码登陆' : '验证码密码', + bloc.isSmsLoginType ? '密码登录' : '验证码登录', style: TextStyle(fontSize: 16.sp), ), ), diff --git a/lib/pages/login/loginpage/time_widget.dart b/lib/pages/login/loginpage/time_widget.dart index 019dd41..e1f45fc 100644 --- a/lib/pages/login/loginpage/time_widget.dart +++ b/lib/pages/login/loginpage/time_widget.dart @@ -38,7 +38,7 @@ class TimerWidgetView extends StatelessWidget { Widget build(BuildContext context) { return MultiBlocListener( listeners: [ - if(pageType == 0) ...[//登陆 + if(pageType == 0) ...[//登录 BlocListener( listener: (context, s) { if (s is FinishedState) { diff --git a/lib/pages/login/setpwd/set_pwd_page.dart b/lib/pages/login/setpwd/set_pwd_page.dart index c81dd51..3279652 100644 --- a/lib/pages/login/setpwd/set_pwd_page.dart +++ b/lib/pages/login/setpwd/set_pwd_page.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -34,12 +35,15 @@ class SetPassWordPage extends StatelessWidget { ); } + /// [pageType]= [resetPwd] or [changePwd] 时,必传手机号和验证码 static push(BuildContext context, SetPwdPageType pageType, {String? phoneNum, String? smsCode}) { - Navigator.of(context).pushNamed(AppRouteName.setPwd, arguments: { - 'pageType': pageType, - 'phoneNum': phoneNum, - 'smsCode': smsCode, - }); + Navigator.of(context).push(CupertinoPageRoute(builder: (context) { + return SetPassWordPage( + phoneNum: phoneNum, + smsCode: smsCode, + pageType: pageType, + ); + })); } } diff --git a/lib/pages/user/bloc/user_bloc.dart b/lib/pages/user/bloc/user_bloc.dart index 5050068..fcfe8af 100644 --- a/lib/pages/user/bloc/user_bloc.dart +++ b/lib/pages/user/bloc/user_bloc.dart @@ -1,16 +1,47 @@ +import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:wow_english/common/core/user_util.dart'; import 'package:wow_english/common/request/dao/user_dao.dart'; +import 'package:wow_english/models/user_entity.dart'; +import 'package:wow_english/pages/user/modify/modify_user_information_page.dart'; +import 'package:wow_english/utils/log_util.dart'; part 'user_event.dart'; part 'user_state.dart'; class UserBloc extends Bloc { + final TextEditingController modifyTextController = TextEditingController(); + UserBloc() : super(UserInitial()) { on(_logout); + on(_updateUser); } void _logout(UserLogout event, Emitter emitter) async { - print('UserBloc._test, event: $event, emitter: $emitter'); await UserDao.logout(); } + + void _updateUser(UserUpdate event, Emitter emitter) async { + Log.d('_updateUser, event: ${event.type}, emitter.isDone: ${emitter.isDone}, text=${modifyTextController.text}'); + UserEntity user = UserUtil.getUser()!; + switch (event.type) { + case ModifyUserInformationType.avatar: + break; + case ModifyUserInformationType.name: + String name = modifyTextController.text; + user.name = name; + try { + await UserDao.updateUserInfo(user); + emitter(UserInfoUpdated()); + } catch (e) { + print(e); + } + break; + case ModifyUserInformationType.age: + String age = modifyTextController.text; + break; + case ModifyUserInformationType.gender: + break; + } + } } diff --git a/lib/pages/user/bloc/user_event.dart b/lib/pages/user/bloc/user_event.dart index 4f84932..87d5b3f 100644 --- a/lib/pages/user/bloc/user_event.dart +++ b/lib/pages/user/bloc/user_event.dart @@ -5,3 +5,9 @@ sealed class UserEvent {} class UserStarted extends UserEvent {} class UserLogout extends UserEvent {} + +class UserUpdate extends UserEvent { + final ModifyUserInformationType type; + + UserUpdate(this.type); +} diff --git a/lib/pages/user/information/user_information_page.dart b/lib/pages/user/information/user_information_page.dart index 88e9496..dccb2dd 100644 --- a/lib/pages/user/information/user_information_page.dart +++ b/lib/pages/user/information/user_information_page.dart @@ -7,6 +7,7 @@ import 'package:wow_english/common/widgets/we_app_bar.dart'; import 'package:wow_english/models/user_entity.dart'; import 'package:wow_english/pages/user/bloc/user_bloc.dart'; import 'package:wow_english/utils/image_util.dart'; +import 'package:wow_english/utils/log_util.dart'; import '../modify/modify_user_information_page.dart'; @@ -28,9 +29,11 @@ class _UserInformationView extends StatelessWidget { @override Widget build(BuildContext context) { return BlocListener( - listener: (context, state) {}, + listener: (context, state) { + Log.d('UserInformationPage: $state'); + }, child: BlocBuilder(builder: (context, state) { - return const _UserInformationContentView(); + return _UserInformationContentView(); }), ); } @@ -39,8 +42,9 @@ class _UserInformationView extends StatelessWidget { class _UserInformationContentView extends StatelessWidget { const _UserInformationContentView({super.key}); - void _openModifyPage(ModifyUserInformationType type) { - print('_openModifyPage($type)'); + void _openModifyPage(BuildContext context, ModifyUserInformationType type) { + Log.d('_openModifyPage($type)'); + ModifyUserInformationPage.push(context, type); } @override @@ -76,7 +80,7 @@ class _UserInformationContentView extends StatelessWidget { fontSize: 21.sp, ), ), - onTap: () => _openModifyPage(ModifyUserInformationType.name)), + onTap: () => _openModifyPage(context, ModifyUserInformationType.name)), 11.verticalSpace, _buildContentRow( '年龄', diff --git a/lib/pages/user/modify/modify_user_information_page.dart b/lib/pages/user/modify/modify_user_information_page.dart index dbdb6e6..b495ff8 100644 --- a/lib/pages/user/modify/modify_user_information_page.dart +++ b/lib/pages/user/modify/modify_user_information_page.dart @@ -1,10 +1,18 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:wow_english/common/core/assets_const.dart'; +import 'package:wow_english/common/widgets/textfield_customer_widget.dart'; import 'package:wow_english/common/widgets/we_app_bar.dart'; import 'package:wow_english/pages/user/bloc/user_bloc.dart'; enum ModifyUserInformationType { - avatar('修改头像'), name('修改名字'), age('修改年龄'), gender('修改性别'); + avatar('头像'), + name('名字'), + age('年龄'), + gender('性别'); const ModifyUserInformationType(this.title); @@ -16,6 +24,12 @@ class ModifyUserInformationPage extends StatelessWidget { const ModifyUserInformationPage({super.key, required this.type}); + static push(BuildContext context, ModifyUserInformationType type) { + Navigator.of(context).push(CupertinoPageRoute(builder: (context) { + return ModifyUserInformationPage(type: type); + })); + } + @override Widget build(BuildContext context) { return BlocProvider( @@ -23,7 +37,72 @@ class ModifyUserInformationPage extends StatelessWidget { child: Scaffold( backgroundColor: Colors.white, appBar: WEAppBar( - titleText: type.title, + titleText: '修改${type.title}', + ), + body: BlocListener( + listener: (context, state) { + }, + child: BlocBuilder(builder: (context, state) { + //final bloc = context.read(); + // 区别是什么? + //BlocProvider.of(context); + return SingleChildScrollView( + child: Column( + children: [ + Padding( + padding: EdgeInsets.only(left: 65.w, right: 55.w, top: 38.h), + child: Row( + children: [ + Text( + type.title, + style: TextStyle( + fontWeight: FontWeight.w700, + color: const Color(0xFF333333), + fontSize: 21.sp, + ), + ), + 20.horizontalSpace, + TextFieldCustomerWidget( + height: 65.h, + width: 222.w, + bgImageName: 'Input_layer_up', + textInputType: TextInputType.name, + inputFormatters: [ + LengthLimitingTextInputFormatter(12), + FilteringTextInputFormatter.deny(RegExp('[ ]')), + ], + //controller: bloc.modifyTextController, + /*onChangeValue: (String value) { + bloc.add(CodeNumberChangeEvent()); + },*/ + ), + Expanded( + child: Container(), + ), + // 按钮 + GestureDetector( + onTap: () { + // 更新type类型的字段 + //bloc.add(UserUpdate(type)); + }, + child: Container( + alignment: Alignment.center, + width: 90.w, + height: 44.h, + decoration: BoxDecoration( + image: DecorationImage(image: AssetImage(AssetsConst.bgButtonBlue), fit: BoxFit.fill), + ), + child: Text( + '确定', + style: TextStyle(fontSize: 17.sp, color: Colors.white), + ), + ), + ) + ], + )), + ], + )); + }), ), ), ); diff --git a/lib/pages/user/user_page.dart b/lib/pages/user/user_page.dart index 839de8d..b0f6be6 100644 --- a/lib/pages/user/user_page.dart +++ b/lib/pages/user/user_page.dart @@ -63,12 +63,15 @@ class _UserView extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - // banner - Offstage( - child: Container( - child: Image.asset(bannerUrl), constraints: BoxConstraints(maxHeight: 196.h)), - ), - 30.verticalSpace, + // todo banner,暂时没有接口获取banner URL + /*Offstage( + child: Column( + children: [ + Container(child: Image.asset(bannerUrl), constraints: BoxConstraints(maxHeight: 196.h)), + 30.verticalSpace, + ], + ), + ),*/ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -170,7 +173,7 @@ class _UserView extends StatelessWidget { )), 30.verticalSpace, OutlinedButton( - onPressed: () => {userBloc.add(UserLogout())}, + onPressed: () => userBloc.add(UserLogout()), style: ButtonStyle( side: MaterialStateProperty.all(BorderSide(color: const Color(0xFF140C10), width: 1.5)), shape: MaterialStateProperty.all( @@ -179,7 +182,7 @@ class _UserView extends StatelessWidget { backgroundColor: MaterialStateProperty.all(Color(0xFFFBB621)), ), child: Text( - "退出登陆", + "退出登录", style: TextStyle( //fontWeight: FontWeight.w600, color: Colors.white, diff --git a/lib/route/route.dart b/lib/route/route.dart index c2071fd..15a170b 100644 --- a/lib/route/route.dart +++ b/lib/route/route.dart @@ -16,6 +16,7 @@ import 'package:wow_english/pages/shop/exchangelist/exchange_lesson_list_page.da import 'package:wow_english/pages/shop/home/shop_home_page.dart'; import 'package:wow_english/pages/tab/tab_page.dart'; import 'package:wow_english/pages/user/information/user_information_page.dart'; +import 'package:wow_english/pages/user/modify/modify_user_information_page.dart'; import 'package:wow_english/pages/user/user_page.dart'; import 'package:wow_english/pages/video/lookvideo/look_video_page.dart'; @@ -27,8 +28,8 @@ class AppRouteName { static const String home = 'home'; static const String fogPwd = 'fogPwd'; - /// 设置密码,修改密码;不要自己调用,使用[SetPassWordPage.push]方法 - static const String setPwd = 'setPwd'; + /// 设置密码,修改密码;不要自己调用,使用[SetPassWordPage.push]方法,隐藏这种实现 + //static const String setPwd = 'setPwd'; static const String webView = 'webView'; static const String lesson = 'lesson'; static const String listen = 'listen'; @@ -41,6 +42,9 @@ class AppRouteName { /// 用户详细信息页 static const String userInformation = 'userInformation'; + + /// 用户修改信息页,不要自己调用,使用[ModifyUserInformationPage.push]方法,隐藏这种实现 + //static const String userModifyInformation = 'userModifyInformation'; ///看视频 static const String lookVideo = 'lookVideo'; ///绘本 @@ -95,6 +99,14 @@ class AppRouter { return CupertinoPageRoute(builder: (_) => const UserPage()); case AppRouteName.userInformation: return CupertinoPageRoute(builder: (_) => const UserInformationPage()); + /*case AppRouteName.userModifyInformation: + return CupertinoPageRoute(builder: (_) { + ModifyUserInformationType argument = ModifyUserInformationType.name; + if (settings.arguments != null) { + argument = settings.arguments as ModifyUserInformationType; + } + return ModifyUserInformationPage(type: argument); + });*/ case AppRouteName.topicPic: var courseLessonId = ''; if (settings.arguments != null) { @@ -109,7 +121,7 @@ class AppRouter { videoUrl: videoUrl, typeTitle: title, )); - case AppRouteName.setPwd: + /*case AppRouteName.setPwd: var map = settings.arguments as Map; final phoneNum = map['phoneNumber'] as String?; final smsCode = map['smsCode'] as String?; @@ -119,7 +131,7 @@ class AppRouter { phoneNum: phoneNum, smsCode: smsCode, pageType: pageType, - )); + ));*/ case AppRouteName.webView: final urlStr = (settings.arguments as Map)['urlStr'] as String; final webViewTitle = (settings.arguments as Map)['webViewTitle'] as String; @@ -162,4 +174,3 @@ void pushNamedAndRemoveUntil(String routeName,RoutePredicate predicate, {Object? void popPage() { Navigator.pop(AppRouter.context); } - diff --git a/lib/utils/text_input_formatter.dart b/lib/utils/text_input_formatter.dart new file mode 100644 index 0000000..4bff6aa --- /dev/null +++ b/lib/utils/text_input_formatter.dart @@ -0,0 +1,23 @@ +import 'package:flutter/services.dart'; + +class RegexFormatter extends TextInputFormatter { + RegexFormatter({required this.regex}); + + /// 需要匹配的正则表达 + final String regex; + + @override + TextEditingValue formatEditUpdate( + TextEditingValue oldValue, + TextEditingValue newValue, + ) { + if (newValue.text.isEmpty) { + return TextEditingValue.empty; + } + + if (!RegExp(regex).hasMatch(newValue.text)) { + return oldValue; + } + return newValue; + } +} -- libgit2 0.22.2