import 'dart:async'; import 'dart:math'; import 'package:flutter/widgets.dart'; import 'package:mnemo_cards/features/packs/images_holder.dart'; import 'package:mnemo_cards_common/mnemo_cards_common.dart'; import 'package:rxdart/rxdart.dart'; import '../../di/locator.dart'; class CardsViewController extends ChangeNotifier { bool _expanded = false; bool _favorite = false; double imageWidth = 0.0; List cards = []; final CardPackDto cardPack; final ScrollController _cardsScrollController = ScrollController(keepScrollOffset: true); ScrollController get cardsScrollController => _cardsScrollController; StreamController _streamController = StreamController.broadcast(); Stream get asStream => _streamController.stream.startWith(state).distinct(); Stream get expandedStream => asStream.map((s) => s.expanded).distinct(); Stream get favoriteStream => asStream.map((s) => s.favorite).distinct(); Stream> get cardsStream => asStream.map((s) => s.packCards).distinct((p, n) => (p.length == n.length) && p.indexed.every((p) => n[p.$1].id == p.$2.id)); CardsViewState get state => CardsViewState( expanded: _expanded, favorite: _favorite, packCards: cards, ); List _filterPackCards() => cardPack.cards .where( (card) => !_favorite || locator.favoriteCardsController.isFavoriteCard(card.id), ) .toList(); void setExpanded(bool expanded) { if (cards.isNotEmpty) { _expanded = expanded; notifyListeners(); } } void setFavorite(bool favorite) { _favorite = favorite; _updateCards(); notifyListeners(); } void _addToStream() { _streamController.add(state); } double get offset => _cardsScrollController.offset; int get offsetCards => (_cardsScrollController.offset / imageWidth).floor(); void toggleExpanded() => setExpanded(!_expanded); CardsViewController(this.cardPack) { addListener(_addToStream); _updateCards(); } void _updateCards() { cards = _filterPackCards(); notifyListeners(); } Future animateTo(int index) { final millis = ((_cardsScrollController.offset / imageWidth - index).abs() * 30.0) .ceil(); return _cardsScrollController.animateTo( imageWidth * index, duration: Duration(milliseconds: max(millis, 100)), curve: Curves.easeInOut, ); } void jumpTo(int index) { return _cardsScrollController.jumpTo( imageWidth * index, ); } } class CardsViewState { final bool expanded; final bool favorite; final List packCards; CardsViewState({ required this.expanded, required this.favorite, required this.packCards, }); @override int get hashCode => expanded.hashCode ^ expanded.hashCode ^ packCards.length; @override bool operator ==(Object other) { return (other is CardsViewState) && expanded == other.expanded && favorite == other.favorite && (packCards.length == other.packCards.length) && packCards.indexed.every((p) => other.packCards[p.$1].id == p.$2.id); } }