refactor(group): 优化群昵称修改流程的用户体验
This commit is contained in:
parent
d4ffbf2165
commit
498bdb7f5d
|
|
@ -22,6 +22,8 @@ class GroupProfileNameCard extends StatefulWidget {
|
||||||
class GroupProfileNameCardState extends TIMUIKitState<GroupProfileNameCard>{
|
class GroupProfileNameCardState extends TIMUIKitState<GroupProfileNameCard>{
|
||||||
final TextEditingController controller = TextEditingController();
|
final TextEditingController controller = TextEditingController();
|
||||||
String? nameCard;
|
String? nameCard;
|
||||||
|
String? validationMessage;
|
||||||
|
bool isValid = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||||
|
|
@ -35,6 +37,11 @@ class GroupProfileNameCardState extends TIMUIKitState<GroupProfileNameCard>{
|
||||||
nameCard = model.getSelfNameCard();
|
nameCard = model.getSelfNameCard();
|
||||||
controller.text = nameCard ?? "";
|
controller.text = nameCard ?? "";
|
||||||
|
|
||||||
|
// 初始化验证状态
|
||||||
|
if (nameCard != null && nameCard!.isNotEmpty) {
|
||||||
|
_validateNameCardRealTime(nameCard!);
|
||||||
|
}
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
|
@ -46,26 +53,7 @@ class GroupProfileNameCardState extends TIMUIKitState<GroupProfileNameCard>{
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
if (!isDesktopScreen) {
|
if (!isDesktopScreen) {
|
||||||
TextInputBottomSheet.showTextInputBottomSheet(
|
_showNameCardInputBottomSheet(context, theme, model);
|
||||||
context: context,
|
|
||||||
title: TIM_t("修改我的群昵称"),
|
|
||||||
tips: TIM_t("仅限中文、字母、数字和下划线,2-20个字"),
|
|
||||||
onSubmitted: (String nameCard) async {
|
|
||||||
final text = nameCard.trim();
|
|
||||||
// 验证输入格式
|
|
||||||
if (_validateNameCard(text)) {
|
|
||||||
model.setNameCard(text);
|
|
||||||
} else {
|
|
||||||
// 显示错误提示
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text(TIM_t("仅限中文、字母、数字和下划线,2-20个字")),
|
|
||||||
backgroundColor: Colors.red,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
theme: theme);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|
@ -103,8 +91,11 @@ class GroupProfileNameCardState extends TIMUIKitState<GroupProfileNameCard>{
|
||||||
),
|
),
|
||||||
if (isDesktopScreen)
|
if (isDesktopScreen)
|
||||||
Text(
|
Text(
|
||||||
TIM_t("仅限中文、字母、数字和下划线,2-20个字"),
|
validationMessage ?? TIM_t("仅限中文、字母、数字和下划线,2-16个字"),
|
||||||
style: TextStyle(color: theme.weakTextColor, fontSize: 12),
|
style: TextStyle(
|
||||||
|
color: isValid ? theme.weakTextColor : Colors.red,
|
||||||
|
fontSize: 12
|
||||||
|
),
|
||||||
),
|
),
|
||||||
if (isDesktopScreen)
|
if (isDesktopScreen)
|
||||||
Container(
|
Container(
|
||||||
|
|
@ -114,6 +105,12 @@ class GroupProfileNameCardState extends TIMUIKitState<GroupProfileNameCard>{
|
||||||
minLines: 1,
|
minLines: 1,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
|
onChanged: (text) {
|
||||||
|
final trimmedText = text.trim();
|
||||||
|
setState(() {
|
||||||
|
_validateNameCardRealTime(trimmedText);
|
||||||
|
});
|
||||||
|
},
|
||||||
onSubmitted: (text) {
|
onSubmitted: (text) {
|
||||||
final trimmedText = text.trim();
|
final trimmedText = text.trim();
|
||||||
// 验证输入格式
|
// 验证输入格式
|
||||||
|
|
@ -123,7 +120,7 @@ class GroupProfileNameCardState extends TIMUIKitState<GroupProfileNameCard>{
|
||||||
// 显示错误提示
|
// 显示错误提示
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(TIM_t("仅限中文、字母、数字和下划线,2-20个字")),
|
content: Text(TIM_t("仅限中文、字母、数字和下划线,2-16个字")),
|
||||||
backgroundColor: Colors.red,
|
backgroundColor: Colors.red,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -167,14 +164,14 @@ class GroupProfileNameCardState extends TIMUIKitState<GroupProfileNameCard>{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 验证群昵称格式
|
/// 验证群昵称格式
|
||||||
/// 仅限中文、字母、数字和下划线,2-20个字
|
/// 仅限中文、字母、数字和下划线,2-16个字
|
||||||
bool _validateNameCard(String text) {
|
bool _validateNameCard(String text) {
|
||||||
if (text.isEmpty) {
|
if (text.isEmpty) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查长度:2-20个字符
|
// 检查长度:2-16个字符
|
||||||
if (text.length < 2 || text.length > 20) {
|
if (text.length < 2 || text.length > 16) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -182,4 +179,251 @@ class GroupProfileNameCardState extends TIMUIKitState<GroupProfileNameCard>{
|
||||||
final RegExp regex = RegExp(r'^[\u4e00-\u9fa5a-zA-Z0-9_]+$');
|
final RegExp regex = RegExp(r'^[\u4e00-\u9fa5a-zA-Z0-9_]+$');
|
||||||
return regex.hasMatch(text);
|
return regex.hasMatch(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 实时验证群昵称格式并设置提示信息
|
||||||
|
void _validateNameCardRealTime(String text) {
|
||||||
|
if (text.isEmpty) {
|
||||||
|
isValid = false;
|
||||||
|
validationMessage = TIM_t("昵称不能为空");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.length < 2) {
|
||||||
|
isValid = false;
|
||||||
|
validationMessage = TIM_t("昵称至少需要2个字符");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.length > 16) {
|
||||||
|
isValid = false;
|
||||||
|
validationMessage = TIM_t("昵称不能超过16个字符");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查字符类型:仅限中文、字母、数字和下划线
|
||||||
|
final RegExp regex = RegExp(r'^[\u4e00-\u9fa5a-zA-Z0-9_]+$');
|
||||||
|
if (!regex.hasMatch(text)) {
|
||||||
|
isValid = false;
|
||||||
|
validationMessage = TIM_t("仅限中文、字母、数字和下划线");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证通过
|
||||||
|
isValid = true;
|
||||||
|
validationMessage = TIM_t("昵称格式正确");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 显示带有实时验证的群昵称输入底部弹窗
|
||||||
|
void _showNameCardInputBottomSheet(BuildContext context, TUITheme theme, model) {
|
||||||
|
TextEditingController modalController = TextEditingController();
|
||||||
|
modalController.text = nameCard ?? "";
|
||||||
|
|
||||||
|
showModalBottomSheet(
|
||||||
|
isScrollControlled: true,
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return _NameCardInputWidget(
|
||||||
|
controller: modalController,
|
||||||
|
theme: theme,
|
||||||
|
model: model,
|
||||||
|
initialNameCard: nameCard,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _NameCardInputWidget extends StatefulWidget {
|
||||||
|
final TextEditingController controller;
|
||||||
|
final TUITheme theme;
|
||||||
|
final model;
|
||||||
|
final String? initialNameCard;
|
||||||
|
|
||||||
|
const _NameCardInputWidget({
|
||||||
|
required this.controller,
|
||||||
|
required this.theme,
|
||||||
|
required this.model,
|
||||||
|
this.initialNameCard,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_NameCardInputWidgetState createState() => _NameCardInputWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NameCardInputWidgetState extends State<_NameCardInputWidget> {
|
||||||
|
String? modalValidationMessage;
|
||||||
|
bool modalIsValid = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
// 初始化验证状态
|
||||||
|
if (widget.initialNameCard != null && widget.initialNameCard!.isNotEmpty) {
|
||||||
|
_validateModalInput(widget.initialNameCard!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _validateModalInput(String text) {
|
||||||
|
setState(() {
|
||||||
|
if (text.isEmpty) {
|
||||||
|
modalIsValid = false;
|
||||||
|
modalValidationMessage = TIM_t("昵称不能为空");
|
||||||
|
} else if (text.length < 2) {
|
||||||
|
modalIsValid = false;
|
||||||
|
modalValidationMessage = TIM_t("昵称至少需要2个字符");
|
||||||
|
} else if (text.length > 16) {
|
||||||
|
modalIsValid = false;
|
||||||
|
modalValidationMessage = TIM_t("昵称不能超过16个字符");
|
||||||
|
} else {
|
||||||
|
final RegExp regex = RegExp(r'^[\u4e00-\u9fa5a-zA-Z0-9_]+$');
|
||||||
|
if (!regex.hasMatch(text)) {
|
||||||
|
modalIsValid = false;
|
||||||
|
modalValidationMessage = TIM_t("仅限中文、字母、数字和下划线");
|
||||||
|
} else {
|
||||||
|
modalIsValid = true;
|
||||||
|
modalValidationMessage = TIM_t("昵称格式正确");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _validateNameCard(String text) {
|
||||||
|
if (text.isEmpty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查长度:2-16个字符
|
||||||
|
if (text.length < 2 || text.length > 16) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查字符类型:仅限中文、字母、数字和下划线
|
||||||
|
final RegExp regex = RegExp(r'^[\u4e00-\u9fa5a-zA-Z0-9_]+$');
|
||||||
|
return regex.hasMatch(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
top: 16,
|
||||||
|
left: 16,
|
||||||
|
right: 16,
|
||||||
|
bottom: MediaQuery.of(context).viewInsets.bottom + 30,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 16),
|
||||||
|
child: Text(
|
||||||
|
TIM_t("修改我的群昵称"),
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(height: 2, color: widget.theme.weakDividerColor),
|
||||||
|
TextField(
|
||||||
|
controller: widget.controller,
|
||||||
|
autofocus: true,
|
||||||
|
onChanged: (text) {
|
||||||
|
final trimmedText = text.trim();
|
||||||
|
_validateModalInput(trimmedText);
|
||||||
|
},
|
||||||
|
onSubmitted: (text) {
|
||||||
|
final trimmedText = text.trim();
|
||||||
|
if (_validateNameCard(trimmedText)) {
|
||||||
|
widget.model.setNameCard(trimmedText);
|
||||||
|
Navigator.pop(context);
|
||||||
|
} else {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(TIM_t("仅限中文、字母、数字和下划线,2-16个字")),
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5),
|
||||||
|
height: 40,
|
||||||
|
child: Text(
|
||||||
|
modalValidationMessage ?? TIM_t("仅限中文、字母、数字和下划线,2-16个字"),
|
||||||
|
style: TextStyle(
|
||||||
|
color: modalIsValid ? Colors.grey : Colors.red,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
margin: const EdgeInsets.only(right: 10),
|
||||||
|
child: ElevatedButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: MaterialStateProperty.all(widget.theme.wideBackgroundColor),
|
||||||
|
shape: MaterialStateProperty.all(
|
||||||
|
RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
TIM_t("取消"),
|
||||||
|
style: TextStyle(color: widget.theme.darkTextColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: ElevatedButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
shape: MaterialStateProperty.all(
|
||||||
|
RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
final text = widget.controller.text.trim();
|
||||||
|
if (_validateNameCard(text)) {
|
||||||
|
widget.model.setNameCard(text);
|
||||||
|
Navigator.pop(context);
|
||||||
|
} else {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text(TIM_t("输入错误")),
|
||||||
|
content: Text(TIM_t("仅限中文、字母、数字和下划线,2-16个字")),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Text(TIM_t("确定")),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(TIM_t("确定")),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue