import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../models/character_pack.dart'; import '../providers/character_provider.dart'; import '../providers/chat_provider.dart'; import '../theme/app_colors.dart'; class CharactersScreen extends StatefulWidget { const CharactersScreen({super.key}); @override State createState() => _CharactersScreenState(); } class _CharactersScreenState extends State { @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { context.read().refresh(); }); } IconData _iconFor(CharacterPack pack) { switch (pack.iconHint) { case IconDataLike.person: return Icons.person_rounded; case IconDataLike.mic: return Icons.mic_rounded; case IconDataLike.sparkle: return Icons.auto_awesome_rounded; case IconDataLike.pets: return Icons.pets_rounded; } } @override Widget build(BuildContext context) { final provider = context.watch(); return Scaffold( appBar: AppBar( title: const Text('角色'), actions: [ IconButton( onPressed: provider.isLoading ? null : () => provider.refresh(), icon: const Icon(Icons.refresh_rounded), ), ], ), body: RefreshIndicator( onRefresh: provider.refresh, child: ListView( padding: const EdgeInsets.all(AppSpacing.md), children: [ if (provider.error != null) Padding( padding: const EdgeInsets.only(bottom: AppSpacing.md), child: Text( provider.error!, style: const TextStyle(color: AppColors.error), ), ), Text( '选择角色内容包,设备画面与对话人设会同步切换。', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: AppColors.textSecondary, ), ), const SizedBox(height: AppSpacing.md), if (provider.isLoading && provider.packs.isEmpty) const Padding( padding: EdgeInsets.only(top: 48), child: Center(child: CircularProgressIndicator()), ) else if (provider.packs.isEmpty) const Padding( padding: EdgeInsets.only(top: 48), child: Center(child: Text('暂无角色配置包')), ) else ...provider.packs.map((pack) { final active = pack.filename == provider.activeFilename; return Card( margin: const EdgeInsets.only(bottom: AppSpacing.md), child: InkWell( borderRadius: BorderRadius.circular(AppRadius.large), onTap: provider.isSwitching || active ? null : () async { final ok = await provider.switchTo(pack.filename); if (!context.mounted) return; if (ok) { // 切换角色清空对话上下文(PRD §2.4) context.read().clearHistory(); } ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( ok ? '已切换到 ${pack.name}' : (provider.error ?? '切换失败'), ), ), ); }, child: Padding( padding: const EdgeInsets.all(AppSpacing.md), child: Row( children: [ CircleAvatar( radius: 28, backgroundColor: active ? AppColors.primary.withValues(alpha: 0.2) : AppColors.background, child: Icon( _iconFor(pack), color: active ? AppColors.primary : AppColors.textSecondary, ), ), const SizedBox(width: AppSpacing.md), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Flexible( child: Text( pack.name, style: Theme.of(context) .textTheme .titleMedium, ), ), if (active) ...[ const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 2, ), decoration: BoxDecoration( color: AppColors.success .withValues(alpha: 0.15), borderRadius: BorderRadius.circular(999), ), child: const Text( '使用中', style: TextStyle( color: AppColors.success, fontSize: 12, ), ), ), ], ], ), const SizedBox(height: 4), Text( '${pack.typeLabel} · ${pack.renderType} · ${pack.filename}', style: Theme.of(context) .textTheme .bodySmall ?.copyWith( color: AppColors.textSecondary, ), ), ], ), ), if (provider.isSwitching && !active) const SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ) else Icon( active ? Icons.check_circle_rounded : Icons.chevron_right_rounded, color: active ? AppColors.success : AppColors.textSecondary, ), ], ), ), ), ); }), ], ), ), ); } }