Files
ReelShort/lib/pages/following/following_edit_bar.dart
T

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,
),
),
),
],
),
),
),
),
);
}
}