Files
app/wei_ai_app/lib/screens/library/library_screen.dart
2026-01-28 19:10:19 +08:00

326 lines
12 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:go_router/go_router.dart';
import '../../data/mock_data.dart';
import '../../models/scenario.dart';
import '../../widgets/tab_content_layout.dart';
class LibraryScreen extends StatefulWidget {
const LibraryScreen({super.key});
@override
State<LibraryScreen> createState() => _LibraryScreenState();
}
class _LibraryScreenState extends State<LibraryScreen> {
String _activeCategory = '全部';
final List<String> _categories = ['全部', '职场', '邻家', '科幻', 'ASMR'];
List<Scenario> get _filteredScenarios {
if (_activeCategory == '全部') return mockScenarios;
return mockScenarios.where((s) =>
s.category == _activeCategory || s.tags.contains(_activeCategory)
).toList();
}
Color _getIntensityColor(String intensity) {
switch (intensity) {
case 'Low':
return const Color(0xFF34D399); // Emerald
case 'Medium':
return const Color(0xFF60A5FA); // Blue
case 'High':
return const Color(0xFFFBBF24); // Amber
case 'Extreme':
return const Color(0xFFF472B6); // Pink
default:
return Colors.white.withOpacity(0.5);
}
}
@override
Widget build(BuildContext context) {
const double bottomNavHeight = 90;
return TabContentLayout(
child: Column(
children: [
// Filter Bar
SizedBox(
height: 50,
child: ListView.separated(
padding: const EdgeInsets.symmetric(horizontal: 16),
scrollDirection: Axis.horizontal,
itemCount: _categories.length,
separatorBuilder: (_, __) => const SizedBox(width: 12),
itemBuilder: (context, index) {
final category = _categories[index];
final isActive = _activeCategory == category;
return Center(
child: GestureDetector(
onTap: () => setState(() => _activeCategory = category),
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
decoration: BoxDecoration(
gradient: isActive
? const LinearGradient(
colors: [Color(0xFFC084FC), Color(0xFFF472B6)],
)
: null,
color: isActive ? null : Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: isActive ? Colors.transparent : Colors.white.withOpacity(0.1),
),
boxShadow: isActive
? [
BoxShadow(
color: const Color(0xFFC084FC).withOpacity(0.4),
blurRadius: 15,
),
]
: null,
),
child: Text(
category,
style: TextStyle(
fontSize: 14,
fontWeight: isActive ? FontWeight.bold : FontWeight.normal,
color: isActive ? Colors.white : Colors.white.withOpacity(0.7),
),
),
),
),
);
},
),
),
const SizedBox(height: 8),
// Scenario List
Expanded(
child: _filteredScenarios.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(LucideIcons.activity, size: 32, color: Colors.white.withOpacity(0.5)),
const SizedBox(height: 8),
Text(
'暂无相关剧本',
style: TextStyle(fontSize: 12, color: Colors.white.withOpacity(0.5)),
),
],
),
)
: ListView.builder(
padding: EdgeInsets.fromLTRB(16, 8, 16, bottomNavHeight + 20),
itemCount: _filteredScenarios.length,
itemBuilder: (context, index) {
final scenario = _filteredScenarios[index];
return _ScenarioCard(
scenario: scenario,
intensityColor: _getIntensityColor(scenario.intensity),
onTap: () {
if (!scenario.isLocked) {
context.push('/player/${scenario.id}');
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('需要积分解锁此高级内容'),
backgroundColor: Color(0xFF4C1D95),
),
);
}
},
)
.animate()
.fadeIn(duration: 400.ms, delay: (index * 80).ms)
.slideX(begin: 0.05, end: 0);
},
),
),
],
),
);
}
}
class _ScenarioCard extends StatelessWidget {
final Scenario scenario;
final Color intensityColor;
final VoidCallback onTap;
const _ScenarioCard({
required this.scenario,
required this.intensityColor,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFF4C1D95).withOpacity(0.2),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: Row(
children: [
// Left: Thumbnail
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.white.withOpacity(0.1)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.3),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
clipBehavior: Clip.antiAlias,
child: Stack(
fit: StackFit.expand,
children: [
Image.network(
scenario.cover,
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Container(
color: Colors.black26,
child: const Center(
child: CircularProgressIndicator(strokeWidth: 2),
),
);
},
),
if (scenario.isLocked)
Container(
color: const Color(0xFF2E1065).withOpacity(0.6),
child: const Center(
child: Icon(LucideIcons.lock, size: 16, color: Colors.white70),
),
),
],
),
),
const SizedBox(width: 16),
// Center: Info
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Category & Intensity
Row(
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
borderRadius: BorderRadius.circular(4),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: Text(
scenario.category,
style: TextStyle(
fontSize: 10,
fontFamily: 'monospace',
letterSpacing: 1,
color: Colors.white.withOpacity(0.9),
),
),
),
const SizedBox(width: 8),
Icon(LucideIcons.zap, size: 10, color: intensityColor),
const SizedBox(width: 2),
Text(
scenario.intensity,
style: TextStyle(
fontSize: 9,
fontWeight: FontWeight.bold,
color: intensityColor,
),
),
],
),
const SizedBox(height: 6),
// Title
Text(
scenario.title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 6),
// Tags
Wrap(
spacing: 6,
children: scenario.tags.take(3).map((tag) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(4),
border: Border.all(color: Colors.white.withOpacity(0.05)),
),
child: Text(
'#$tag',
style: TextStyle(
fontSize: 10,
color: Colors.white.withOpacity(0.7),
),
),
);
}).toList(),
),
],
),
),
// Right: Play Button
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: scenario.isLocked
? Colors.white.withOpacity(0.05)
: const Color(0xFFC084FC).withOpacity(0.2),
),
child: Center(
child: Icon(
scenario.isLocked ? LucideIcons.lock : LucideIcons.play,
size: scenario.isLocked ? 16 : 18,
color: scenario.isLocked
? Colors.white.withOpacity(0.3)
: const Color(0xFFC084FC),
),
),
),
],
),
),
);
}
}