import 'package:flutter/material.dart'; import '../l10n/tr.dart'; import 'theme.dart'; /// 옆으로 미는 줄 (폰 세로의 버튼 줄 등) 에 "더 있음" 표시. /// 줄이 화면보다 길면 끝 쪽에 흐려지는 덮개와 [›] (왼쪽으로 밀었으면 [‹]) 를 보이고, 누르면 그쪽으로 한 화면쯤 민다. /// (MKV 화면 폭 412: 세 번째 버튼이 화면 밖에서 잘렸는데 더 있다는 표시가 없었다 - 15 지적) class ScrollMore extends StatefulWidget { final Widget child; /// 덮개 색 (줄 뒤 바탕과 같게) final Color? background; const ScrollMore({super.key, required this.child, this.background}); @override State createState() => _ScrollMoreState(); } class _ScrollMoreState extends State { final _scroll = ScrollController(); bool _left = false, _right = false; @override void initState() { super.initState(); _scroll.addListener(_update); WidgetsBinding.instance.addPostFrameCallback((_) => _update()); } @override void dispose() { _scroll.dispose(); super.dispose(); } void _update() { if (!mounted || !_scroll.hasClients) return; final p = _scroll.position; final left = p.pixels > 1; final right = p.pixels < p.maxScrollExtent - 1; if (left != _left || right != _right) { setState(() { _left = left; _right = right; }); } } void _jump(int dir) { final p = _scroll.position; final to = (p.pixels + dir * p.viewportDimension * 0.7).clamp(0.0, p.maxScrollExtent); _scroll.animateTo(to, duration: const Duration(milliseconds: 220), curve: Curves.easeOut); } /// 끝의 [›] · [‹]: 짧은 흐림 + 바탕을 깐 단추 (15 지적: 잘린 버튼 글자가 [›] 뒤로 비쳐 보였다) Widget _edge(bool right) { final bg = widget.background ?? Theme.of(context).scaffoldBackgroundColor; final fade = Container( width: 20, decoration: BoxDecoration( gradient: LinearGradient( begin: right ? Alignment.centerLeft : Alignment.centerRight, end: right ? Alignment.centerRight : Alignment.centerLeft, colors: [bg.withValues(alpha: 0), bg], ), ), ); final button = ColoredBox( color: bg, child: Center( child: IconButton( tooltip: right ? tr('버튼 더 보기') : tr('앞의 버튼 보기'), visualDensity: VisualDensity.compact, style: IconButton.styleFrom(backgroundColor: JjColors.panelHigh), icon: Icon(right ? Icons.chevron_right : Icons.chevron_left, color: JjColors.accent), onPressed: () => _jump(right ? 1 : -1), ), ), ); return Positioned( top: 0, bottom: 0, left: right ? null : 0, right: right ? 0 : null, child: Row(mainAxisSize: MainAxisSize.min, children: right ? [fade, button] : [button, fade]), ); } @override Widget build(BuildContext context) => NotificationListener( // 줄 안의 버튼 글이 바뀌어 길이가 달라져도 다시 본다 onNotification: (_) { WidgetsBinding.instance.addPostFrameCallback((_) => _update()); return false; }, child: Stack(children: [ SingleChildScrollView(controller: _scroll, scrollDirection: Axis.horizontal, child: widget.child), if (_left) _edge(false), if (_right) _edge(true), ]), ); }