diff --git a/lib/helpers/extensions/refocus_after_rebuild.dart b/lib/helpers/extensions/refocus_after_rebuild.dart new file mode 100644 index 0000000..a9ce60d --- /dev/null +++ b/lib/helpers/extensions/refocus_after_rebuild.dart @@ -0,0 +1,9 @@ +import "package:material_ui/material_ui.dart"; + +extension RefocusAfterRebuild on FocusNode { + /// Focuses this again once the text field using it has been replaced. + void refocusAfterRebuild() { + unfocus(); + WidgetsBinding.instance.addPostFrameCallback((_) => requestFocus()); + } +} diff --git a/lib/helpers/hooks/chat_composer.dart b/lib/helpers/hooks/chat_composer.dart index 71262c8..44b5352 100644 --- a/lib/helpers/hooks/chat_composer.dart +++ b/lib/helpers/hooks/chat_composer.dart @@ -3,6 +3,7 @@ import "package:flutter_hooks/flutter_hooks.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:material_ui/material_ui.dart"; import "package:nexus/controllers/composer_draft.dart"; +import "package:nexus/helpers/extensions/refocus_after_rebuild.dart"; import "package:nexus/models/composer_params.dart"; import "package:nexus/models/relation_type.dart"; @@ -17,11 +18,10 @@ final class ChatComposer({ final node = useFocusNode( onKeyEvent: (node, event) { if (event is KeyDownEvent && event.logicalKey == .escape) { + final isEditing = ref.read(provider).isEditing; ref.read(provider.notifier).dismiss(); - // Ending an edit replaces the text field, losing its focus - WidgetsBinding.instance.addPostFrameCallback( - (_) => node.requestFocus(), - ); + // Ending an edit replaces the text field + if (isEditing) node.refocusAfterRebuild(); return .handled; } @@ -44,7 +44,11 @@ final class ChatComposer({ void relate(int rowId, RelationType type) { notifier.relate(rowId, type); - // Starting an edit replaces the text field, losing its focus - WidgetsBinding.instance.addPostFrameCallback((_) => node.requestFocus()); + // Starting or ending an edit replaces the text field + if (type == .edit || relationType == .edit) { + node.refocusAfterRebuild(); + } else { + node.requestFocus(); + } } } diff --git a/lib/widgets/composer/composer.dart b/lib/widgets/composer/composer.dart index 7bb7c0b..9376318 100644 --- a/lib/widgets/composer/composer.dart +++ b/lib/widgets/composer/composer.dart @@ -8,6 +8,7 @@ import "package:nexus/controllers/bot_command_draft.dart"; import "package:nexus/controllers/bot_commands.dart"; import "package:nexus/controllers/composer_draft.dart"; import "package:nexus/controllers/rooms.dart"; +import "package:nexus/helpers/extensions/refocus_after_rebuild.dart"; import "package:nexus/helpers/extensions/show_error_snack_bar.dart"; import "package:nexus/helpers/extensions/with_latest_edit.dart"; import "package:nexus/models/bot_command.dart"; @@ -178,8 +179,8 @@ class const Composer( void dismiss() { draftNotifier.dismiss(); - // Ending an edit replaces the text field, losing its focus - WidgetsBinding.instance.addPostFrameCallback((_) => node?.requestFocus()); + // Ending an edit replaces the text field + if (isEditing && (node?.hasFocus ?? false)) node?.refocusAfterRebuild(); } final sinceTypingSent = useMemoized(Stopwatch.new); @@ -302,7 +303,8 @@ class const Composer( ); } ref.read(botCommandProvider.notifier).clear(); - WidgetsBinding.instance.addPostFrameCallback((_) => node?.requestFocus()); + // The text field replaces the command form + node?.refocusAfterRebuild(); } void send() {