Chat banner dismiss, calmer scroll, real send state - #85
Open
VidurShah wants to merge 2 commits into
Open
Conversation
Quality-of-life fixes for three things the chat page did visibly wrong: an undismissable warning banner, a violent scroll on entering the tab, and a composer that threw away typed text when a send failed. - Let the hours banner be swiped up to dismiss, collapsing it with AnimatedSize. The flag lives on _ChatPageState, which BottomBar rebuilds on every tab switch, so the notice reappears the next time the user navigates to the chat rather than being persisted. A trailing chevron makes the otherwise-invisible gesture discoverable. - Make the initial scroll placement instant. Entering the tab used to animate a 200ms scrollTo from the top of the conversation to the newest message, which ScrollablePositionedList renders by interpolating between two overlapping lists. Only genuinely new messages animate now, and the search jump is untouched. - Clear the composer only once the sendChatMessage callable returns rather than before the network call, and show a spinner in the send button while the message is in flight. Closes #76
VidurShah
force-pushed
the
vidur/home-dynamic-stats
branch
from
August 19, 2026 20:24
3ed17b4 to
9096fba
Compare
VidurShah
force-pushed
the
vidur/chat-fine-tuning
branch
from
August 19, 2026 20:24
60d790f to
383e37e
Compare
_NotificationIconButton and its state class were defined but never referenced in build() — the permission prompt is handled by _requestNotificationPermission in initState instead. Removing them also makes the firebase_messaging import unused, since AuthorizationStatus was its only consumer in this file.
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.
Problem
Three things the chat page did visibly wrong. Closes #76.
BottomBaruses anAnimatedSwitcherwith a changingValueKey, soChatPageis destroyed and recreated on every tab switch._MessagesListremounts with_lastDocCount = 0, the first snapshot arrives, and_scrollToBottomruns a 200ms animatedscrollTofrom index 0 to the newest message.ScrollablePositionedListanimates by rendering two overlapping lists and interpolating between them, which is what read as the wild scroll.This is stacked on #84 (issue #72) — that one needs to merge first. The branch is cut from
vidur/home-dynamic-statsand the base auto-retargets tomainonce #84 lands, which keeps the diff here chat-only. Both branches touchchat_page.dart, so stacking was cheaper than resolving it twice.Changes
All in
lib/pages/chat_page.dart. No new dependencies —AnimatedSizeandGestureDetectorship with the framework._hoursBanner— now takes an upward fling (onVerticalDragEnd,primaryVelocity < 0, so a downward drag over it does nothing and you can't lose it while trying to read it) and collapses viaAnimatedSizeat the same 220mseaseOutused elsewhere in the app.Dismissiblewould be the more idiomatic widget, but it wants a fixed-extent child in a list and ships horizontal-swipe defaults we'd only have to switch back off. A trailingkeyboard_arrow_upchevron was added as an affordance since the gesture is otherwise completely invisible._ChatPageState— added_hoursNoticeDismissed. It resets on every mount, which is exactly the acceptance criterion: becauseBottomBarrebuildsChatPagefrom scratch on each tab switch, the banner is back the next time the user navigates to chat. Deliberately not persisted to storage or hoisted intoBottomBarState— either would break that requirement._MessagesListState—_scrollToBottomtakes{bool animate = true}, and a_didInitialScrollflag makes the first placementDuration.zero. Messages arriving while you're on the page still animate smoothly, andscrollToMessage(the search jump) is untouched. The flag also resets indidUpdateWidgetalongside_lastDocCountwhen the list is pointed at a different chat._ChatInputState._send—_controller.clear()moved after theawait.sendChatMessageis a callable that resolves once its Firestore batch commits, so awaiting it genuinely is "made it into firebase"; the bubble itself lands a beat later via the snapshot listener. The image path has no text to preserve, so it was left alone._InputButton— gained aloadingflag that swaps the icon for an 18pxCircularProgressIndicator, matching the pattern already inpending_verification.dart. Both buttons still grey out during a send._NotificationIconButton(separate commit) — removed. It and its state class were defined but never referenced inbuild(); the permission prompt is handled by_requestNotificationPermissionininitStateinstead. That also made thefirebase_messagingimport unused, sinceAuthorizationStatuswas its only consumer in this file.Notes / follow-ups
BottomBarto anIndexedStackto preserve chat state across tabs would remove the remount entirely and fix this at the root, but it would also break the banner's reappear-every-time requirement and disturb the checklist's unsaved-changes flow. Explicitly out of scope here.AnimatedSwitchercross-fade inbottom_bar.dartstill runs concurrently with the scroll. If any residual jitter shows up, that's the next thing to look at — but it means touchingbottom_bar.dart, which affects every tab.