171 lines
6.5 KiB
Dart
171 lines
6.5 KiB
Dart
import 'package:flutter/material.dart';
|
||
import '../models/prank_category.dart';
|
||
import '../models/prank_sound.dart';
|
||
import '../managers/data_manager.dart';
|
||
import '../managers/sound_manager.dart';
|
||
import '../widgets/big_player_panel.dart';
|
||
|
||
class CategoryDetailPage extends StatefulWidget {
|
||
final PrankCategory category;
|
||
|
||
const CategoryDetailPage({super.key, required this.category});
|
||
|
||
@override
|
||
State<CategoryDetailPage> createState() => _CategoryDetailPageState();
|
||
}
|
||
|
||
class _CategoryDetailPageState extends State<CategoryDetailPage> {
|
||
|
||
@override
|
||
void dispose() {
|
||
SoundManager().stop();
|
||
super.dispose();
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
body: Stack(
|
||
alignment: Alignment.bottomCenter,
|
||
children: [
|
||
CustomScrollView(
|
||
physics: const BouncingScrollPhysics(),
|
||
slivers: [
|
||
SliverAppBar(
|
||
expandedHeight: 200,
|
||
pinned: true,
|
||
backgroundColor: widget.category.themeColor,
|
||
leading: IconButton(
|
||
icon: const Icon(Icons.arrow_back_ios_new, color: Colors.white),
|
||
onPressed: () => Navigator.pop(context),
|
||
),
|
||
flexibleSpace: FlexibleSpaceBar(
|
||
title: Text(widget.category.categoryName),
|
||
background: Container(
|
||
decoration: BoxDecoration(
|
||
gradient: LinearGradient(
|
||
colors: [
|
||
widget.category.themeColor,
|
||
widget.category.themeColor.withOpacity(0.7),
|
||
],
|
||
begin: Alignment.topLeft,
|
||
end: Alignment.bottomRight,
|
||
),
|
||
),
|
||
child: Stack(
|
||
children: [
|
||
Positioned(
|
||
right: -30,
|
||
bottom: -30,
|
||
child: Icon(Icons.music_note, size: 200, color: Colors.white.withOpacity(0.15)),
|
||
),
|
||
Center(
|
||
child: Icon(Icons.play_circle_fill, size: 60, color: Colors.white.withOpacity(0.3)),
|
||
)
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
SliverPadding(
|
||
padding: const EdgeInsets.fromLTRB(0, 20, 0, 250),
|
||
sliver: SliverList(
|
||
delegate: SliverChildBuilderDelegate(
|
||
(context, index) {
|
||
final sound = widget.category.list[index];
|
||
return _buildSoundTile(context, sound);
|
||
},
|
||
childCount: widget.category.list.length,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
// 详情页覆盖整个屏幕,不需要避让 BottomBar,所以使用默认的 30 偏移
|
||
BigPlayerPanel(
|
||
themeColor: widget.category.themeColor,
|
||
bottomOffset: 30,
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildSoundTile(BuildContext context, PrankSound sound) {
|
||
final manager = SoundManager();
|
||
return ValueListenableBuilder<List<PrankSound>>(
|
||
valueListenable: DataManager().favoritesNotifier,
|
||
builder: (context, favorites, _) {
|
||
final isFav = sound.isFavorite;
|
||
|
||
return ValueListenableBuilder<PrankSound?>(
|
||
valueListenable: manager.currentSoundNotifier,
|
||
builder: (context, currentSound, _) {
|
||
final bool isActive = currentSound == sound;
|
||
return Container(
|
||
margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
|
||
decoration: BoxDecoration(
|
||
color: isActive ? widget.category.themeColor.withOpacity(0.05) : Colors.white,
|
||
border: isActive ? Border.all(color: widget.category.themeColor, width: 2) : null,
|
||
borderRadius: BorderRadius.circular(20),
|
||
boxShadow: [
|
||
BoxShadow(
|
||
color: Colors.black.withOpacity(0.02),
|
||
blurRadius: 10,
|
||
offset: const Offset(0, 4),
|
||
),
|
||
],
|
||
),
|
||
child: Material(
|
||
color: Colors.transparent,
|
||
child: InkWell(
|
||
borderRadius: BorderRadius.circular(20),
|
||
onTap: () => manager.play(sound),
|
||
child: Padding(
|
||
padding: const EdgeInsets.all(12),
|
||
child: Row(
|
||
children: [
|
||
Container(
|
||
width: 50,
|
||
height: 50,
|
||
decoration: BoxDecoration(
|
||
color: widget.category.themeColor.withOpacity(0.1),
|
||
borderRadius: BorderRadius.circular(15),
|
||
),
|
||
child: isActive
|
||
? Icon(Icons.equalizer, color: widget.category.themeColor)
|
||
: Icon(Icons.music_note, color: widget.category.themeColor),
|
||
),
|
||
const SizedBox(width: 16),
|
||
Expanded(
|
||
child: Text(
|
||
sound.title,
|
||
style: TextStyle(
|
||
fontWeight: isActive ? FontWeight.bold : FontWeight.normal,
|
||
fontSize: 16,
|
||
color: isActive ? widget.category.themeColor : const Color(0xFF1D1D35),
|
||
),
|
||
),
|
||
),
|
||
IconButton(
|
||
icon: Icon(
|
||
isFav ? Icons.favorite_rounded : Icons.favorite_border_rounded,
|
||
color: isFav ? const Color(0xFFFF6584) : Colors.grey[300],
|
||
),
|
||
onPressed: () {
|
||
DataManager().toggleFavorite(sound);
|
||
},
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
},
|
||
);
|
||
}
|
||
);
|
||
}
|
||
}
|