Как обновить анимированный список с другой страницы в Flutter (общий список)?

#flutter #flutter-animatedlist

#flutter #flutter-animatedlist

Вопрос:

Для профессионального приложения у меня есть список оценок (оценка objectc), и я хочу разрешить пользователю отмечать некоторые оценки как избранные (управляемые избранным классом со списком свойств избранное).

Мой homapage-это просмотр страницы с участием 2 детей : глобальный список оценок и избранное. Первый — это статическое представление списка, которое можно перезагрузить, когда пользователь добавляет оценку в качестве избранного (на основе StreamBuilder с блоком). Но я хочу, чтобы на второй странице отображался анимированный список избранного, потому что для пользователя более очевидно отклонять избранное с помощью анимированного списка, а не просто «отбрасывать» отклоненный элемент…

Моя проблема : когда я обновляю анимированный список с первой страницы (где анимированный список не создан), он не добавляет элементы, поэтому, наконец, я получаю ошибку «диапазон индексов»…

Я попытался объявить AnimatedList внутри блока, и я использую animatedListKey.currentState.insertItem(индекс) для обновления элементов, но я могу проверить, что animatedListKey.currentState равен нулю, когда AnimatedList не «физически» собран.

Мои занятия :

  1. Первая страница: страница списка показателей
 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  );  }  );  } }  
  1. Вторая страница: страница избранного
 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;  } }  
  1. 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();  } }  
  1. 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… :’)