From a76c39a9c37d44e57c6f3576cf3b2cb9928691dc Mon Sep 17 00:00:00 2001 From: David Riedel <48388362+Riedel-David@users.noreply.github.com> Date: Thu, 22 Oct 2026 13:40:15 +0300 Subject: [PATCH 1/2] Profile: let the language picker sheet grow and scroll instead of overflowing --- lib/ui2/profile/profile.dart | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/lib/ui2/profile/profile.dart b/lib/ui2/profile/profile.dart index 8252aa16e..9eccf6b27 100644 --- a/lib/ui2/profile/profile.dart +++ b/lib/ui2/profile/profile.dart @@ -212,22 +212,27 @@ Future _pickLanguage(BuildContext c) async { context: c, backgroundColor: p.card, showDragHandle: true, + // The default sheet is capped at 9/16 of the screen; with seven languages + // the list overflowed it. Lift the cap and scroll if it still doesn't fit. + isScrollControlled: true, builder: (sheet) => SafeArea( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - for (final code in options) - ListTile( - title: Text(_languageLabel(sheet, code), style: F.body.copyWith(color: p.ink)), - trailing: ctrl.code == code - ? Icon(LucideIcons.check, size: 18, color: p.on(C.blue)) - : null, - onTap: () async { - await ctrl.setCode(code); - if (sheet.mounted) Navigator.of(sheet).pop(); - }, - ), - ], + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + for (final code in options) + ListTile( + title: Text(_languageLabel(sheet, code), style: F.body.copyWith(color: p.ink)), + trailing: ctrl.code == code + ? Icon(LucideIcons.check, size: 18, color: p.on(C.blue)) + : null, + onTap: () async { + await ctrl.setCode(code); + if (sheet.mounted) Navigator.of(sheet).pop(); + }, + ), + ], + ), ), ), ); From f715a0f5d34b8fc663387efa5a9b1238cc01d843 Mon Sep 17 00:00:00 2001 From: David Riedel <48388362+Riedel-David@users.noreply.github.com> Date: Thu, 22 Oct 2026 13:53:21 +0300 Subject: [PATCH 2/2] Language sheet: respect the top safe area, add a regression test --- lib/ui2/profile/profile.dart | 1 + test/language_picker_sheet_test.dart | 47 ++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 test/language_picker_sheet_test.dart diff --git a/lib/ui2/profile/profile.dart b/lib/ui2/profile/profile.dart index 9eccf6b27..6002cc659 100644 --- a/lib/ui2/profile/profile.dart +++ b/lib/ui2/profile/profile.dart @@ -215,6 +215,7 @@ Future _pickLanguage(BuildContext c) async { // The default sheet is capped at 9/16 of the screen; with seven languages // the list overflowed it. Lift the cap and scroll if it still doesn't fit. isScrollControlled: true, + useSafeArea: true, // a full-height sheet must not reach under the status bar builder: (sheet) => SafeArea( child: SingleChildScrollView( child: Column( diff --git a/test/language_picker_sheet_test.dart b/test/language_picker_sheet_test.dart new file mode 100644 index 000000000..2e052f401 --- /dev/null +++ b/test/language_picker_sheet_test.dart @@ -0,0 +1,47 @@ +// The language picker used to overflow its bottom sheet (capped at 9/16 of the +// screen) once the list grew past what fits. It must lay out without an +// overflow on a short screen, and every language must stay reachable. +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'package:openstrap_edge/l10n/app_localizations.dart'; +import 'package:openstrap_edge/state/locale_controller.dart'; +import 'package:openstrap_edge/ui2/profile/profile.dart'; +import 'package:openstrap_edge/ui2/ui2.dart'; + +void main() { + testWidgets('language sheet scrolls on a short screen and selects the last language', + (tester) async { + SharedPreferences.setMockInitialValues({}); + tester.view.physicalSize = const Size(360, 560); + tester.view.devicePixelRatio = 1; + addTearDown(tester.view.reset); + + final ctrl = LocaleController.seed(null); + await tester.pumpWidget(ChangeNotifierProvider.value( + value: ctrl, + child: MaterialApp( + theme: buildTheme(Brightness.dark), + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + home: const ProfileHomeView(stats: ProfileStats(name: 'Sahil')), + ), + )); + await tester.pumpAndSettle(); + + await tester.tap(find.text('System default').first); + await tester.pumpAndSettle(); + expect(tester.takeException(), isNull, reason: 'sheet must not overflow'); + + final last = find.text('中文'); + await tester.ensureVisible(last); + await tester.pumpAndSettle(); + await tester.tap(last); + await tester.pumpAndSettle(); + + expect(ctrl.code, 'zh'); + expect(find.text('Deutsch'), findsNothing, reason: 'sheet should be dismissed'); + }); +}