使用Bloc从ListView中删除特定项目

问题描述

我有一个包含ListView的页面,其中包含TextFormFields。用户可以从该ListView添加或删除项目。 我使用了bloc模式,并将ListView中的Items数量及其内容绑定到以bloc状态保存的列表。当我想删除项目时,我从该列表中删除相应的文本并产生新状态。但是,这将始终删除最后一个项目,而不是应该删除的项目。调试时,我可以清楚地看到我要删除的项目实际上已从状态列表中删除。尽管如此,ListView还是删除了最后一个项目。

我已经读到使用键可以解决此问题,并且可以解决。但是,如果我使用按键,则会出现新问题。 现在,每次写入字符时,TextFormField都会失去焦点。我猜想这与以下事实有关:每次键入一个字符时,ListView都会重绘其项,并且以某种方式拥有键会使焦点的行为有所不同。

有什么办法解决这个问题吗?

页面代码(ListView在底部):

class GiveBeneftis extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var bloc = BlocProvider.of<CreateChallengeBloc>(context);
    return BlocBuilder<CreateChallengeBloc,CreateChallengeState>(
        builder: (context,state) {
      return CreatePageTemplate(
        progress: state.progressOfCreation,buttonBar: NavigationButtons(
          onPressPrevious: () {
            bloc.add(ProgressOfCreationChanged(nav_direction: -1));
            Navigator.of(context).pop();
          },onPressNext: () {
            bloc.add(ProgressOfCreationChanged(nav_direction: 1));
            Navigator.of(context).pushNamed("create_challenge/add_pictures");
          },previous: 'Details',next: 'Picture',),child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,children: <Widget>[
            Text(
              'List the benefits of you Challenge',textAlign: TextAlign.center,style: TextStyle(fontSize: 28,fontWeight: FontWeight.bold),SizedBox(height: 30),Text(
              'Optionally: Make a list of physical and mental benefits the participants can expect. ',style: TextStyle(
                  color: Colors.grey,fontSize: 14,fontWeight: FontWeight.w400),SizedBox(height: 50),Container(
              margin: EdgeInsets.all(8.0),decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(12),color: Colors.yellow[600]),child: FlatButton(
                materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,onPressed: () => bloc.add(ChallengeBenefitAdded()),child: Text('Add a benefit',style: TextStyle(
                        color: Colors.white,fontWeight: FontWeight.bold)),Expanded(
                child: new ListView.builder(
                    itemCount: state.benefits.length,itemBuilder: (BuildContext context,int i) {
                      final item = state.benefits[i];
                      return Padding(
                          padding: EdgeInsets.symmetric(horizontal: 25),child: TextFieldTile(
                            //key: UniqueKey(),labelText: 'Benefit ${i + 1}',validator: null,initialText: state.benefits[i],onTextChanged: (value) => bloc.add(
                                ChallengeBenefitChanged(
                                    number: i,text: value)),onCancelIconClicked: () {
                              bloc.add(ChallengeBenefitRemoved(number: i));
                            },));
                    })),],);
    });
  }
}

TextfieldTile的代码:

class TextFieldTile extends StatelessWidget {
  final Function onTextChanged;
  final Function onCancelIconClicked;
  final Function validator;
  final String labelText;
  final String initialText;

  const TextFieldTile(
      {Key key,this.onTextChanged,this.onCancelIconClicked,this.labelText,this.initialText,this.validator})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Stack(children: <Widget>[
      TextFormField(
          textCapitalization: TextCapitalization.sentences,initialValue: initialText,validator: validator,onChanged: onTextChanged,maxLines: null,decoration: InputDecoration(
            labelText: labelText,)),Align(
        alignment: Alignment.topRight,child: IconButton(
            icon: Icon(Icons.cancel),onPressed: onCancelIconClicked),]);
  }
}

集团的相关部分:

 if (event is ChallengeBenefitAdded) {
      var newBenefitsList = List<String>.from(state.benefits);
      newBenefitsList.add("");
      yield state.copyWith(benefits: newBenefitsList);
    }
    else if (event is ChallengeBenefitChanged) {
      var newBenefitsList = List<String>.from(state.benefits);
      newBenefitsList[event.number] = event.text;
      yield state.copyWith(benefits: newBenefitsList);
    }
    else if (event is ChallengeBenefitRemoved) {
      var newBenefitsList = List<String>.from(state.benefits);
      newBenefitsList.removeAt(event.number);
      yield state.copyWith(benefits: newBenefitsList);
    }

解决方法

我想到了您可以在这里做的两件事。

  1. 在文本字段中创建一个不同的块来处理更改,这将避免在不需要时实际上不必更新整个列表的状态。
  2. 有一个条件,可避免在您的集团更改为仅与键盘操作相关的状态时重建列表。

示例:

BlocBuilder<CreateChallengeBloc,CreateChallengeState>(
    buildWhen: (previousState,currentState) {
        return (currentState is YourNonKeyboardStates);
     }
     ...
);

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...