Skip to content

fix:taping on text field margin does'nt hide the keyboard #337

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
326 changes: 168 additions & 158 deletions lib/src/widgets/chatui_textfield.dart
Original file line number Diff line number Diff line change
Expand Up @@ -131,177 +131,187 @@ class _ChatUITextFieldState extends State<ChatUITextField> {
@override
Widget build(BuildContext context) {
final outlineBorder = _outLineBorder;
return Container(
padding:
textFieldConfig?.padding ?? const EdgeInsets.symmetric(horizontal: 6),
margin: textFieldConfig?.margin,
decoration: BoxDecoration(
borderRadius: textFieldConfig?.borderRadius ??
BorderRadius.circular(textFieldBorderRadius),
color: sendMessageConfig?.textFieldBackgroundColor ?? Colors.white,
),
child: ValueListenableBuilder<bool>(
valueListenable: isRecording,
builder: (_, isRecordingValue, child) {
return Row(
children: [
if (isRecordingValue && controller != null && !kIsWeb)
Expanded(
child: AudioWaveforms(
size: const Size(double.maxFinite, 50),
recorderController: controller!,
margin: voiceRecordingConfig?.margin,
padding: voiceRecordingConfig?.padding ??
EdgeInsets.symmetric(
horizontal: cancelRecordConfiguration == null ? 8 : 5,
),
decoration: voiceRecordingConfig?.decoration ??
BoxDecoration(
color: voiceRecordingConfig?.backgroundColor,
borderRadius: BorderRadius.circular(12.0),
),
waveStyle: voiceRecordingConfig?.waveStyle ??
WaveStyle(
extendWaveform: true,
showMiddleLine: false,
waveColor:
voiceRecordingConfig?.waveStyle?.waveColor ??
Colors.black,
),
),
)
else
Expanded(
child: TextField(
focusNode: widget.focusNode,
controller: widget.textEditingController,
style: textFieldConfig?.textStyle ??
const TextStyle(color: Colors.white),
maxLines: textFieldConfig?.maxLines ?? 5,
minLines: textFieldConfig?.minLines ?? 1,
keyboardType: textFieldConfig?.textInputType,
inputFormatters: textFieldConfig?.inputFormatters,
onChanged: _onChanged,
enabled: textFieldConfig?.enabled,
textCapitalization: textFieldConfig?.textCapitalization ??
TextCapitalization.sentences,
decoration: InputDecoration(
hintText:
textFieldConfig?.hintText ?? PackageStrings.message,
fillColor: sendMessageConfig?.textFieldBackgroundColor ??
Colors.white,
filled: true,
hintStyle: textFieldConfig?.hintStyle ??
TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
color: Colors.grey.shade600,
letterSpacing: 0.25,
return GestureDetector(
Copy link
Preview

Copilot AI Jun 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Wrapping the entire text field container with GestureDetector and HitTestBehavior.opaque may intercept taps intended for the TextField, causing focus to be lost immediately when interacting with the field. Consider limiting the tap detection to only the margin area or placing the detector at a higher level (e.g., Scaffold) to avoid interfering with child input.

Copilot uses AI. Check for mistakes.

behavior: HitTestBehavior.opaque,
onTap: () {
// Unfocus the text field when tapping outside its area.
FocusManager.instance.primaryFocus?.unfocus();
},
child: Container(
padding: textFieldConfig?.padding ??
const EdgeInsets.symmetric(horizontal: 6),
margin: textFieldConfig?.margin,
decoration: BoxDecoration(
borderRadius: textFieldConfig?.borderRadius ??
BorderRadius.circular(textFieldBorderRadius),
color: sendMessageConfig?.textFieldBackgroundColor ?? Colors.white,
),
child: ValueListenableBuilder<bool>(
valueListenable: isRecording,
builder: (_, isRecordingValue, child) {
return Row(
children: [
if (isRecordingValue && controller != null && !kIsWeb)
Expanded(
child: AudioWaveforms(
size: const Size(double.maxFinite, 50),
recorderController: controller!,
margin: voiceRecordingConfig?.margin,
padding: voiceRecordingConfig?.padding ??
EdgeInsets.symmetric(
horizontal:
cancelRecordConfiguration == null ? 8 : 5,
),
decoration: voiceRecordingConfig?.decoration ??
BoxDecoration(
color: voiceRecordingConfig?.backgroundColor,
borderRadius: BorderRadius.circular(12.0),
),
contentPadding: textFieldConfig?.contentPadding ??
const EdgeInsets.symmetric(horizontal: 6),
border: outlineBorder,
focusedBorder: outlineBorder,
enabledBorder: outlineBorder,
disabledBorder: outlineBorder,
waveStyle: voiceRecordingConfig?.waveStyle ??
WaveStyle(
extendWaveform: true,
showMiddleLine: false,
waveColor:
voiceRecordingConfig?.waveStyle?.waveColor ??
Colors.black,
),
),
)
else
Expanded(
child: TextField(
focusNode: widget.focusNode,
controller: widget.textEditingController,
style: textFieldConfig?.textStyle ??
const TextStyle(color: Colors.white),
maxLines: textFieldConfig?.maxLines ?? 5,
minLines: textFieldConfig?.minLines ?? 1,
keyboardType: textFieldConfig?.textInputType,
inputFormatters: textFieldConfig?.inputFormatters,
onChanged: _onChanged,
enabled: textFieldConfig?.enabled,
textCapitalization: textFieldConfig?.textCapitalization ??
TextCapitalization.sentences,
decoration: InputDecoration(
hintText:
textFieldConfig?.hintText ?? PackageStrings.message,
fillColor:
sendMessageConfig?.textFieldBackgroundColor ??
Colors.white,
filled: true,
hintStyle: textFieldConfig?.hintStyle ??
TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
color: Colors.grey.shade600,
letterSpacing: 0.25,
),
contentPadding: textFieldConfig?.contentPadding ??
const EdgeInsets.symmetric(horizontal: 6),
border: outlineBorder,
focusedBorder: outlineBorder,
enabledBorder: outlineBorder,
disabledBorder: outlineBorder,
),
),
),
),
ValueListenableBuilder<String>(
valueListenable: _inputText,
builder: (_, inputTextValue, child) {
if (inputTextValue.isNotEmpty) {
return IconButton(
color: sendMessageConfig?.defaultSendButtonColor ??
Colors.green,
onPressed: (textFieldConfig?.enabled ?? true)
? () {
widget.onPressed();
_inputText.value = '';
}
: null,
icon: sendMessageConfig?.sendButtonIcon ??
const Icon(Icons.send),
);
} else {
return Row(
children: [
if (!isRecordingValue) ...[
if (sendMessageConfig?.enableCameraImagePicker ??
true)
ValueListenableBuilder<String>(
valueListenable: _inputText,
builder: (_, inputTextValue, child) {
if (inputTextValue.isNotEmpty) {
return IconButton(
color: sendMessageConfig?.defaultSendButtonColor ??
Colors.green,
onPressed: (textFieldConfig?.enabled ?? true)
? () {
widget.onPressed();
_inputText.value = '';
}
: null,
icon: sendMessageConfig?.sendButtonIcon ??
const Icon(Icons.send),
);
} else {
return Row(
children: [
if (!isRecordingValue) ...[
if (sendMessageConfig?.enableCameraImagePicker ??
true)
IconButton(
constraints: const BoxConstraints(),
onPressed: (textFieldConfig?.enabled ?? true)
? () => _onIconPressed(
ImageSource.camera,
config: sendMessageConfig
?.imagePickerConfiguration,
)
: null,
icon: imagePickerIconsConfig
?.cameraImagePickerIcon ??
Icon(
Icons.camera_alt_outlined,
color: imagePickerIconsConfig
?.cameraIconColor,
),
),
if (sendMessageConfig?.enableGalleryImagePicker ??
true)
IconButton(
constraints: const BoxConstraints(),
onPressed: (textFieldConfig?.enabled ?? true)
? () => _onIconPressed(
ImageSource.gallery,
config: sendMessageConfig
?.imagePickerConfiguration,
)
: null,
icon: imagePickerIconsConfig
?.galleryImagePickerIcon ??
Icon(
Icons.image,
color: imagePickerIconsConfig
?.galleryIconColor,
),
),
],
if ((sendMessageConfig?.allowRecordingVoice ??
false) &&
!kIsWeb &&
(Platform.isIOS || Platform.isAndroid))
IconButton(
constraints: const BoxConstraints(),
onPressed: (textFieldConfig?.enabled ?? true)
? () => _onIconPressed(
ImageSource.camera,
config: sendMessageConfig
?.imagePickerConfiguration,
)
? _recordOrStop
: null,
icon: imagePickerIconsConfig
?.cameraImagePickerIcon ??
icon: (isRecordingValue
? voiceRecordingConfig?.stopIcon
: voiceRecordingConfig?.micIcon) ??
Icon(
Icons.camera_alt_outlined,
isRecordingValue ? Icons.stop : Icons.mic,
color:
imagePickerIconsConfig?.cameraIconColor,
voiceRecordingConfig?.recorderIconColor,
),
),
if (sendMessageConfig?.enableGalleryImagePicker ??
true)
if (isRecordingValue &&
cancelRecordConfiguration != null)
IconButton(
constraints: const BoxConstraints(),
onPressed: (textFieldConfig?.enabled ?? true)
? () => _onIconPressed(
ImageSource.gallery,
config: sendMessageConfig
?.imagePickerConfiguration,
)
: null,
icon: imagePickerIconsConfig
?.galleryImagePickerIcon ??
Icon(
Icons.image,
color: imagePickerIconsConfig
?.galleryIconColor,
),
onPressed: () {
cancelRecordConfiguration?.onCancel?.call();
_cancelRecording();
},
icon: cancelRecordConfiguration?.icon ??
const Icon(Icons.cancel_outlined),
color: cancelRecordConfiguration?.iconColor ??
voiceRecordingConfig?.recorderIconColor,
),
],
if ((sendMessageConfig?.allowRecordingVoice ?? false) &&
!kIsWeb &&
(Platform.isIOS || Platform.isAndroid))
IconButton(
onPressed: (textFieldConfig?.enabled ?? true)
? _recordOrStop
: null,
icon: (isRecordingValue
? voiceRecordingConfig?.stopIcon
: voiceRecordingConfig?.micIcon) ??
Icon(
isRecordingValue ? Icons.stop : Icons.mic,
color:
voiceRecordingConfig?.recorderIconColor,
),
),
if (isRecordingValue &&
cancelRecordConfiguration != null)
IconButton(
onPressed: () {
cancelRecordConfiguration?.onCancel?.call();
_cancelRecording();
},
icon: cancelRecordConfiguration?.icon ??
const Icon(Icons.cancel_outlined),
color: cancelRecordConfiguration?.iconColor ??
voiceRecordingConfig?.recorderIconColor,
),
],
);
}
},
),
],
);
},
);
}
},
),
],
);
},
),
),
);
}
Expand Down