fix: kanban board card text input inconsistency (#5307)

This commit is contained in:
Richard Shiue 2024-05-10 11:25:22 +08:00 committed by GitHub
parent c732438f8a
commit f47c88b022
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -55,18 +55,26 @@ class _TextCellState extends State<TextCardCell> {
widget.cellContext, widget.cellContext,
).as(), ).as(),
); );
late final TextEditingController _textEditingController = late final TextEditingController _textEditingController;
TextEditingController(text: cellBloc.state.content);
final focusNode = SingleListenerFocusNode(); final focusNode = SingleListenerFocusNode();
bool focusWhenInit = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
focusWhenInit = widget.editableNotifier?.isCellEditing.value ?? false;
if (focusWhenInit) { _textEditingController = TextEditingController(text: cellBloc.state.content)
focusNode.requestFocus(); ..addListener(() {
if (_textEditingController.value.composing.isCollapsed) {
cellBloc
.add(TextCellEvent.updateText(_textEditingController.value.text));
}
});
if (widget.editableNotifier?.isCellEditing.value ?? false) {
WidgetsBinding.instance.addPostFrameCallback((_) {
focusNode.requestFocus();
cellBloc.add(const TextCellEvent.enableEdit(true));
});
} }
// If the focusNode lost its focus, the widget's editableNotifier will // If the focusNode lost its focus, the widget's editableNotifier will
@ -74,7 +82,6 @@ class _TextCellState extends State<TextCardCell> {
// end edit event. // end edit event.
focusNode.addListener(() { focusNode.addListener(() {
if (!focusNode.hasFocus) { if (!focusNode.hasFocus) {
focusWhenInit = false;
widget.editableNotifier?.isCellEditing.value = false; widget.editableNotifier?.isCellEditing.value = false;
cellBloc.add(const TextCellEvent.enableEdit(false)); cellBloc.add(const TextCellEvent.enableEdit(false));
} }
@ -100,45 +107,25 @@ class _TextCellState extends State<TextCardCell> {
@override @override
void didUpdateWidget(covariant oldWidget) { void didUpdateWidget(covariant oldWidget) {
_bindEditableNotifier(); if (oldWidget.editableNotifier != widget.editableNotifier) {
_bindEditableNotifier();
}
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isTitle = cellBloc.cellController.fieldInfo.isPrimary;
return BlocProvider.value( return BlocProvider.value(
value: cellBloc, value: cellBloc,
child: BlocConsumer<TextCellBloc, TextCellState>( child: BlocListener<TextCellBloc, TextCellState>(
listenWhen: (previous, current) => previous.content != current.content, listenWhen: (previous, current) => previous.content != current.content,
listener: (context, state) { listener: (context, state) {
if (!state.enableEdit) { if (!state.enableEdit) {
_textEditingController.text = state.content; _textEditingController.text = state.content;
} }
}, },
builder: (context, state) { child: isTitle ? _buildTitle() : _buildText(),
final isTitle = cellBloc.cellController.fieldInfo.isPrimary;
if (state.content.isEmpty &&
state.enableEdit == false &&
focusWhenInit == false &&
!isTitle) {
return const SizedBox.shrink();
}
final icon = isTitle ? _buildIcon(state) : null;
final child = isTitle
? _buildTextField(state.enableEdit || focusWhenInit)
: _buildText(state.content);
return Row(
children: [
if (icon != null) ...[
icon,
const HSpace(4.0),
],
Expanded(child: child),
],
);
},
), ),
); );
} }
@ -146,6 +133,8 @@ class _TextCellState extends State<TextCardCell> {
@override @override
void dispose() { void dispose() {
_textEditingController.dispose(); _textEditingController.dispose();
widget.editableNotifier?.isCellEditing
.removeListener(_bindEditableNotifier);
focusNode.dispose(); focusNode.dispose();
cellBloc.close(); cellBloc.close();
super.dispose(); super.dispose();
@ -170,61 +159,83 @@ class _TextCellState extends State<TextCardCell> {
return null; return null;
} }
Widget _buildText(String content) { Widget _buildText() {
final text = return BlocBuilder<TextCellBloc, TextCellState>(
content.isEmpty ? LocaleKeys.grid_row_textPlaceholder.tr() : content; builder: (context, state) {
final color = content.isEmpty ? Theme.of(context).hintColor : null; final content = state.content;
final text = content.isEmpty
? LocaleKeys.grid_row_textPlaceholder.tr()
: content;
final color = content.isEmpty ? Theme.of(context).hintColor : null;
return Padding( return Padding(
padding: widget.style.padding, padding: widget.style.padding,
child: Text( child: Text(
text, text,
style: widget.style.textStyle.copyWith(color: color), style: widget.style.textStyle.copyWith(color: color),
maxLines: widget.style.maxLines, maxLines: widget.style.maxLines,
), ),
);
},
); );
} }
Widget _buildTextField(bool isEditing) { Widget _buildTitle() {
final padding = final textField = _buildTextField();
widget.style.padding.add(const EdgeInsets.symmetric(vertical: 4.0)); return BlocBuilder<TextCellBloc, TextCellState>(
return IgnorePointer( builder: (context, state) {
ignoring: !isEditing, final icon = _buildIcon(state);
child: CallbackShortcuts( return Row(
bindings: { children: [
const SingleActivator(LogicalKeyboardKey.escape): () => if (icon != null) ...[
focusNode.unfocus(), icon,
}, const HSpace(4.0),
child: TextField( ],
controller: _textEditingController, Expanded(child: textField),
focusNode: focusNode, ],
onChanged: (_) { );
if (_textEditingController.value.composing.isCollapsed) { },
cellBloc );
.add(TextCellEvent.updateText(_textEditingController.text)); }
}
}, Widget _buildTextField() {
onEditingComplete: () => focusNode.unfocus(), return BlocSelector<TextCellBloc, TextCellState, bool>(
maxLines: isEditing ? null : 2, selector: (state) => state.enableEdit,
minLines: 1, builder: (context, isEditing) {
textInputAction: TextInputAction.done, return IgnorePointer(
readOnly: !isEditing, ignoring: !isEditing,
enableInteractiveSelection: isEditing, child: CallbackShortcuts(
style: widget.style.titleTextStyle, bindings: {
decoration: InputDecoration( const SingleActivator(LogicalKeyboardKey.escape): () =>
contentPadding: padding, focusNode.unfocus(),
border: InputBorder.none, },
enabledBorder: InputBorder.none, child: TextField(
isDense: true, controller: _textEditingController,
isCollapsed: true, focusNode: focusNode,
hintText: LocaleKeys.grid_row_titlePlaceholder.tr(), onEditingComplete: () => focusNode.unfocus(),
hintStyle: widget.style.titleTextStyle.copyWith( maxLines: isEditing ? null : 2,
color: Theme.of(context).hintColor, minLines: 1,
textInputAction: TextInputAction.done,
readOnly: !isEditing,
enableInteractiveSelection: isEditing,
style: widget.style.titleTextStyle,
decoration: InputDecoration(
contentPadding: widget.style.padding
.add(const EdgeInsets.symmetric(vertical: 4.0)),
border: InputBorder.none,
enabledBorder: InputBorder.none,
isDense: true,
isCollapsed: true,
hintText: LocaleKeys.grid_row_titlePlaceholder.tr(),
hintStyle: widget.style.titleTextStyle.copyWith(
color: Theme.of(context).hintColor,
),
),
onTapOutside: (_) {},
), ),
), ),
onTapOutside: (_) {}, );
), },
),
); );
} }
} }