优化界面显示

This commit is contained in:
DelLevin-Home
2026-08-12 21:43:58 +08:00
parent e543618139
commit be3299a0b3
5 changed files with 79 additions and 48 deletions

View File

@@ -947,7 +947,7 @@ class _BookDetailPageState extends State<BookDetailPage> {
isOverlay: true, isOverlay: true,
), ),
// 关联人物 // 关联人物
WorkPeopleSection(workId: book.id, workType: 'book'), WorkPeopleSection(workId: book.id, workType: 'book', isOverlay: true),
// 简介:内部已有毛玻璃卡片 // 简介:内部已有毛玻璃卡片
if (book.summary != null && book.summary!.isNotEmpty) ...[ if (book.summary != null && book.summary!.isNotEmpty) ...[
const SizedBox(height: 12), const SizedBox(height: 12),

View File

@@ -1185,7 +1185,7 @@ class _GameDetailPageState extends State<GameDetailPage> {
isOverlay: true, isOverlay: true,
), ),
// 关联人物 // 关联人物
WorkPeopleSection(workId: game.id, workType: 'game'), WorkPeopleSection(workId: game.id, workType: 'game', isOverlay: true),
if (game.summary != null && game.summary!.isNotEmpty) ...[ if (game.summary != null && game.summary!.isNotEmpty) ...[
const SizedBox(height: 12), const SizedBox(height: 12),
_buildOverlaySummary(game), _buildOverlaySummary(game),

View File

@@ -1107,7 +1107,7 @@ class _MovieDetailPageState extends State<MovieDetailPage> {
isOverlay: true, isOverlay: true,
), ),
// 关联人物 // 关联人物
WorkPeopleSection(workId: movie.id, workType: 'movie'), WorkPeopleSection(workId: movie.id, workType: 'movie', isOverlay: true),
const SizedBox(height: 12), const SizedBox(height: 12),
// 简介:内部已有毛玻璃卡片 // 简介:内部已有毛玻璃卡片
if (movie.summary != null && movie.summary!.isNotEmpty) if (movie.summary != null && movie.summary!.isNotEmpty)

View File

@@ -15,15 +15,16 @@ class PlaylistCreatePage extends StatefulWidget {
class _PlaylistCreatePageState extends State<PlaylistCreatePage> { class _PlaylistCreatePageState extends State<PlaylistCreatePage> {
late final TextEditingController _nameController; late final TextEditingController _nameController;
late final TextEditingController _descController; late final TextEditingController _descController;
final FocusNode _nameFocusNode = FocusNode();
late String _selectedType; late String _selectedType;
bool _isSaving = false; bool _isSaving = false;
bool get _isEdit => widget.playlist != null; bool get _isEdit => widget.playlist != null;
static const _types = [ static const _types = [
('movie', '影视', Icons.movie_outlined, Colors.blue), ('movie', '影视', Icons.movie_outlined),
('book', '书籍', Icons.menu_book_outlined, Colors.teal), ('book', '书籍', Icons.menu_book_outlined),
('game', '游戏', Icons.sports_esports_outlined, Colors.orange), ('game', '游戏', Icons.sports_esports_outlined),
]; ];
@override @override
@@ -38,12 +39,21 @@ class _PlaylistCreatePageState extends State<PlaylistCreatePage> {
void dispose() { void dispose() {
_nameController.dispose(); _nameController.dispose();
_descController.dispose(); _descController.dispose();
_nameFocusNode.dispose();
super.dispose(); super.dispose();
} }
Future<void> _save() async { Future<void> _save() async {
final name = _nameController.text.trim(); final name = _nameController.text.trim();
if (name.isEmpty) return; if (name.isEmpty) {
_nameFocusNode.requestFocus();
if (mounted) {
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(const SnackBar(content: Text('请输入片单名称')));
}
return;
}
if (_isSaving) return; if (_isSaving) return;
setState(() => _isSaving = true); setState(() => _isSaving = true);
@@ -80,15 +90,21 @@ class _PlaylistCreatePageState extends State<PlaylistCreatePage> {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(_isEdit ? '编辑片单' : '创建片单'), title: Text(_isEdit ? '编辑片单' : '创建片单'),
actions: [ ),
TextButton( bottomNavigationBar: SafeArea(
child: Padding(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 12),
child: FilledButton(
onPressed: _isSaving ? null : _save, onPressed: _isSaving ? null : _save,
child: Text(_isEdit ? '保存' : '创建', style: TextStyle( style: FilledButton.styleFrom(
color: _nameController.text.trim().isEmpty ? colors.onSurface.withValues(alpha: 0.3) : colors.primary, minimumSize: const Size.fromHeight(48),
fontWeight: FontWeight.w600, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
)), ),
child: _isSaving
? const SizedBox(width: 22, height: 22, child: CircularProgressIndicator(strokeWidth: 2.5))
: Text(_isEdit ? '保存' : '创建', style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
), ),
], ),
), ),
body: ListView( body: ListView(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
@@ -97,36 +113,45 @@ class _PlaylistCreatePageState extends State<PlaylistCreatePage> {
if (!_isEdit) ...[ if (!_isEdit) ...[
Text('片单类型', style: TextStyle(fontSize: 13, fontWeight: FontWeight.w500, color: colors.onSurface.withValues(alpha: 0.6))), Text('片单类型', style: TextStyle(fontSize: 13, fontWeight: FontWeight.w500, color: colors.onSurface.withValues(alpha: 0.6))),
const SizedBox(height: 10), const SizedBox(height: 10),
Row( Container(
children: _types.map((t) { padding: const EdgeInsets.all(2),
final (type, label, icon, color) = t; decoration: BoxDecoration(
final selected = _selectedType == type; color: colors.surfaceContainerHighest,
return Expanded( borderRadius: BorderRadius.circular(8),
child: GestureDetector( ),
onTap: () => setState(() => _selectedType = type), child: Row(
child: Container( children: _types.map((t) {
margin: const EdgeInsets.symmetric(horizontal: 4), final (type, label, icon) = t;
padding: const EdgeInsets.symmetric(vertical: 12), final selected = _selectedType == type;
decoration: BoxDecoration( return Expanded(
color: selected ? color.withValues(alpha: 0.1) : colors.surfaceContainerHighest, child: GestureDetector(
borderRadius: BorderRadius.circular(10), behavior: HitTestBehavior.opaque,
border: selected ? Border.all(color: color.withValues(alpha: 0.3)) : null, onTap: () => setState(() => _selectedType = type),
), child: AnimatedContainer(
child: Column( duration: const Duration(milliseconds: 150),
children: [ curve: Curves.easeOut,
Icon(icon, size: 22, color: selected ? color : colors.onSurface.withValues(alpha: 0.4)), padding: const EdgeInsets.symmetric(vertical: 12),
const SizedBox(height: 6), decoration: BoxDecoration(
Text(label, style: TextStyle( color: selected ? colors.surface : Colors.transparent,
fontSize: 12, borderRadius: BorderRadius.circular(6),
fontWeight: selected ? FontWeight.w600 : FontWeight.normal, ),
color: selected ? color : colors.onSurface.withValues(alpha: 0.5), child: Row(
)), mainAxisAlignment: MainAxisAlignment.center,
], children: [
Icon(icon, size: 16, color: selected ? colors.primary : colors.onSurface.withValues(alpha: 0.4)),
const SizedBox(width: 6),
Text(label, style: TextStyle(
fontSize: 13,
fontWeight: selected ? FontWeight.w500 : FontWeight.normal,
color: selected ? colors.onSurface : colors.onSurface.withValues(alpha: 0.4),
)),
],
),
), ),
), ),
), );
); }).toList(),
}).toList(), ),
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
], ],
@@ -135,6 +160,8 @@ class _PlaylistCreatePageState extends State<PlaylistCreatePage> {
const SizedBox(height: 8), const SizedBox(height: 8),
TextField( TextField(
controller: _nameController, controller: _nameController,
focusNode: _nameFocusNode,
autofocus: !_isEdit,
maxLength: 30, maxLength: 30,
decoration: InputDecoration( decoration: InputDecoration(
hintText: '输入片单名称', hintText: '输入片单名称',

View File

@@ -11,11 +11,13 @@ import 'person_info_sheet.dart';
class WorkPeopleSection extends StatefulWidget { class WorkPeopleSection extends StatefulWidget {
final String workId; final String workId;
final String workType; // 'movie' / 'book' / 'game' final String workType; // 'movie' / 'book' / 'game'
final bool isOverlay; // 叠层模式(深色毛玻璃背景)下用白色文字
const WorkPeopleSection({ const WorkPeopleSection({
super.key, super.key,
required this.workId, required this.workId,
required this.workType, required this.workType,
this.isOverlay = false,
}); });
@override @override
@@ -170,6 +172,8 @@ class _WorkPeopleSectionState extends State<WorkPeopleSection> {
if (_items.isEmpty) return const SizedBox.shrink(); if (_items.isEmpty) return const SizedBox.shrink();
final colors = Theme.of(context).colorScheme; final colors = Theme.of(context).colorScheme;
final primaryColor = widget.isOverlay ? Colors.white : colors.onSurface;
final secondaryColor = widget.isOverlay ? Colors.white.withValues(alpha: 0.5) : colors.onSurface.withValues(alpha: 0.4);
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 20), padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 20),
@@ -182,7 +186,7 @@ class _WorkPeopleSectionState extends State<WorkPeopleSection> {
width: 4, width: 4,
height: 16, height: 16,
decoration: BoxDecoration( decoration: BoxDecoration(
color: colors.onSurface, color: primaryColor,
borderRadius: BorderRadius.circular(2), borderRadius: BorderRadius.circular(2),
), ),
), ),
@@ -192,7 +196,7 @@ class _WorkPeopleSectionState extends State<WorkPeopleSection> {
style: TextStyle( style: TextStyle(
fontSize: 15, fontSize: 15,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: colors.onSurface, color: primaryColor,
), ),
), ),
], ],
@@ -220,7 +224,7 @@ class _WorkPeopleSectionState extends State<WorkPeopleSection> {
final idx = _items.indexOf(item); final idx = _items.indexOf(item);
return Padding( return Padding(
padding: EdgeInsets.only(left: idx == 0 ? 0 : 16), padding: EdgeInsets.only(left: idx == 0 ? 0 : 16),
child: _buildPersonChip(item, colors), child: _buildPersonChip(item, primaryColor, secondaryColor),
); );
}).toList(), }).toList(),
), ),
@@ -231,7 +235,7 @@ class _WorkPeopleSectionState extends State<WorkPeopleSection> {
); );
} }
Widget _buildPersonChip(_PersonRole item, ColorScheme colors) { Widget _buildPersonChip(_PersonRole item, Color primaryColor, Color secondaryColor) {
final person = item.person; final person = item.person;
return GestureDetector( return GestureDetector(
onTap: () => PersonInfoSheet.show(context, person), onTap: () => PersonInfoSheet.show(context, person),
@@ -248,7 +252,7 @@ class _WorkPeopleSectionState extends State<WorkPeopleSection> {
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
person.name, person.name,
style: TextStyle(fontSize: 12, fontWeight: FontWeight.w500, color: colors.onSurface), style: TextStyle(fontSize: 12, fontWeight: FontWeight.w500, color: primaryColor),
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center, textAlign: TextAlign.center,
@@ -258,7 +262,7 @@ class _WorkPeopleSectionState extends State<WorkPeopleSection> {
_buildRoleText(item), _buildRoleText(item),
style: TextStyle( style: TextStyle(
fontSize: 10, fontSize: 10,
color: colors.onSurface.withValues(alpha: 0.4), color: secondaryColor,
height: 1.3, height: 1.3,
), ),
maxLines: 2, maxLines: 2,