#flutter #flutter-animatedlist
#flutter #flutter-animatedlist
Вопрос:
Для профессионального приложения у меня есть список оценок (оценка objectc), и я хочу разрешить пользователю отмечать некоторые оценки как избранные (управляемые избранным классом со списком свойств избранное).
Мой homapage-это просмотр страницы с участием 2 детей : глобальный список оценок и избранное. Первый — это статическое представление списка, которое можно перезагрузить, когда пользователь добавляет оценку в качестве избранного (на основе StreamBuilder с блоком). Но я хочу, чтобы на второй странице отображался анимированный список избранного, потому что для пользователя более очевидно отклонять избранное с помощью анимированного списка, а не просто «отбрасывать» отклоненный элемент…
Моя проблема : когда я обновляю анимированный список с первой страницы (где анимированный список не создан), он не добавляет элементы, поэтому, наконец, я получаю ошибку «диапазон индексов»…
Я попытался объявить AnimatedList внутри блока, и я использую animatedListKey.currentState.insertItem(индекс) для обновления элементов, но я могу проверить, что animatedListKey.currentState равен нулю, когда AnimatedList не «физически» собран.
Мои занятия :
- Первая страница: страница списка показателей
class ScoreListPageBody extends StatelessWidget { const ScoreListPageBody({Key? key}) : super(key: key); @override Widget build(BuildContext context) { ScoresBloc bloc = BlocProvider.oflt;ScoresBlocgt;(context); return StreamBuilderlt;Listlt;Categorygt;gt;( stream: bloc.categoriesStream, initialData: bloc.categories, builder: (BuildContext context, AsyncSnapshotlt;Listlt;Categorygt;gt; stream) { Listlt;Widgetgt; _slivers = []; if(!stream.hasData) throw Exception("Exception in ScoreListPageBody. No data available to build stream..."); for (Category category in stream.data!) { _slivers.add( SliverStickyHeader( header: HeaderTile( category: category, headerColor: Theme.of(context).colorScheme.listHeader, separatorColor: Theme.of(context).colorScheme.separator, iconColor: Theme.of(context).colorScheme.listHeaderIcon, headerTextColor: Theme.of(context).colorScheme.listHeaderText, ), sliver: SliverList( delegate: SliverChildBuilderDelegate( (BuildContext context, int i) { final int itemIndex = i ~/ 2; if (!i.isEven) return Divider(height: 1, color: Theme.of(context).colorScheme.separator,); else return ScoreTile( score: category.scores[itemIndex], onPressed: () =gt; bloc.toggle(category.scores[itemIndex]), ); }, childCount: category.scores.length * 2 - 1 ), ) ) ); } return CustomScrollView( slivers: _slivers ); } ); } }
- Вторая страница: страница избранного
class FavoritesListPageBody extends StatelessWidget { const FavoritesListPageBody({Key? key}) : super(key: key); @override Widget build(BuildContext context) { ScoresBloc bloc = BlocProvider.oflt;ScoresBlocgt;(context); if(bloc.favoritesManager.favorites.isEmpty) { return Container( constraints: const BoxConstraints.expand(), color: Theme.of(context).colorScheme.favListEmptyBackground, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.search_rounded, size: 120, color: Theme.of(context).colorScheme.noFavoriteFound), const SizedBox(height: 50,), Text("Aucun favori", style: TextStyle(color: Theme.of(context).colorScheme.noFavoriteFound)), ], ) ); } // Print the AnimatedList as declared in ScoresBloc return bloc.animatedListBloc.animatedList; } }
- The main BLoC used for these pages (NB: it uses the singleton pattern):
class ScoresBloc extends BlocBase { // To trigger StreamBuilder according to Listlt;Scoregt; content final StreamControllerlt;Listlt;Scoregt;gt; _scoresController = new StreamControllerlt;Listlt;Scoregt;gt;.broadcast(); Streamlt;Listlt;Scoregt;gt; get scoresStream =gt; this._scoresController.stream; Sinklt;Listlt;Scoregt;gt; get scoresSink =gt; this._scoresController.sink; // To trigger StreamBuilder according to Listlt;Categorygt; content final StreamControllerlt;Listlt;Categorygt;gt; _categoriesController = new StreamControllerlt;Listlt;Categorygt;gt;.broadcast(); Streamlt;Listlt;Categorygt;gt; get categoriesStream =gt; this._categoriesController.stream; Sinklt;Listlt;Categorygt;gt; get categoriesSink =gt; this._categoriesController.sink; // Properties to access lists of scores and Favorites manager (Favoriteslt;Tgt; class) final Listlt;Categorygt; categories = lt;Categorygt;[]; final Listlt;Scoregt; scores = lt;Scoregt;[]; late Favoriteslt;Scoregt; favoritesManager; // Pattern Singleton with private constructor to force the use of init() static final ScoresBloc _singleton = ScoresBloc._internal(); ScoresBloc._internal(); bool _initialized = false; // late AnimatedList animatedList; late AnimatedListProvider animatedListBloc; static Futurelt;ScoresBlocgt; init({required ScoreListProvider provider}) async { ScoresBloc instance = _singleton; if(instance._initialized) return instance; instance.categories.addAll(provider.categories); instance.scores.addAll(provider.scores); instance.favoritesManager = new Favoriteslt;Scoregt;(); await instance.favoritesManager.init(instance.scores); //instance.animatedList = instance._buildAnimatedList(); instance.animatedListBloc = new AnimatedListProvider(favoritesManager: instance.favoritesManager); instance._initialized = true; return instance; } // Toggle a score status and update 1) AnimatedList, and 2) streams void toggle(Score score) { if(!score.isFavorite) { this.favoritesManager.toggle(score); this.animatedListBloc.toggle(ScoreEvent.Fav, score); this._updateStreams(); } else { this.animatedListBloc.toggle(ScoreEvent.Unfav, score); this.favoritesManager.toggle(score); this._updateStreams(); } } void _updateStreams() { this.categoriesSink.add(this.categories); this.scoresSink.add(this.scores); } @override void dispose() { this._scoresController.close(); this._categoriesController.close(); } }
- Manage the AnimatedList (is instanciated in ScoresBloc and returns the Widget for FavoritesPage):
enum ScoreEvent { Fav, // ignore: constant_identifier_names Unfav // ignore: constant_identifier_names } class AnimatedListProvider { // AnimatedList for favorites page final _animatedListKey = new GlobalKeylt;AnimatedListStategt;(); GlobalKeylt;AnimatedListStategt; get animatedListKey =gt; this._animatedListKey; late AnimatedList animatedList; late Favoriteslt;Scoregt; favoritesManager; // Get the favorites manager from a parent class AnimatedListProvider({required this.favoritesManager}) { this.animatedList = this._buildAnimatedList(); } // Declare the AnimatedList even if not physically builded on screen AnimatedList _buildAnimatedList() { return AnimatedList( key: this.animatedListKey, initialItemCount: this.favoritesManager.favorites.length, itemBuilder: (BuildContext context, int index, Animationlt;doublegt; animation) { return FavoriteSlidingTile( animation: animation, score: this.favoritesManager.favorites.toList()[index], ); } ); } // Update AnimatedList when triggered by ScoresBloc void toggle(ScoreEvent event, Score score) { if(event == ScoreEvent.Fav) { if(this.animatedListKey.currentState == null) print("It won't work..."); this.animatedListKey.currentState?.insertItem(this.favoritesManager.favorites.toList().indexOf(score)); } else { this.animatedListKey.currentState?.removeItem( this.favoritesManager.favorites.toList().indexOf(score), (context, animation) =gt; FavoriteSlidingTile(animation: animation, score: score), duration: const Duration(milliseconds: 2000) ); } } }
What do you think I could do to achieve this ? I tried so many things, the only solution I see now is to abandon AnimatedList, that would be such a pity… :’)