99 lines
3.9 KiB
Dart
99 lines
3.9 KiB
Dart
/// 한 줄 · 두 줄 배치 판단 (관리자 지시 10/21: 화면마다 따로 재지 않고 이것 하나를 부른다 - 82a87da P2, after_1020 에서 옮김).
|
|
///
|
|
/// 약속 ([fitRow] - 판단만, 그리지 않음)
|
|
/// - 입력: [maxWidth] 쓸 수 있는 폭, [first] · [second] 앞 · 뒤 묶음 아이템의 최소 폭 (단추는 기본 누를 자리 그대로),
|
|
/// [gap] 한 줄일 때 두 묶음 사이, 줄일 수 있는 아이템 하나의 폭 범위 [flexMin] ~ [flexMax] (없으면 0 · 0).
|
|
/// - 출력: [RowFit.split] 이면 두 줄 (위 = first, 아래 = second + 줄일 수 있는 아이템), [RowFit.flexWidth] 줄일 수 있는 아이템의 폭
|
|
/// (0 이면 그 줄에 넣지 않는다 - 같은 일을 하는 다른 단추가 남아 있어야 한다).
|
|
/// - 오류: 없음. 두 줄로도 [second] 가 들지 않으면 split = true · flexWidth = 0 이고 [RowFit.overflow] 에 모자란 폭.
|
|
///
|
|
/// 약속 ([FitButtonRows] - 그림): 앞 단추들 · 뒤 앞 단추들 · 줄일 수 있는 것 (폭을 받아 그림) · 뒤 끝 단추들 → 한 줄
|
|
/// (앞 · 간격 · 뒤 앞 · 줄일 것 · 빈칸 · 뒤 끝) 또는 두 줄 (위 앞 단추 고르게, 아래 뒤 묶음). 단추 크기는 줄이지 않는다.
|
|
/// 쓰는 곳: 재생 화면 조절 줄 (P2)
|
|
library;
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
class RowFit {
|
|
final bool split;
|
|
final double flexWidth;
|
|
final double overflow;
|
|
const RowFit(this.split, this.flexWidth, this.overflow);
|
|
}
|
|
|
|
RowFit fitRow({
|
|
required double maxWidth,
|
|
required List<double> first,
|
|
required List<double> second,
|
|
double gap = 0,
|
|
double flexMin = 0,
|
|
double flexMax = 0,
|
|
bool forceSplit = false,
|
|
}) {
|
|
double sum(List<double> xs) => xs.fold(0.0, (a, b) => a + b);
|
|
final a = sum(first), b = sum(second);
|
|
final split = forceSplit || a + gap + b + flexMax > maxWidth;
|
|
final room = maxWidth - b - (split ? 0 : a + gap);
|
|
var flex = room.clamp(0.0, flexMax);
|
|
if (flex < flexMin) flex = 0;
|
|
final over = (split ? [a, b].reduce((x, y) => x > y ? x : y) : a + gap + b) - maxWidth;
|
|
return RowFit(split, flex, over > 0 ? over : 0);
|
|
}
|
|
|
|
class FitButtonRows extends StatelessWidget {
|
|
const FitButtonRows({
|
|
super.key,
|
|
required this.first,
|
|
required this.secondLead,
|
|
required this.secondTail,
|
|
required this.buttonWidth,
|
|
this.flex,
|
|
this.flexMin = 0,
|
|
this.flexMax = 0,
|
|
this.gap = 8,
|
|
this.forceSplit = false,
|
|
});
|
|
|
|
/// 위 줄 (두 줄일 때) · 앞 (한 줄일 때) 단추들
|
|
final List<Widget> first;
|
|
|
|
/// 뒤 묶음의 앞 (줄일 것 앞) · 끝 (빈칸 뒤) 단추들
|
|
final List<Widget> secondLead, secondTail;
|
|
|
|
/// 단추 하나의 누를 자리 (판단에 쓰는 폭)
|
|
final double buttonWidth;
|
|
|
|
/// 줄일 수 있는 것 (받은 폭으로 그림 - 예: 소리 막대). 폭이 0 이면 넣지 않는다
|
|
final Widget Function(double width)? flex;
|
|
final double flexMin, flexMax, gap;
|
|
|
|
/// 늘 두 줄 (손가락 화면)
|
|
final bool forceSplit;
|
|
|
|
@override
|
|
Widget build(BuildContext context) => LayoutBuilder(builder: (context, box) {
|
|
final fit = fitRow(
|
|
maxWidth: box.maxWidth,
|
|
first: List.filled(first.length, buttonWidth),
|
|
second: List.filled(secondLead.length + secondTail.length, buttonWidth),
|
|
gap: gap,
|
|
flexMin: flex == null ? 0 : flexMin,
|
|
flexMax: flex == null ? 0 : flexMax,
|
|
forceSplit: forceSplit,
|
|
);
|
|
final rest = [
|
|
...secondLead,
|
|
if (flex != null && fit.flexWidth > 0) flex!(fit.flexWidth),
|
|
const Spacer(),
|
|
...secondTail,
|
|
];
|
|
return Column(mainAxisSize: MainAxisSize.min, children: [
|
|
if (fit.split) Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: first),
|
|
Row(children: [
|
|
if (!fit.split) ...[...first, SizedBox(width: gap)],
|
|
...rest,
|
|
]),
|
|
]);
|
|
});
|
|
}
|