0

对于专业应用程序,我有一个分数列表(objetc 分数),我希望允许用户将一些分数标记为收藏夹(由具有属性列表收藏夹的收藏夹类管理)。

我的主页是一个带 2 个孩子的 PageView:全局得分列表和收藏夹。第一个是静态 ListView,当用户添加一个 Score 作为收藏夹时可以重新加载它(基于带有 BLoC 的 StreamBuilder)。但我希望第二页显示收藏夹的 AnimatedList,因为用户可以更明确地使用动画列表关闭收藏夹,而不是仅仅“删除”被关闭的项目......

我的问题:当我从第一页(未构建 AnimatedList)更新 AnimatedList 时,它没有添加项目,所以最后我得到一个“索引范围”错误......

我尝试在 BLoC 中声明 AnimatedList 并使用 animatedListKey.currentState.insertItem(index) 更新项目,但是当 AnimatedList 未“物理”构建时,我可以验证 animatedListKey.currentState 为空。

我的课程 :

  1. 第一页:ScoreListPage
class ScoreListPageBody extends StatelessWidget
{
    const ScoreListPageBody({Key? key}) : super(key: key);
    
    @override
    Widget build(BuildContext context)
    {
        ScoresBloc bloc = BlocProvider.of<ScoresBloc>(context);
        
        return StreamBuilder<List<Category>>(
            stream: bloc.categoriesStream,
            initialData: bloc.categories,
            builder: (BuildContext context, AsyncSnapshot<List<Category>> stream)
            {
                List<Widget> _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: () => bloc.toggle(category.scores[itemIndex]),
                                        );
                                    },
                                    childCount: category.scores.length * 2 - 1
                                ),
                            )
                        )
                    );
                }
                
                return CustomScrollView(
                    slivers: _slivers
                );
            }
        );
    }
}
  1. 第二页:FavoritesPage
class FavoritesListPageBody extends StatelessWidget
{
    const FavoritesListPageBody({Key? key}) : super(key: key);
    
    @override
    Widget build(BuildContext context)
    {
        ScoresBloc bloc = BlocProvider.of<ScoresBloc>(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. 用于这些页面的主要 BLoC(注意:它使用单例模式):
class ScoresBloc extends BlocBase
{
    // To trigger StreamBuilder according to List<Score> content
    final StreamController<List<Score>> _scoresController = new StreamController<List<Score>>.broadcast();
    Stream<List<Score>> get scoresStream => this._scoresController.stream;
    Sink<List<Score>> get scoresSink => this._scoresController.sink;
    
    // To trigger StreamBuilder according to List<Category> content
    final StreamController<List<Category>> _categoriesController = new StreamController<List<Category>>.broadcast();
    Stream<List<Category>> get categoriesStream => this._categoriesController.stream;
    Sink<List<Category>> get categoriesSink => this._categoriesController.sink;
    
    // Properties to access lists of scores and Favorites manager (Favorites<T> class)
    final List<Category> categories = <Category>[];
    final List<Score> scores = <Score>[];
    late Favorites<Score> 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 Future<ScoresBloc> 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 Favorites<Score>();
        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. 管理 AnimatedList(在 ScoresBloc 中实例化并返回 FavoritesPage 的 Widget):
enum ScoreEvent
{
    Fav, // ignore: constant_identifier_names
    Unfav // ignore: constant_identifier_names
}

class AnimatedListProvider
{
    // AnimatedList for favorites page
    final _animatedListKey = new GlobalKey<AnimatedListState>();
    GlobalKey<AnimatedListState> get animatedListKey => this._animatedListKey;
    
    late AnimatedList animatedList;
    late Favorites<Score> 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, Animation<double> 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) => FavoriteSlidingTile(animation: animation, score: score),
                duration: const Duration(milliseconds: 2000)
            );
        }
    }
}

你认为我能做些什么来实现这一目标?我尝试了很多东西,我现在看到的唯一解决方案就是放弃 AnimatedList,那真是太可惜了...... :')

4

0 回答 0