229 lines
7.2 KiB
Dart
229 lines
7.2 KiB
Dart
// Author: fruit.com.cn
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
|
|
import '../../core/data/services/favorite_controller.dart';
|
|
import '../../core/theme/app_colors.dart';
|
|
import '../../l10n/app_localizations.dart';
|
|
|
|
enum FollowingTab { favorites, history }
|
|
|
|
/// 编辑底栏的全选与取消动作。
|
|
class FollowingEditActions {
|
|
const FollowingEditActions({
|
|
required this.onToggleAll,
|
|
required this.onRemove,
|
|
});
|
|
|
|
final void Function(Set<String> ids) onToggleAll;
|
|
final VoidCallback onRemove;
|
|
}
|
|
|
|
const double _barHeight = 48;
|
|
const double _barHorizontal = 16;
|
|
const double _barFontSize = 13;
|
|
const double _barButtonHeight = 30;
|
|
const double _removeDialogButtonHeight = 32;
|
|
const double _removeDialogRadius = 6;
|
|
const double _removeDialogBottomInset = 8;
|
|
|
|
/// 播放历史移除确认。只把移除按钮压矮。
|
|
Future<bool> confirmHistoryRemove(BuildContext context) async {
|
|
final l10n = AppLocalizations.of(context)!;
|
|
return await showDialog<bool>(
|
|
context: context,
|
|
builder: (dialogContext) => AlertDialog(
|
|
actionsPadding: const EdgeInsets.fromLTRB(
|
|
24,
|
|
0,
|
|
16,
|
|
_removeDialogBottomInset,
|
|
),
|
|
content: Text(l10n.confirmRemove),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(dialogContext, false),
|
|
child: Text(l10n.cancel),
|
|
),
|
|
FilledButton(
|
|
style: FilledButton.styleFrom(
|
|
visualDensity: VisualDensity.compact,
|
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
minimumSize: const Size(0, _removeDialogButtonHeight),
|
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(_removeDialogRadius),
|
|
),
|
|
),
|
|
onPressed: () => Navigator.pop(dialogContext, true),
|
|
child: Text(l10n.remove),
|
|
),
|
|
],
|
|
),
|
|
) ??
|
|
false;
|
|
}
|
|
|
|
/// 确认后返回是否取消已选追剧。
|
|
Future<bool> confirmCancelFollowing(BuildContext context) async {
|
|
final l10n = AppLocalizations.of(context)!;
|
|
return await showDialog<bool>(
|
|
context: context,
|
|
builder: (dialogContext) => AlertDialog(
|
|
content: Text(l10n.confirmCancelFollowing),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(dialogContext, false),
|
|
child: Text(l10n.cancel),
|
|
),
|
|
FilledButton(
|
|
onPressed: () => Navigator.pop(dialogContext, true),
|
|
child: Text(l10n.cancelFollowing),
|
|
),
|
|
],
|
|
),
|
|
) ??
|
|
false;
|
|
}
|
|
|
|
/// 只在追剧编辑中显示底栏。
|
|
class FollowingEditSlot extends StatelessWidget {
|
|
const FollowingEditSlot({
|
|
super.key,
|
|
required this.tab,
|
|
required this.editing,
|
|
required this.favorites,
|
|
required this.selected,
|
|
required this.actions,
|
|
});
|
|
|
|
final ValueListenable<FollowingTab> tab;
|
|
final ValueListenable<bool> editing;
|
|
final FavoriteController favorites;
|
|
final ValueListenable<Set<String>> selected;
|
|
final FollowingEditActions actions;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ValueListenableBuilder<FollowingTab>(
|
|
valueListenable: tab,
|
|
builder: (context, current, _) {
|
|
return ValueListenableBuilder<bool>(
|
|
valueListenable: editing,
|
|
builder: (context, isEditing, _) {
|
|
if (!isEditing || current != FollowingTab.favorites) {
|
|
return const SizedBox.shrink();
|
|
}
|
|
return _bar();
|
|
},
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _bar() {
|
|
return ListenableBuilder(
|
|
listenable: favorites,
|
|
builder: (context, _) {
|
|
return ValueListenableBuilder<Set<String>>(
|
|
valueListenable: selected,
|
|
builder: (context, picked, _) {
|
|
final ids = favorites.cards.map((drama) => drama.id).toSet();
|
|
return FollowingEditBar(
|
|
allSelected: ids.isNotEmpty && ids.every(picked.contains),
|
|
canRemove: picked.isNotEmpty,
|
|
onToggleAll: () => actions.onToggleAll(ids),
|
|
onRemove: actions.onRemove,
|
|
);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 追剧编辑底栏:全选,以及取消已选追剧。
|
|
class FollowingEditBar extends StatelessWidget {
|
|
const FollowingEditBar({
|
|
super.key,
|
|
required this.allSelected,
|
|
required this.canRemove,
|
|
required this.onToggleAll,
|
|
required this.onRemove,
|
|
});
|
|
|
|
final bool allSelected;
|
|
final bool canRemove;
|
|
final VoidCallback onToggleAll;
|
|
final VoidCallback onRemove;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l10n = AppLocalizations.of(context)!;
|
|
return Material(
|
|
key: const Key('following-edit-bar'),
|
|
color: AppColors.card,
|
|
child: SafeArea(
|
|
top: false,
|
|
child: SizedBox(
|
|
height: _barHeight,
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: _barHorizontal),
|
|
child: Row(
|
|
children: [
|
|
TextButton(
|
|
key: const Key('following-select-all'),
|
|
style: TextButton.styleFrom(
|
|
visualDensity: VisualDensity.compact,
|
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
minimumSize: const Size(0, _barButtonHeight),
|
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
|
textStyle: const TextStyle(
|
|
fontSize: _barFontSize,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
onPressed: onToggleAll,
|
|
child: Text(
|
|
allSelected ? l10n.deselectAll : l10n.selectAll,
|
|
style: const TextStyle(
|
|
fontSize: _barFontSize,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
),
|
|
const Spacer(),
|
|
FilledButton(
|
|
key: const Key('following-cancel-follow'),
|
|
style: FilledButton.styleFrom(
|
|
visualDensity: VisualDensity.compact,
|
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
minimumSize: const Size(0, _removeDialogButtonHeight),
|
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(_removeDialogRadius),
|
|
),
|
|
textStyle: const TextStyle(
|
|
fontSize: _barFontSize,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
onPressed: canRemove ? onRemove : null,
|
|
child: Text(
|
|
l10n.cancelFollowing,
|
|
style: const TextStyle(
|
|
fontSize: _barFontSize,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|