fix(mobile): make the composer return key insert a newline#3041
Open
rodroudi wants to merge 1 commit into
Open
fix(mobile): make the composer return key insert a newline#3041rodroudi wants to merge 1 commit into
rodroudi wants to merge 1 commit into
Conversation
The channel composer set `textInputAction: TextInputAction.send`, which renders the iOS/Android return key as a send key and wires it to `onSubmitted: (_) => send()`. That gave the composer two send affordances — the keyboard's return key and the in-app send button — and no way at all to type a newline, despite `maxLines: 5`. Switch the field to `TextInputType.multiline` + `TextInputAction.newline` and drop `onSubmitted`, matching the Pulse note composer (`compose_note_page.dart`) and the platform conventions users expect from iMessage, WhatsApp, and Signal. Sending is now the send button's job alone. That makes the send button load-bearing, and it wasn't always reachable: it lived inside the `AnimatedSwitcher` that swaps the action row for the formatting toolbar, so tapping the `Aa` button removed it from the tree. Previously the keyboard send key covered that gap; without it the user would have had no way to send while formatting was open. Hoist `_SendButton` out of the switcher so it stays mounted in both states. The layout is unchanged — it still sits flush to the trailing edge. Two widget tests cover the change: one asserts the return key is a newline action and that a submit action sends nothing, the other opens the formatting toolbar and sends from the button. Co-authored-by: Fizz <noreply@buzz-relay.org> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Rod Roudi <rod@rodroudi.com>
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Summary
The mobile channel composer sets the software keyboard's return key to a send action. The result is two send controls and zero ways to type a newline: the keyboard's return key sends, the in-app ↑ button sends, and a
maxLines: 5field can never reach line two.This makes the return key insert a newline and leaves sending to the send button alone.
Before / after
send(blue)returnRoot cause
mobile/lib/features/channels/compose_bar.dartTextInputAction.sendis a platform hint that tells iOS and Android to relabel the return key. WithmaxLines: 5the field is visually multi-line, but the only key that could produce a newline has been repurposed, so the extra lines are unreachable by typing.This is also inconsistent within our own app — the Pulse note composer already does the right thing:
The change
1. Return inserts a newline.
keyboardTypeis set explicitly rather than relying onTextField'smaxLines != 1default, becauseTextInputAction.newlineasserts against a non-multiline keyboard type — being explicit keeps the pairing obvious to the next reader and immune to a latermaxLinesedit.2. Keep the send button mounted.
Removing the keyboard send key makes the ↑ button the only way to send, so it has to be present in every composer state. It wasn't.
_SendButtonlived inside theAnimatedSwitcherthat swaps the action row for the formatting toolbar, so tapping Aa removed it from the widget tree entirely.Today that's survivable because the keyboard send key covers the gap. After change 1 it would be a dead end: formatting toolbar open, nothing to tap, no way to send. So
_SendButtonis hoisted out of the switcher and the now-redundantSpacer()dropped. Layout is unchanged — the button still sits flush to the trailing edge in both states.This was verified, not assumed. The regression test was written first and run against unmodified
main:Alternatives considered
TextInputAction.send, add a separate newline affordance. Adds UI to work around a one-line platform hint, and still leaves the double-send confusion.textInputActionand leave_SendButtonwhere it is. Smaller diff, but ships a state with no way to send. Rejected.Known follow-up (deliberately out of scope)
With a hardware keyboard (iPad, paired Bluetooth), Enter now inserts a newline and sending requires tapping ↑. That is correct-by-default and matches iMessage, but desktop Buzz uses Enter = send / Shift+Enter = newline, so there's a case for hardware-keyboard-only Enter-to-send. That needs a
Shortcuts/Actionslayer plus its own tests and is a separate change — happy to open it as a follow-up if maintainers want the parity.Risk
Low, and contained to
mobile/. One widget, no state management, no persistence, no protocol surface. The behavioral risk is muscle memory: anyone who learned to send with the keyboard key now taps ↑ instead. The reverse habit — return producing a newline — is the platform default everywhere else, so the transition costs one tap to learn.Related issue
None found. Searched open and closed PRs (
textInputAction,return key,composer keyboard) and issues (newline,mobile keyboard) onblock/buzz— no duplicates. The nearest neighbor is #2729, an unrelated composer whitespace bug.Testing
Two widget tests added to
mobile/test/features/channels/compose_bar_test.dart, both written before the fix and both failing onmain:return key inserts a newline instead of sending— assertstextInputAction == newlineandkeyboardType == multiline, then fires a submit action and assertsonSendwas never called. This is the direct regression guard: reintroducingonSubmitted: (_) => send()fails it.keeps the send button reachable while formatting is open— opens the formatting toolbar, asserts the ↑ button is still hit-testable, taps it, and asserts the message actually sent.Verified locally against the Hermit-pinned toolchain:
Note: I have not attached simulator screenshots — repo guidance forbids agents from running
flutter run/flutter build, so the verification here is the widget-test layer plus the on-device reproduction that prompted the fix. Screenshots of the before state are available if useful.