fix(client): 修复登录页候选词点击后未填入输入框
点击候选词时 pointer-down 先触发 TextField 失焦,focus listener 立即移除 Overlay,导致 InkWell.onTap (pointer-up) 执行时 widget 已不存在。改为失焦后延迟 150ms 关闭下拉框,给 onTap 足够时间填值。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -45,7 +45,8 @@ class _LoginScreenState extends ConsumerState<LoginScreen> {
|
||||
_openDropdown(_hotelLayerLink, _hotelCodeHistory, _shopCodeCtrl,
|
||||
isHotel: true);
|
||||
} else if (!_hotelCodeFocus.hasFocus) {
|
||||
_closeHotel();
|
||||
// 延迟关闭:让候选词的 onTap(pointer-up)先执行,再关闭下拉框
|
||||
Future.delayed(const Duration(milliseconds: 150), _closeHotel);
|
||||
}
|
||||
});
|
||||
_usernameFocus.addListener(() {
|
||||
@@ -53,7 +54,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen> {
|
||||
_openDropdown(_usernameLayerLink, _usernameHistory, _usernameCtrl,
|
||||
isHotel: false);
|
||||
} else if (!_usernameFocus.hasFocus) {
|
||||
_closeUsername();
|
||||
Future.delayed(const Duration(milliseconds: 150), _closeUsername);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -117,29 +118,10 @@ class _LoginScreenState extends ConsumerState<LoginScreen> {
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: items.length,
|
||||
separatorBuilder: (_, __) => const Divider(height: 1),
|
||||
itemBuilder: (_, i) => GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTapDown: (_) {
|
||||
ctrl.text = items[i];
|
||||
ctrl.selection = TextSelection.collapsed(
|
||||
offset: items[i].length);
|
||||
if (isHotel) { _closeHotel(); } else { _closeUsername(); }
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12, vertical: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.history,
|
||||
size: 14, color: AppTheme.textSecondary),
|
||||
const SizedBox(width: 8),
|
||||
Text(items[i],
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppTheme.textPrimary)),
|
||||
],
|
||||
),
|
||||
),
|
||||
itemBuilder: (_, i) => _HistoryMenuItem(
|
||||
value: items[i],
|
||||
onSelected: () =>
|
||||
_selectHistoryItem(items[i], ctrl, isHotel: isHotel),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -158,6 +140,27 @@ class _LoginScreenState extends ConsumerState<LoginScreen> {
|
||||
setState(() {}); // refresh arrow icon
|
||||
}
|
||||
|
||||
void _selectHistoryItem(
|
||||
String value,
|
||||
TextEditingController ctrl, {
|
||||
required bool isHotel,
|
||||
}) {
|
||||
ctrl.value = TextEditingValue(
|
||||
text: value,
|
||||
selection: TextSelection.collapsed(offset: value.length),
|
||||
);
|
||||
|
||||
if (isHotel) {
|
||||
_closeHotel();
|
||||
_hotelCodeFocus.unfocus();
|
||||
} else {
|
||||
_closeUsername();
|
||||
_usernameFocus.unfocus();
|
||||
}
|
||||
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void _closeHotel() {
|
||||
_hotelEntry?.remove();
|
||||
_hotelEntry = null;
|
||||
@@ -472,3 +475,52 @@ class _BackgroundPainter extends CustomPainter {
|
||||
@override
|
||||
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
||||
}
|
||||
|
||||
class _HistoryMenuItem extends StatefulWidget {
|
||||
final String value;
|
||||
final VoidCallback onSelected;
|
||||
|
||||
const _HistoryMenuItem({
|
||||
required this.value,
|
||||
required this.onSelected,
|
||||
});
|
||||
|
||||
@override
|
||||
State<_HistoryMenuItem> createState() => _HistoryMenuItemState();
|
||||
}
|
||||
|
||||
class _HistoryMenuItemState extends State<_HistoryMenuItem> {
|
||||
bool _hovered = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MouseRegion(
|
||||
onEnter: (_) => setState(() => _hovered = true),
|
||||
onExit: (_) => setState(() => _hovered = false),
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: Material(
|
||||
color: _hovered ? const Color(0xFFF0F4FF) : Colors.white,
|
||||
child: InkWell(
|
||||
onTap: widget.onSelected,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.history,
|
||||
size: 14, color: AppTheme.textSecondary),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
widget.value,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppTheme.textPrimary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user