generated from dellevin/template
角色信息添加刷新按钮
This commit is contained in:
@@ -18,6 +18,13 @@ class _PersonListPageState extends State<PersonListPage> {
|
||||
String _filter = 'all'; // all / 导演 / 编剧 / 主演 / 作者
|
||||
String _searchQuery = '';
|
||||
final _searchController = TextEditingController();
|
||||
bool _loading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_refresh();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@@ -25,6 +32,16 @@ class _PersonListPageState extends State<PersonListPage> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _refresh() async {
|
||||
setState(() => _loading = true);
|
||||
final provider = context.read<AppProvider>();
|
||||
await Future.wait([
|
||||
provider.loadMovies(),
|
||||
provider.loadBooks(),
|
||||
]);
|
||||
if (mounted) setState(() => _loading = false);
|
||||
}
|
||||
|
||||
List<_PersonEntry> _buildPersons() {
|
||||
final provider = context.read<AppProvider>();
|
||||
final map = <String, _PersonEntry>{};
|
||||
@@ -61,7 +78,6 @@ class _PersonListPageState extends State<PersonListPage> {
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
final allPersons = _buildPersons();
|
||||
|
||||
// 过滤
|
||||
var filtered = allPersons.where((p) {
|
||||
if (_filter != 'all' && !p.roles.contains(_filter)) return false;
|
||||
if (_searchQuery.isNotEmpty && !p.name.toLowerCase().contains(_searchQuery.toLowerCase())) return false;
|
||||
@@ -70,7 +86,17 @@ class _PersonListPageState extends State<PersonListPage> {
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: colors.surface,
|
||||
appBar: AppBar(title: const Text('角色信息')),
|
||||
appBar: AppBar(
|
||||
title: const Text('角色信息'),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: _loading
|
||||
? SizedBox(width: 18, height: 18, child: CircularProgressIndicator(strokeWidth: 2, color: colors.onSurface.withValues(alpha: 0.5)))
|
||||
: const Icon(Icons.refresh),
|
||||
onPressed: _loading ? null : _refresh,
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
// 搜索栏
|
||||
@@ -184,7 +210,6 @@ class _PersonListPageState extends State<PersonListPage> {
|
||||
'作者': const Color(0xFF7E57C2),
|
||||
};
|
||||
final totalWorks = person.movies.length + person.books.length;
|
||||
|
||||
return ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 4),
|
||||
leading: CircleAvatar(
|
||||
@@ -223,7 +248,7 @@ class _PersonListPageState extends State<PersonListPage> {
|
||||
}
|
||||
}
|
||||
|
||||
// ─── 人物详情页 ───
|
||||
// ─── 人物详情页(只读展示)───
|
||||
|
||||
class _PersonDetailPage extends StatelessWidget {
|
||||
final _PersonEntry person;
|
||||
@@ -239,7 +264,6 @@ class _PersonDetailPage extends StatelessWidget {
|
||||
'作者': const Color(0xFF7E57C2),
|
||||
};
|
||||
|
||||
// 按类型分组作品
|
||||
final movieItems = <_WorkItem>[];
|
||||
final bookItems = <_WorkItem>[];
|
||||
|
||||
@@ -318,7 +342,6 @@ class _PersonDetailPage extends StatelessWidget {
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
// 封面
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
child: SizedBox(
|
||||
@@ -333,7 +356,6 @@ class _PersonDetailPage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
// 标题 + 角色
|
||||
Expanded(
|
||||
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
Text(item.title, style: TextStyle(fontSize: 13, fontWeight: FontWeight.w500, color: colors.onSurface),
|
||||
|
||||
@@ -122,6 +122,7 @@ class AppProvider extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
|
||||
// ─── 分页加载(供列表页触底加载使用)────────────────────────
|
||||
static const int _pageSize = 20;
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ class DatabaseHelper {
|
||||
|
||||
return await openDatabase(
|
||||
path,
|
||||
version: 25,
|
||||
version: 26,
|
||||
onCreate: _createDB,
|
||||
onUpgrade: _onUpgrade,
|
||||
);
|
||||
|
||||
@@ -105,6 +105,47 @@ class _GenreSelectorPageState extends State<GenreSelectorPage> {
|
||||
}
|
||||
}
|
||||
|
||||
void _editItem(int index, String oldValue) {
|
||||
final editController = TextEditingController(text: oldValue);
|
||||
showDialog<String>(
|
||||
context: context,
|
||||
builder: (ctx) {
|
||||
final colors = Theme.of(ctx).colorScheme;
|
||||
return AlertDialog(
|
||||
backgroundColor: colors.surface,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
title: Text('编辑', style: TextStyle(fontSize: 17, fontWeight: FontWeight.w600, color: colors.onSurface)),
|
||||
content: TextField(
|
||||
controller: editController,
|
||||
autofocus: true,
|
||||
style: TextStyle(fontSize: 15, color: colors.onSurface),
|
||||
cursorColor: colors.primary,
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: colors.surfaceContainerHigh,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
|
||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: BorderSide.none),
|
||||
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: colors.primary, width: 1)),
|
||||
),
|
||||
onSubmitted: (v) => Navigator.pop(ctx, v.trim()),
|
||||
),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.pop(ctx), child: Text('取消', style: TextStyle(color: colors.onSurface.withValues(alpha: 0.6)))),
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.pop(ctx, editController.text.trim()),
|
||||
style: ElevatedButton.styleFrom(backgroundColor: colors.primary, foregroundColor: colors.onPrimary, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
|
||||
child: const Text('确定'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
).then((newValue) {
|
||||
if (newValue != null && newValue.isNotEmpty && newValue != oldValue && mounted) {
|
||||
setState(() => _selected[index] = newValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
@@ -164,7 +205,8 @@ class _GenreSelectorPageState extends State<GenreSelectorPage> {
|
||||
padding: EdgeInsets.zero,
|
||||
itemCount: _selected.length,
|
||||
itemBuilder: (_, i) {
|
||||
final tag = _selected[_selected.length - 1 - i];
|
||||
final idx = _selected.length - 1 - i;
|
||||
final tag = _selected[idx];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 6),
|
||||
child: Container(
|
||||
@@ -176,10 +218,23 @@ class _GenreSelectorPageState extends State<GenreSelectorPage> {
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
leading: Icon(Icons.check_circle, size: 20, color: colors.primary),
|
||||
title: Text(tag, style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: colors.onSurface)),
|
||||
trailing: GestureDetector(
|
||||
onTap: () => _toggle(tag),
|
||||
child: Icon(Icons.close, size: 18, color: colors.onSurface.withValues(alpha: 0.35)),
|
||||
title: GestureDetector(
|
||||
onTap: () => _editItem(idx, tag),
|
||||
child: Text(tag, style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: colors.onSurface)),
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => _editItem(idx, tag),
|
||||
child: Icon(Icons.edit, size: 16, color: colors.onSurface.withValues(alpha: 0.3)),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
GestureDetector(
|
||||
onTap: () => _toggle(tag),
|
||||
child: Icon(Icons.close, size: 18, color: colors.onSurface.withValues(alpha: 0.35)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// 右侧滑入文本输入弹窗(单行编辑用,如影视名称、书籍名称)
|
||||
/// 右侧滑入文本输入弹窗(单行/多行编辑用,如影视名称、书籍名称、简介)
|
||||
class TextInputPanel extends StatefulWidget {
|
||||
final String title;
|
||||
final String initialValue;
|
||||
final String hint;
|
||||
final TextInputType keyboardType;
|
||||
final int maxLines;
|
||||
|
||||
const TextInputPanel({
|
||||
super.key,
|
||||
@@ -13,6 +14,7 @@ class TextInputPanel extends StatefulWidget {
|
||||
this.initialValue = '',
|
||||
this.hint = '',
|
||||
this.keyboardType = TextInputType.text,
|
||||
this.maxLines = 1,
|
||||
});
|
||||
|
||||
static Future<String?> show({
|
||||
@@ -21,6 +23,7 @@ class TextInputPanel extends StatefulWidget {
|
||||
String initialValue = '',
|
||||
String hint = '',
|
||||
TextInputType keyboardType = TextInputType.text,
|
||||
int maxLines = 1,
|
||||
}) {
|
||||
return showGeneralDialog<String>(
|
||||
context: context,
|
||||
@@ -40,6 +43,7 @@ class TextInputPanel extends StatefulWidget {
|
||||
initialValue: initialValue,
|
||||
hint: hint,
|
||||
keyboardType: keyboardType,
|
||||
maxLines: maxLines,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -107,7 +111,8 @@ class _TextInputPanelState extends State<TextInputPanel> {
|
||||
child: TextField(
|
||||
controller: _controller,
|
||||
autofocus: true,
|
||||
keyboardType: widget.keyboardType,
|
||||
keyboardType: widget.maxLines > 1 ? TextInputType.multiline : widget.keyboardType,
|
||||
maxLines: widget.maxLines,
|
||||
style: TextStyle(fontSize: 15, color: colors.onSurface),
|
||||
cursorColor: colors.primary,
|
||||
decoration: InputDecoration(
|
||||
|
||||
Reference in New Issue
Block a user