对于专业应用程序,我有一个分数列表(objetc 分数),我希望允许用户将一些分数标记为收藏夹(由具有属性列表收藏夹的收藏夹类管理)。
我的主页是一个带 2 个孩子的 PageView:全局得分列表和收藏夹。第一个是静态 ListView,当用户添加一个 Score 作为收藏夹时可以重新加载它(基于带有 BLoC 的 StreamBuilder)。但我希望第二页显示收藏夹的 AnimatedList,因为用户可以更明确地使用动画列表关闭收藏夹,而不是仅仅“删除”被关闭的项目......
我的问题:当我从第一页(未构建 AnimatedList)更新 AnimatedList 时,它没有添加项目,所以最后我得到一个“索引范围”错误......
我尝试在 BLoC 中声明 AnimatedList 并使用 animatedListKey.currentState.insertItem(index) 更新项目,但是当 AnimatedList 未“物理”构建时,我可以验证 animatedListKey.currentState 为空。
我的课程 :
- 第一页: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
);
}
);
}
}
- 第二页: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;
}
}
- 用于这些页面的主要 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();
}
}
- 管理 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,那真是太可惜了...... :')