폰 가로 브라우저 · 다운로드 주소 칸: 모자라면 위 막대 두 줄 (201 · 200)

- 버그 (기기 확인 지적): 가로 폰에서 주소 칸이 너무 좁거나 보이지 않던 것
- AppTopBar 가 가운데 폭을 재고 minMiddleWidth (브라우저 380 · 다운로드 420) 보다 좁으면 두 줄로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix_mkv
RyuJongJin 2026-10-11 23:09:35 +09:00
parent 487d955a99
commit c9c4f031b5
4 changed files with 77 additions and 4 deletions

View File

@ -25,20 +25,44 @@ bool isCompact(BuildContext context) => MediaQuery.sizeOf(context).width < compa
/// 모든 화면의 위쪽 막대: [nav] [middle (남는 너비)] [actions].
/// 좁은 화면에서는 두 줄 - 위: [nav] … [actions], 아래: [middle] 이 한 줄을 다 쓴다.
class AppTopBar extends StatelessWidget {
/// [minMiddleWidth] (201): 한 줄일 때 middle 이 이보다 좁아지면 (폰 가로처럼 넓지만 버튼이 폭을 다 차지할 때) 두 줄로.
/// 브라우저 주소 칸처럼 middle 이 꼭 보여야 하는 화면이 준다 (0 이면 폭만으로 정함)
class AppTopBar extends StatefulWidget {
final Widget nav;
final Widget middle;
final Widget actions;
const AppTopBar({super.key, required this.nav, required this.middle, required this.actions});
final double minMiddleWidth;
const AppTopBar({super.key, required this.nav, required this.middle, required this.actions, this.minMiddleWidth = 0});
@override
State<AppTopBar> createState() => _AppTopBarState();
}
class _AppTopBarState extends State<AppTopBar> {
final _mid = GlobalKey();
/// 한 줄에서 middle 이 모자랐던 화면 폭 - 이 폭 이하이면 두 줄
double? _twoRowsAtOrBelow;
void _check(double width) {
if (!mounted || widget.minMiddleWidth <= 0) return;
final box = _mid.currentContext?.findRenderObject() as RenderBox?;
if (box == null || !box.hasSize) return;
if (box.size.width < widget.minMiddleWidth) setState(() => _twoRowsAtOrBelow = width);
}
@override
Widget build(BuildContext context) {
if (!isCompact(context)) {
final nav = widget.nav, middle = widget.middle, actions = widget.actions;
final width = MediaQuery.sizeOf(context).width;
final narrow = _twoRowsAtOrBelow != null && width <= _twoRowsAtOrBelow!;
if (!isCompact(context) && !narrow) {
if (widget.minMiddleWidth > 0) WidgetsBinding.instance.addPostFrameCallback((_) => _check(width));
return Container(
height: appBarHeight,
color: JjColors.panel,
padding: const EdgeInsets.only(left: 8, right: appBarRightPadding),
child: Row(children: [nav, const SizedBox(width: 8), Expanded(child: middle), actions]),
child: Row(children: [nav, const SizedBox(width: 8), Expanded(child: KeyedSubtree(key: _mid, child: middle)), actions]),
);
}
return Container(

View File

@ -591,7 +591,9 @@ class _BrowserPageState extends State<BrowserPage> with RouteAware {
);
// 주소 칸이 좁아지면 (폰 세로 · 태블릿 세로 · 폰 가로 100%) 자주 안 쓰는 버튼은 ⋮ 메뉴로 - 주소가 "http" 만 보이거나
// 아예 사라지던 것. 버튼을 없애지는 않는다.
// 201: 폰 가로처럼 넓어도 버튼이 폭을 다 차지하면 주소 칸이 사라졌다 - 그때는 두 줄 (아래 줄을 주소 칸이 다 씀)
return AppTopBar(
minMiddleWidth: 380,
nav: const AppNavButtons(onBrowserPage: true),
actions: AppActions(c: widget.c),
middle: LayoutBuilder(builder: (context, box) {

View File

@ -88,7 +88,9 @@ class _DownloadsPageState extends State<DownloadsPage> {
body: SwipeNav(
current: 'downloads',
child: Column(children: [
// 200: 폰 가로에서 버튼이 폭을 다 차지해 주소 칸이 눌려 주소가 보이지 않았다 - 모자라면 두 줄 (아래 줄을 주소 칸이 다 씀)
AppTopBar(
minMiddleWidth: 420,
nav: const AppNavButtons(onDownloadsPage: true),
actions: const AppActions(),
middle: Row(children: [

View File

@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:jj_mkvmaker/ui/app_actions.dart';
/// 201: 넓은 화면 (폰 가로) 이어도 버튼이 폭을 다 차지해 가운데 (브라우저 주소 칸) 가 모자라면 두 줄로
void main() {
Widget bar({double minMiddle = 0}) => MaterialApp(
home: Scaffold(
body: Column(children: [
AppTopBar(
minMiddleWidth: minMiddle,
nav: const SizedBox(width: 420, height: 40),
actions: const SizedBox(width: 360, height: 40),
middle: const SizedBox(key: ValueKey('mid'), height: 36, child: Text('주소')),
),
]),
),
);
testWidgets('201: 가운데가 최소 폭보다 좁아지면 두 줄 (아래 줄을 가운데가 다 씀)', (tester) async {
tester.view.physicalSize = const Size(900, 400); // 폰 가로 (compactWidth 720 보다 넓음)
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.reset);
await tester.pumpWidget(bar(minMiddle: 380));
await tester.pump();
await tester.pump();
expect(tester.getSize(find.byKey(const ValueKey('mid'))).width, greaterThan(800));
// 넓은 화면에서는 한 줄 그대로
tester.view.physicalSize = const Size(1600, 900);
await tester.pump();
await tester.pump();
expect(tester.getSize(find.byKey(const ValueKey('mid'))).width, lessThan(900));
expect(tester.getSize(find.byKey(const ValueKey('mid'))).width, greaterThanOrEqualTo(380));
});
testWidgets('201: 최소 폭을 주지 않은 화면은 예전처럼 (폭만으로 정함)', (tester) async {
tester.view.physicalSize = const Size(900, 400);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.reset);
await tester.pumpWidget(bar());
await tester.pump();
await tester.pump();
expect(tester.getSize(find.byKey(const ValueKey('mid'))).width, lessThan(380));
});
}