Initial commit
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:forui/forui.dart';
|
||||
|
||||
import '../../../providers/danmaku_settings_provider.dart';
|
||||
import '../../../shared/constants/breakpoints.dart';
|
||||
import '../../../shared/widgets/adaptive_modal.dart';
|
||||
|
||||
void showBlockedKeywordsDialog(
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
List<String> keywords,
|
||||
) {
|
||||
showAdaptiveModal<void>(
|
||||
context: context,
|
||||
maxWidth: 420,
|
||||
compactMaxHeightFactor: 0.9,
|
||||
builder: (ctx) => _BlockedKeywordsDialog(
|
||||
keywords: keywords,
|
||||
onAdd: (kw) =>
|
||||
ref.read(danmakuSettingsProvider.notifier).addBlockedKeyword(kw),
|
||||
onRemove: (kw) =>
|
||||
ref.read(danmakuSettingsProvider.notifier).removeBlockedKeyword(kw),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class _BlockedKeywordsDialog extends StatefulWidget {
|
||||
final List<String> keywords;
|
||||
final ValueChanged<String> onAdd;
|
||||
final ValueChanged<String> onRemove;
|
||||
|
||||
const _BlockedKeywordsDialog({
|
||||
required this.keywords,
|
||||
required this.onAdd,
|
||||
required this.onRemove,
|
||||
});
|
||||
|
||||
@override
|
||||
State<_BlockedKeywordsDialog> createState() => _BlockedKeywordsDialogState();
|
||||
}
|
||||
|
||||
class _BlockedKeywordsDialogState extends State<_BlockedKeywordsDialog> {
|
||||
late final List<String> _keywords;
|
||||
final _controller = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_keywords = List.of(widget.keywords);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _add() {
|
||||
final text = _controller.text.trim();
|
||||
if (text.isEmpty || _keywords.contains(text)) return;
|
||||
setState(() => _keywords.add(text));
|
||||
widget.onAdd(text);
|
||||
_controller.clear();
|
||||
}
|
||||
|
||||
void _remove(String kw) {
|
||||
setState(() => _keywords.remove(kw));
|
||||
widget.onRemove(kw);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final isCompact = Breakpoints.isCompact(MediaQuery.sizeOf(context).width);
|
||||
final chipsMaxHeight =
|
||||
MediaQuery.sizeOf(context).height * (isCompact ? 0.38 : 0.34);
|
||||
|
||||
return SingleChildScrollView(
|
||||
padding: EdgeInsets.fromLTRB(24, isCompact ? 16 : 24, 24, 24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text('屏蔽关键词', style: theme.textTheme.titleLarge),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'匹配到这些关键词的弹幕将被隐藏',
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _controller,
|
||||
decoration: const InputDecoration(
|
||||
hintText: '输入关键词后回车添加',
|
||||
isDense: true,
|
||||
),
|
||||
textInputAction: TextInputAction.done,
|
||||
autocorrect: false,
|
||||
enableSuggestions: false,
|
||||
onSubmitted: (_) => _add(),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
IconButton.filledTonal(
|
||||
tooltip: '添加',
|
||||
onPressed: _add,
|
||||
icon: const Icon(Icons.add, size: 18),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
if (_keywords.isEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 24),
|
||||
child: Text(
|
||||
'暂无屏蔽关键词',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: theme.colorScheme.onSurfaceVariant),
|
||||
),
|
||||
)
|
||||
else
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(maxHeight: chipsMaxHeight),
|
||||
child: SingleChildScrollView(
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: _keywords
|
||||
.map(
|
||||
(kw) => Chip(
|
||||
label: Text(kw),
|
||||
onDeleted: () => _remove(kw),
|
||||
deleteIcon: const Icon(Icons.close, size: 14),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
if (isCompact)
|
||||
FButton(
|
||||
onPress: () => Navigator.pop(context),
|
||||
child: const Text('完成'),
|
||||
)
|
||||
else
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: FButton(
|
||||
variant: FButtonVariant.ghost,
|
||||
onPress: () => Navigator.pop(context),
|
||||
child: const Text('完成'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user