diff --git a/apps/app_seeds/lib/ui/chat_screen.dart b/apps/app_seeds/lib/ui/chat_screen.dart index a48026c..ec8a492 100644 --- a/apps/app_seeds/lib/ui/chat_screen.dart +++ b/apps/app_seeds/lib/ui/chat_screen.dart @@ -386,8 +386,12 @@ class _ChatBody extends StatelessWidget { }, child: Column( children: [ - const _TrustBanner(), - const _RatingStrip(), + // Free the vertical space the trust/rating strips take while the + // keyboard is up: on short screens (e.g. landscape) their fixed height + // plus the composer would otherwise overflow the shrunken body. They + // reappear the moment the keyboard is dismissed. + const HideWhenKeyboardOpen(child: _TrustBanner()), + const HideWhenKeyboardOpen(child: _RatingStrip()), Expanded( child: ChatMessageList( peerName: peerName, @@ -403,6 +407,21 @@ class _ChatBody extends StatelessWidget { } } +/// Hides [child] while the on-screen keyboard is open. Used for the chat's +/// secondary strips (trust, rating) so their fixed height doesn't push the +/// composer off a short screen when the keyboard steals vertical space. +class HideWhenKeyboardOpen extends StatelessWidget { + const HideWhenKeyboardOpen({required this.child, super.key}); + + final Widget child; + + @override + Widget build(BuildContext context) => + MediaQuery.viewInsetsOf(context).bottom > 0 + ? const SizedBox.shrink() + : child; +} + /// The scrolling list of chat bubbles. Anchored at the bottom (chat /// convention): the newest message is always in view, and new arrivals show up /// in place instead of landing below the fold. Reads the [MessagesCubit] from diff --git a/apps/app_seeds/test/ui/hide_when_keyboard_open_test.dart b/apps/app_seeds/test/ui/hide_when_keyboard_open_test.dart new file mode 100644 index 0000000..1123d5f --- /dev/null +++ b/apps/app_seeds/test/ui/hide_when_keyboard_open_test.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:tane/ui/chat_screen.dart'; + +void main() { + Widget host({required double keyboardInset}) => MediaQuery( + data: MediaQueryData(viewInsets: EdgeInsets.only(bottom: keyboardInset)), + child: const Directionality( + textDirection: TextDirection.ltr, + child: HideWhenKeyboardOpen(child: Text('banner')), + ), + ); + + testWidgets('shows its child when the keyboard is closed', (tester) async { + await tester.pumpWidget(host(keyboardInset: 0)); + expect(find.text('banner'), findsOneWidget); + }); + + testWidgets('hides its child when the keyboard is open', (tester) async { + await tester.pumpWidget(host(keyboardInset: 300)); + expect(find.text('banner'), findsNothing); + }); +}