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

Deleting specific item out of ListView with Bloc

我有一个包含 ListView 的页面,其中包含 TextFormFields。用户可以在该 ListView 中添加或删除项目。 我使用 bloc 模式,并将 ListView 中的项目数量及其内容绑定到以 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. ',
              textAlign: TextAlign.center,
              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);
     }
     ...
);