/// 약속 (W 공용 위젯, 설계 7-2 JjTitleBar - 창 · 판 안의 제목 줄): 아이콘 · 제목 · 설명 · 단추들 → 한 줄. /// 입력: 제목 ([titleLines] 줄까지 - 처음 1, 넘치면 "…") · 앞 아이콘 · 설명 (넓으면 제목 옆, 좁으면 제목 아래 2줄까지 - 숨기지 않음) · 단추들 (늘 오른쪽 끝에 다 보임) · /// [narrowBelow] (이 폭보다 좁으면 좁은 꼴) · [narrowActions] (좁을 때 쓸 단추들 - 예: 글 단추 → 같은 이름의 아이콘 단추) · 제목 글꼴. /// 출력: Row (넘치지 않음 - 줄이는 것은 제목 · 설명뿐). 오류 없음. /// 쓰는 곳: 다운로드 창 · 재생 목록 판 · 즐겨찾기 판 · 작업 현황 판 · 저장 공간 정리 창 · 화면 · 색 보정. 화면은 부르기만 한다. /// 쪽 전체의 위 막대는 [jjTitleBar] library; import 'package:flutter/material.dart'; import 'theme.dart'; class JjHeaderRow extends StatelessWidget { const JjHeaderRow({ super.key, required this.title, this.icon, this.subtitle, this.actions = const [], this.narrowActions, this.narrowBelow = 0, this.style, this.titleLines = 1, }); final String title; /// 제목 앞 아이콘 (크기 · 색은 부르는 쪽이 정한다) final Widget? icon; final String? subtitle; final List actions; final List? narrowActions; final double narrowBelow; /// 제목 글꼴 (null 이면 둘레의 글꼴 - 창 제목이면 창 제목 글꼴) final TextStyle? style; final int titleLines; static TextStyle get _subStyle => TextStyle(fontSize: 12, color: JjColors.textDim); // 좁은 꼴 · 설명이 없으면 LayoutBuilder 없이 (창 제목은 Flutter 가 내재 폭을 재는데, LayoutBuilder 는 그것을 못 한다) @override Widget build(BuildContext context) => narrowBelow <= 0 && subtitle == null ? _row(false, double.infinity) : LayoutBuilder(builder: (context, box) => _row(box.maxWidth < narrowBelow, box.maxWidth)); Widget _row(bool narrow, double width) { final head = Text(title, maxLines: titleLines, overflow: TextOverflow.ellipsis, style: style); return Row(children: [ if (icon != null) ...[icon!, const SizedBox(width: 8)], if (subtitle == null) Expanded(child: head) else if (narrow) Expanded( child: Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ head, Text(subtitle!, maxLines: 2, overflow: TextOverflow.ellipsis, style: _subStyle), ]), ) else ...[ // 제목은 쓰는 만큼 (반까지), 설명이 나머지 - 단추는 오른쪽 끝에 붙는다 ConstrainedBox(constraints: BoxConstraints(maxWidth: width / 2), child: head), const SizedBox(width: 12), Expanded(child: Text(subtitle!, maxLines: 1, overflow: TextOverflow.ellipsis, style: _subStyle)), ], ...(narrow ? narrowActions ?? actions : actions), ]); } }