首页 > 解决方案 > 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];
}

标签: flutterbloc

解决方案


经过几个小时的搜索,我发现答案与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' ),
          ),

        );
      },

推荐阅读