import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../providers/debug_provider.dart'; import '../theme/app_colors.dart'; class DebugScreen extends StatelessWidget { const DebugScreen({super.key}); @override Widget build(BuildContext context) { final provider = context.watch(); final entries = provider.filteredEntries; return Scaffold( appBar: AppBar( title: const Text('调试日志'), actions: [ IconButton( onPressed: provider.entries.isEmpty ? null : provider.clearLogs, icon: const Icon(Icons.delete_sweep_outlined), tooltip: '清空日志', ), ], ), body: Column( children: [ Container( width: double.infinity, padding: const EdgeInsets.fromLTRB( AppSpacing.md, AppSpacing.md, AppSpacing.md, AppSpacing.sm, ), decoration: BoxDecoration( gradient: LinearGradient( colors: [ AppColors.card, AppColors.card.withValues(alpha: 0.72), ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), border: const Border(bottom: BorderSide(color: AppColors.border)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '事件时间线', style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: AppSpacing.xs), Text( '保留最近 ${DebugProvider.maxEntries} 条日志,支持按链路筛选。', style: Theme.of(context).textTheme.bodySmall?.copyWith( color: AppColors.textSecondary, ), ), const SizedBox(height: AppSpacing.md), Wrap( spacing: AppSpacing.sm, runSpacing: AppSpacing.sm, children: [ _FilterChipItem( label: '全部', selected: provider.filter == DebugLogFilter.all, color: AppColors.textSecondary, onTap: () => provider.setFilter(DebugLogFilter.all), ), _FilterChipItem( label: 'BLE', selected: provider.filter == DebugLogFilter.ble, color: AppColors.info, onTap: () => provider.setFilter(DebugLogFilter.ble), ), _FilterChipItem( label: 'WS', selected: provider.filter == DebugLogFilter.ws, color: AppColors.success, onTap: () => provider.setFilter(DebugLogFilter.ws), ), _FilterChipItem( label: 'HTTP', selected: provider.filter == DebugLogFilter.http, color: AppColors.warning, onTap: () => provider.setFilter(DebugLogFilter.http), ), ], ), ], ), ), Expanded( child: entries.isEmpty ? const _EmptyDebugState() : ListView.separated( padding: const EdgeInsets.all(AppSpacing.md), itemCount: entries.length, separatorBuilder: (_, __) => const SizedBox(height: AppSpacing.sm), itemBuilder: (context, index) { return _DebugLogCard(entry: entries[index]); }, ), ), ], ), ); } } class _FilterChipItem extends StatelessWidget { const _FilterChipItem({ required this.label, required this.selected, required this.color, required this.onTap, }); final String label; final bool selected; final Color color; final VoidCallback onTap; @override Widget build(BuildContext context) { return FilterChip( label: Text(label), selected: selected, onSelected: (_) => onTap(), showCheckmark: false, selectedColor: color.withValues(alpha: 0.18), side: BorderSide(color: selected ? color : AppColors.border), labelStyle: Theme.of(context).textTheme.labelLarge?.copyWith( color: selected ? color : AppColors.textSecondary, fontWeight: FontWeight.w600, ), backgroundColor: AppColors.card, ); } } class _DebugLogCard extends StatelessWidget { const _DebugLogCard({required this.entry}); final DebugLogEntry entry; @override Widget build(BuildContext context) { final color = _typeColor(entry.type); return Container( decoration: BoxDecoration( color: AppColors.card, borderRadius: BorderRadius.circular(AppRadius.large), border: Border.all(color: AppColors.border), ), child: IntrinsicHeight( child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Container( width: 4, decoration: BoxDecoration( color: color, borderRadius: const BorderRadius.only( topLeft: Radius.circular(AppRadius.large), bottomLeft: Radius.circular(AppRadius.large), ), ), ), Expanded( child: Padding( padding: const EdgeInsets.all(AppSpacing.md), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( padding: const EdgeInsets.symmetric( horizontal: AppSpacing.sm, vertical: AppSpacing.xs, ), decoration: BoxDecoration( color: color.withValues(alpha: 0.14), borderRadius: BorderRadius.circular(999), border: Border.all( color: color.withValues(alpha: 0.35), ), ), child: Text( entry.label, style: Theme.of(context).textTheme.labelMedium?.copyWith( color: color, fontWeight: FontWeight.w700, ), ), ), const Spacer(), Text( _formatTimestamp(entry.timestamp), style: Theme.of(context).textTheme.bodySmall?.copyWith( color: AppColors.textSecondary, ), ), ], ), const SizedBox(height: AppSpacing.sm), Text( entry.summary, style: Theme.of(context).textTheme.bodyLarge, ), if (entry.details != null && entry.details!.isNotEmpty) ...[ const SizedBox(height: AppSpacing.sm), Text( entry.details!, maxLines: 3, overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.bodySmall?.copyWith( color: AppColors.textSecondary, ), ), ], ], ), ), ), ], ), ), ); } Color _typeColor(DebugLogType type) { switch (type) { case DebugLogType.ble: return AppColors.info; case DebugLogType.ws: return AppColors.success; case DebugLogType.http: return AppColors.warning; } } String _formatTimestamp(DateTime timestamp) { final hh = timestamp.hour.toString().padLeft(2, '0'); final mm = timestamp.minute.toString().padLeft(2, '0'); final ss = timestamp.second.toString().padLeft(2, '0'); final ms = timestamp.millisecond.toString().padLeft(3, '0'); return '$hh:$mm:$ss.$ms'; } } class _EmptyDebugState extends StatelessWidget { const _EmptyDebugState(); @override Widget build(BuildContext context) { return Center( child: Padding( padding: const EdgeInsets.all(AppSpacing.xl), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 72, height: 72, decoration: BoxDecoration( gradient: LinearGradient( colors: [ AppColors.info.withValues(alpha: 0.24), AppColors.success.withValues(alpha: 0.16), ], ), borderRadius: BorderRadius.circular(24), ), child: const Icon(Icons.bug_report_outlined, size: 34), ), const SizedBox(height: AppSpacing.md), Text( '当前没有调试事件', style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: AppSpacing.xs), Text( '连接设备、触发播放、执行网络或 BLE 操作后,日志会按时间顺序出现在这里。', textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: AppColors.textSecondary, ), ), ], ), ), ); } }