import 'package:flutter/material.dart'; import 'package:friendly_map/modules/circles/circles.dart'; import '../theme.dart'; import 'common.dart'; /// 원 위의 한 점: 이름표와 색, 눌렀을 때 할 일. class RingDot { const RingDot({required this.label, required this.color, this.sub, this.selected = false, this.faded = false, this.onTap}); final String label; final String? sub; final Color color; final bool selected; final bool faded; final VoidCallback? onTap; } /// 가운데에 [center] 를 두고, [rings] 의 고리마다 안쪽에서 바깥쪽 원 위에 점을 고르게 놓는다. /// 고리 하나가 6명을 넘으면 바깥 고리가 하나 더 생긴다 (rings() 로 미리 나눠서 넘긴다). class RingView extends StatelessWidget { const RingView({super.key, required this.center, required this.rings, this.centerLabel = '나', this.dotSize = 44}); final Widget center; final List> rings; final String centerLabel; final double dotSize; @override Widget build(BuildContext context) { return LayoutBuilder(builder: (context, box) { final w = box.maxWidth; // 첫 고리는 가운데 "나"와 이름표가 겹치지 않을 만큼 떨어뜨리고, 고리 사이는 점 + 이름표 두 줄 높이만큼 둔다. final first = dotSize + 52; final gap = dotSize + 52; final radius = [for (var i = 0; i < rings.length; i++) first + gap * i]; final reach = (radius.isEmpty ? first : radius.last) + 46; final canvasW = reach * 2 > w ? reach * 2 : w; final h = reach * 2 + 16; // 사람이 많아 원이 화면보다 커지면 통째로 줄여서 화면 안에 맞춘다. final scale = canvasW > w ? w / canvasW : 1.0; final cx = canvasW / 2, cy = h / 2; return SizedBox( width: w, height: h * scale, child: FittedBox( fit: BoxFit.scaleDown, child: SizedBox( width: canvasW, height: h, child: Stack(children: [ for (var i = 0; i < rings.length; i++) Positioned( left: cx - radius[i], top: cy - radius[i], child: Container(width: radius[i] * 2, height: radius[i] * 2, decoration: BoxDecoration(shape: BoxShape.circle, border: Border.all(color: Pal.line))), ), Positioned(left: cx - 30, top: cy - 30, child: SizedBox(width: 60, height: 60, child: center)), for (var i = 0; i < rings.length; i++) for (final e in _spots(rings[i], radius[i]).indexed) Positioned( left: cx + e.$2.$1 - 42, top: cy + e.$2.$2 - dotSize / 2, child: _Dot(rings[i][e.$1], dotSize), ), ]), ), ), ); }); } List<(double, double)> _spots(List ring, double r) => ringPositions(ring.length, r); } class _Dot extends StatelessWidget { const _Dot(this.d, this.size); final RingDot d; final double size; @override Widget build(BuildContext context) { return InkWell( borderRadius: BorderRadius.circular(12), onTap: d.onTap, child: SizedBox( width: 84, child: Column(mainAxisSize: MainAxisSize.min, children: [ Opacity( opacity: d.faded ? 0.4 : 1, child: Stack(clipBehavior: Clip.none, children: [ Avatar(d.label, size: size, color: d.color), if (d.selected) Positioned( right: -4, top: -4, child: Container( decoration: const BoxDecoration(color: Pal.accent, shape: BoxShape.circle), padding: const EdgeInsets.all(2), child: const Icon(Icons.check, size: 14, color: Colors.black), ), ), ]), ), const SizedBox(height: 3), Text(d.label, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600)), if (d.sub != null) Text(d.sub!, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle(fontSize: 10, color: Pal.muted)), ]), ), ); } } /// 가운데 "나" 동그라미. class MeDot extends StatelessWidget { const MeDot(this.name, {super.key}); final String name; @override Widget build(BuildContext context) => Container( decoration: BoxDecoration(shape: BoxShape.circle, color: Pal.accent.withValues(alpha: 0.2), border: Border.all(color: Pal.accent, width: 2)), alignment: Alignment.center, child: Text(name.isEmpty ? '나' : name.characters.first, style: const TextStyle(fontSize: 22, fontWeight: FontWeight.w800)), ); }