import 'package:flutter/material.dart'; import '../theme.dart'; import '../labels.dart'; /// 与 Web `ApplyBucketBar` 对齐:Segmented + 选中态。 class BucketBar extends StatelessWidget { const BucketBar({super.key, required this.value, required this.onChanged, this.extra = const []}); final String value; final ValueChanged onChanged; final List<(String, String)> extra; @override Widget build(BuildContext context) { final items = <(String, String)>[ ('', '全部'), ('pending', '待审批'), ('approved', '已通过'), ('rejected', '已驳回'), ...extra, ]; return SingleChildScrollView( scrollDirection: Axis.horizontal, padding: const EdgeInsets.fromLTRB(16, 8, 16, 4), child: Row( children: [ for (final it in items) Padding( padding: const EdgeInsets.only(right: 8), child: ChoiceChip( label: Text(it.$2), selected: value == it.$1, showCheckmark: false, visualDensity: VisualDensity.compact, selectedColor: kBind.withValues(alpha: 0.16), onSelected: (_) => onChanged(it.$1), ), ), ], ), ); } } class EmptyHint extends StatelessWidget { const EmptyHint(this.text, {super.key}); final String text; @override Widget build(BuildContext context) { return Center( child: Padding( padding: const EdgeInsets.all(32), child: Text(text, style: const TextStyle(color: kMute)), ), ); } } class KvTile extends StatelessWidget { const KvTile({super.key, required this.title, this.subtitle, this.trailing, this.onTap, this.badge, this.status}); final String title; final String? subtitle; final Widget? trailing; final VoidCallback? onTap; final int? badge; final dynamic status; @override Widget build(BuildContext context) { return Material( color: Colors.white, child: InkWell( onTap: onTap, child: Padding( padding: const EdgeInsets.fromLTRB(16, 12, 12, 0), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Container( padding: const EdgeInsets.only(bottom: 12), decoration: const BoxDecoration(border: Border(bottom: BorderSide(color: kLine, width: 0.5))), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle(fontSize: 16, color: kInk)), if (subtitle != null && subtitle!.isNotEmpty) Padding( padding: const EdgeInsets.only(top: 4), child: Text(subtitle!, maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle(fontSize: 13, color: kMute)), ), ], ), ), ), const SizedBox(width: 8), Padding( padding: const EdgeInsets.only(top: 2), child: Column( children: [ if (status != null) StatusDot(status), if (badge != null && badge! > 0) Padding( padding: const EdgeInsets.only(top: 6), child: CircleAvatar( radius: 10, backgroundColor: kDanger, child: Text('$badge', style: const TextStyle(color: Colors.white, fontSize: 10)), ), ), if (trailing != null) trailing!, if (onTap != null) const Icon(Icons.chevron_right, color: Color(0xFFC0C0C0), size: 20), ], ), ), ], ), ), ), ); } }