import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/extensions/favourites.dart'; import 'package:aves/model/favourites.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/basic/popup/menu_row.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/fx/sweeper.dart'; import 'package:aves/widgets/common/identity/buttons/captioned_button.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class FavouriteToggler extends StatefulWidget { final Set entries; final bool isMenuItem; final FocusNode? focusNode; final VoidCallback? onPressed; const FavouriteToggler({ super.key, required this.entries, this.isMenuItem = false, this.focusNode, this.onPressed, }); @override State createState() => _FavouriteTogglerState(); } class _FavouriteTogglerState extends State { final ValueNotifier _isFavouriteNotifier = ValueNotifier(false); Set get entries => widget.entries; static const isFavouriteIcon = Icon(AIcons.favourite, fill: 1); static const isNotFavouriteIcon = Icon(AIcons.favourite, fill: 0); @override void initState() { super.initState(); favourites.addListener(_onChanged); _onChanged(); } @override void didUpdateWidget(covariant FavouriteToggler oldWidget) { super.didUpdateWidget(oldWidget); _onChanged(); } @override void dispose() { favourites.removeListener(_onChanged); _isFavouriteNotifier.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return ValueListenableBuilder( valueListenable: _isFavouriteNotifier, builder: (context, isFavourite, child) { if (widget.isMenuItem) { return isFavourite ? MenuRow( text: context.l10n.entryActionRemoveFavourite, icon: isFavouriteIcon, ) : MenuRow( text: context.l10n.entryActionAddFavourite, icon: isNotFavouriteIcon, ); } final animate = context.select((v) => v.animate); return Stack( alignment: Alignment.center, children: [ IconButton( icon: isFavourite ? isFavouriteIcon : isNotFavouriteIcon, onPressed: widget.onPressed, focusNode: widget.focusNode, tooltip: isFavourite ? context.l10n.entryActionRemoveFavourite : context.l10n.entryActionAddFavourite, ), if (animate) Sweeper( key: ValueKey(entries.length == 1 ? entries.first : entries.length), builder: (context) => Icon( AIcons.favourite, fill: 0, color: context.select((v) => v.favourite), ), toggledNotifier: _isFavouriteNotifier, ), ], ); }, ); } void _onChanged() { _isFavouriteNotifier.value = entries.isNotEmpty && entries.every((entry) => entry.isFavourite); } } class FavouriteTogglerCaption extends StatefulWidget { final Set entries; final bool enabled; const FavouriteTogglerCaption({ super.key, required this.entries, required this.enabled, }); @override State createState() => _FavouriteTogglerCaptionState(); } class _FavouriteTogglerCaptionState extends State { final ValueNotifier _isFavouriteNotifier = ValueNotifier(false); Set get entries => widget.entries; @override void initState() { super.initState(); favourites.addListener(_onChanged); _onChanged(); } @override void didUpdateWidget(covariant FavouriteTogglerCaption oldWidget) { super.didUpdateWidget(oldWidget); _onChanged(); } @override void dispose() { favourites.removeListener(_onChanged); _isFavouriteNotifier.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return ValueListenableBuilder( valueListenable: _isFavouriteNotifier, builder: (context, isFavourite, child) { return CaptionedButtonText( text: isFavourite ? context.l10n.entryActionRemoveFavourite : context.l10n.entryActionAddFavourite, enabled: widget.enabled, ); }, ); } void _onChanged() { _isFavouriteNotifier.value = entries.isNotEmpty && entries.every((entry) => entry.isFavourite); } }