import 'package:aves/model/entry.dart'; import 'package:aves/model/favourites.dart'; import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/basic/menu.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 VoidCallback? onPressed; const FavouriteToggler({ super.key, required this.entries, this.isMenuItem = false, this.onPressed, }); @override State createState() => _FavouriteTogglerState(); } class _FavouriteTogglerState extends State { final ValueNotifier isFavouriteNotifier = ValueNotifier(false); Set get entries => widget.entries; static const isFavouriteIcon = AIcons.favouriteActive; static const isNotFavouriteIcon = AIcons.favourite; static const favouriteSweeperIcon = AIcons.favourite; @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: const Icon(isFavouriteIcon), ) : MenuRow( text: context.l10n.entryActionAddFavourite, icon: const Icon(isNotFavouriteIcon), ); } return Stack( alignment: Alignment.center, children: [ IconButton( icon: Icon(isFavourite ? isFavouriteIcon : isNotFavouriteIcon), onPressed: widget.onPressed, tooltip: isFavourite ? context.l10n.entryActionRemoveFavourite : context.l10n.entryActionAddFavourite, ), Sweeper( key: ValueKey(entries.length == 1 ? entries.first : entries.length), builder: (context) => Icon( favouriteSweeperIcon, 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); } }