flutter - Fluter Bloc 从网格中删除项目
问题描述
我有一个由集团状态控制的小部件。作为开始,一个 url 列表被传递给用于创建初始网格的小部件。然后,一旦必须添加或删除 url,就会调用 bloc 事件来更新列表并产生新状态。然而,我的问题是,当一个项目被删除时,打印到屏幕上的列表是正确的(删除了项目),但 gridview 似乎只删除了最后一个图像而不是删除的图像。我在这里做错了什么?
相关问题(我确实从 Gridview.count 更改为 Gridview.builder),但无济于事。
小部件:
class ItemImageGrid extends StatelessWidget {
final List<String> urls;
const ItemImageGrid({Key key, this.urls }) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
child: BlocBuilder<ItemGridBloc, ItemGridState>(
builder: (context, state) {
if(state is ItemGridInitialState){
print("state is ItemGridInitialState");
return _showCarouselAndStartLoading(context);
}else if(state is ImagesUpdatedState){
print("state is ImagesUpdatedState");
return _showGrid(context, state.urls);
}
return Center(child: CircularProgressIndicator());
},
),
);
}
Widget _showCarouselAndStartLoading(BuildContext context){
BlocProvider.of<ItemGridBloc>(context).add( // add or dispatch??? try both to see difference - seems dispatch cannot be used here...
LoadImageEvent(urls),
);
return Center(child: CircularProgressIndicator());
}
Widget _showGrid(BuildContext context, List<String> urls) {
if(urls.isEmpty){
return Center(
child: Text("No photos yet")
);
}
return GridView.builder(
itemCount: urls.length,
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
// You must use the GridDelegate to specify row item count
// and spacing between items
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
// childAspectRatio: 1.0,
crossAxisSpacing: 5.0,
mainAxisSpacing: 5.0,
),
itemBuilder: (BuildContext context, int index) {
return BlocProvider(
create: (context) => CachedImageBloc( ),
child: GestureDetector(
onTap: () => {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context2) {
// List<String> clonedList;
return BlocProvider(
create: (context) => new CachedImageBloc( ),
child: new BasicImagePreview(
imageFilePath: urls[index],
// bloc: BlocProvider.of<InspectionItemAddEditBloc>(context),
// image_paths: imagePaths,
onDelete: () =>{
BlocProvider.of<ItemGridBloc>(context).add(ImageRemovedEvent( urls[index])),
},
),
);
}))
},
child: LocalImageViewer(
url: urls[index],
errorAssetPath: 'assets/error_loading.png' ),
),
);
},
);
}
}
集团:
class ItemGridBloc extends Bloc<ItemGridEvent, ItemGridState> {
List<String> urls;
ItemGridBloc();
@override
Stream<ItemGridState> mapEventToState(
ItemGridEvent event,
) async* {
print('event = $event');
if(event is ImageAddedEvent){
yield* _mapImageAddedEventToState(event);
}
else if(event is ImageRemovedEvent){
yield* _mapImageRemovedEventToState(event);
} else if(event is LoadImageEvent){
yield* _mapLoadImageEventToState(event);
}
}
@override
ItemGridState get initialState => ItemGridInitialState();
Stream<ItemGridState> _mapImageAddedEventToState(ImageAddedEvent event) async*{
urls.add(event.url);
yield ImagesUpdatedState(List.of(urls));
}
Stream<ItemGridState>_mapImageRemovedEventToState(ImageRemovedEvent event) async*{
print('urls b4 remove:');
urls.forEach((element) {print('$element');});
print('removing url: ${event.url}');
urls.remove(event.url);
print('urls after remove:');
urls.forEach((element) {print('$element');});
yield ImagesUpdatedState(urls);
}
Stream<ItemGridState> _mapLoadImageEventToState(LoadImageEvent event) async*{
urls = List.of(event.urls);
yield ImagesUpdatedState(event.urls);
}
}
活动类别:
abstract class ItemGridEvent extends Equatable {
const ItemGridEvent();
}
class ImageAddedEvent extends ItemGridEvent{
final String url;
ImageAddedEvent(this.url);
@override
List<Object> get props => [url];
}
class LoadImageEvent extends ItemGridEvent{
final List<String> urls;
LoadImageEvent(this.urls);
@override
List<Object> get props => [urls];
}
class ImageRemovedEvent extends ItemGridEvent{
final String url;
ImageRemovedEvent(this.url);
@override
List<Object> get props => [url];
}
状态类:
abstract class ItemGridState extends Equatable {
const ItemGridState();
}
class ItemGridInitialState extends ItemGridState {
@override
List<Object> get props => [];
}
class ImagesUpdatedState extends ItemGridState {
final List<String> urls;
ImagesUpdatedState(this.urls);
@override
List<Object> get props => [urls];
}
解决方案
经过几个小时的搜索,我发现答案与Flutter中的KEYS有关。
简而言之,Flutter 仅按类型而不是状态来比较小部件。因此,当 GridView 中表示的 List 的状态发生更改时,Flutter 不知道应该删除哪些子项,因为它们的 Types 仍然相同并签出。Flutter 发现的唯一问题是项目的数量,这就是为什么它总是删除 Grid 中的最后一个小部件。
因此,如果您想在 Flutter 中操作包含有状态子级的列表,请为每个子级的顶级小部件分配一个 Key。本文提供了更详细的解释。
我对代码所做的唯一更改是在生成项目时如下:
itemBuilder: (BuildContext context, int index) {
return BlocProvider(
key: UniqueKey(), // assign the key
create: (context) => CachedImageBloc( ),
child: GestureDetector(
onTap: () => {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context2) {
// List<String> clonedList;
return BlocProvider(
create: (context) => new CachedImageBloc( ),
child: new BasicImagePreview(
imageFilePath: urls[index],
// bloc: BlocProvider.of<InspectionItemAddEditBloc>(context),
// image_paths: imagePaths,
onDelete: () =>{
BlocProvider.of<ItemGridBloc>(context).add(ImageRemovedEvent( urls[index])),
},
),
);
}))
},
child: LocalImageViewer(
url: urls[index],
errorAssetPath: 'assets/error_loading.png' ),
),
);
},
推荐阅读
- java - Spring Batch 和我的 Batis:MyBatisBatchItemWriter 抛出 IllegalArgumentException:参数类型不匹配
- delphi - Delphi XE8 - 本机 Wifi API - Wlan API - 从 WLAN_HOSTED_NETWORK_REASON 获取价值
- python - 我想知道为什么魔术函数会为特定的运算符给出特定的答案
- angular - 如何通过 Visual Studio 2017 为 .Net Core 2.1 项目添加 Angular Material
- git - 将 sql server 数据库提交到源代码控制
- laravel - 我正在使用 jorenvanhocht/laravel-share 包使用 laravel 共享,但是当我在社交网站上共享时它不显示预览或缩略图
- ios - 无法使用 firebase ML 套件检测眨眼事件
- create-react-app - 如何调整画布大小?
- c# - 我可以在仍然使用 DbContext 的同时调用未等待的 Async 方法并返回响应吗?
- sql-server - cte 和申报联盟