diff --git a/Fly App/lib/ui/fly/modes/waypoints/waypoint_sheets.dart b/Fly App/lib/ui/fly/modes/waypoints/waypoint_sheets.dart index 10f54b9..d8c1506 100644 --- a/Fly App/lib/ui/fly/modes/waypoints/waypoint_sheets.dart +++ b/Fly App/lib/ui/fly/modes/waypoints/waypoint_sheets.dart @@ -428,6 +428,10 @@ class _RenameDialogState extends State<_RenameDialog> { @override Widget build(BuildContext context) => AlertDialog( + // In landscape the keyboard leaves ~200 dp: scroll rather than + // squeeze the field to a line, and spend little on margins. + scrollable: true, + insetPadding: const EdgeInsets.symmetric(horizontal: 40, vertical: 8), backgroundColor: const Color(0xF20B1426), title: Text(widget.title, style: HudText.sans.copyWith(fontSize: 14)), content: TextField( diff --git a/Fly App/lib/ui/fly/overlays/live_stream.dart b/Fly App/lib/ui/fly/overlays/live_stream.dart index 36f2fb8..7ba1526 100644 --- a/Fly App/lib/ui/fly/overlays/live_stream.dart +++ b/Fly App/lib/ui/fly/overlays/live_stream.dart @@ -97,6 +97,9 @@ class _UrlDialogState extends State<_UrlDialog> { Widget build(BuildContext context) { final bool ok = _c.text.trim().isEmpty || validStreamUrl(_c.text); return AlertDialog( + // Landscape with the keyboard up leaves ~200 dp: scroll, never squeeze. + scrollable: true, + insetPadding: const EdgeInsets.symmetric(horizontal: 40, vertical: 8), title: const Text('Stream address'), content: TextField( key: const ValueKey('liveStreamUrl-field'), diff --git a/Fly App/lib/ui/fly/panels/settings/setting_tiles.dart b/Fly App/lib/ui/fly/panels/settings/setting_tiles.dart index cf881b2..a865330 100644 --- a/Fly App/lib/ui/fly/panels/settings/setting_tiles.dart +++ b/Fly App/lib/ui/fly/panels/settings/setting_tiles.dart @@ -215,6 +215,9 @@ class _NumberDialogState extends State<_NumberDialog> { Widget build(BuildContext context) { final String unit = widget.unit.isEmpty ? '' : ' ${widget.unit}'; return AlertDialog( + // Landscape with the keyboard up leaves ~200 dp: scroll, never squeeze. + scrollable: true, + insetPadding: const EdgeInsets.symmetric(horizontal: 40, vertical: 8), backgroundColor: const Color(0xFF10203F), title: Text(widget.label, style: HudText.sans.copyWith(fontSize: 15)), content: TextField( @@ -271,6 +274,9 @@ class _TextSettingDialogState extends State { @override Widget build(BuildContext context) { return AlertDialog( + // Landscape with the keyboard up leaves ~200 dp: scroll, never squeeze. + scrollable: true, + insetPadding: const EdgeInsets.symmetric(horizontal: 40, vertical: 8), backgroundColor: const Color(0xFF10203F), title: Text(widget.label, style: HudText.sans.copyWith(fontSize: 15)), content: TextField( diff --git a/Fly App/test/fly/waypoint_test.dart b/Fly App/test/fly/waypoint_test.dart index 264f154..e0645ab 100644 --- a/Fly App/test/fly/waypoint_test.dart +++ b/Fly App/test/fly/waypoint_test.dart @@ -15,7 +15,9 @@ import 'package:pilotvault_fly/fly/state/aircraft_state.dart'; import 'package:pilotvault_fly/fly/state/mission_state.dart'; import 'package:pilotvault_fly/fly/telemetry_reducer.dart'; import 'package:pilotvault_fly/ui/fly/dialogs/slide_to_confirm.dart'; +import 'package:pilotvault_fly/theme.dart'; import 'package:pilotvault_fly/ui/fly/fly_screen.dart'; +import 'package:pilotvault_fly/ui/fly/modes/waypoints/waypoint_sheets.dart' show showRenameDialog; import 'package:shared_preferences/shared_preferences.dart'; import 'fake_dji.dart'; @@ -799,6 +801,24 @@ void main() { expect(dji.calls.last, 'pauseWaypointMission'); }); }); + testWidgets('the route name field keeps its height above the keyboard', (WidgetTester tester) async { + // The tablet in landscape with the keyboard up left ~205 dp for the dialog; + // this is tighter still, and the field must keep its full height. + tester.view.physicalSize = const Size(1280, 800); + tester.view.devicePixelRatio = 1; + tester.view.viewInsets = const FakeViewPadding(bottom: 600); + tester.view.padding = const FakeViewPadding(top: 40); + addTearDown(tester.view.reset); + await tester.pumpWidget(MaterialApp( + theme: PV.theme(), + home: Builder(builder: (BuildContext context) => TextButton(onPressed: () => showRenameDialog(context, 'New route', title: 'Save route as'), child: const Text('open'))), + )); + await tester.tap(find.text('open')); + await tester.pumpAndSettle(); + final Rect field = tester.getRect(find.byKey(const ValueKey('rename-field'))); + expect(field.height, greaterThanOrEqualTo(56), reason: 'the field, with its counter, is never squeezed'); + expect(tester.takeException(), isNull); + }); } /// An in-memory API Server route store, with the server's last-writer-wins rule. @@ -842,4 +862,5 @@ class _FakeRemote implements MissionRemote { routes[id] = RemoteRoute(id, at, null); return null; } + }