You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无状态Widget结合Bloc使用ImagePicker为何触发LateInitializationError?

问题排查:LateInitializationError与TextFieldTags、Bloc的冲突

问题描述

使用ImagePicker选择图片功能正常,但将图片传入Bloc的sink时程序崩溃,报错:

LateInitializationError: Field '_textSeparators@411091221' has already been initialized.

移除FormBuilder后功能恢复正常,逐步添加组件排查发现,TextFieldTags组件是引发错误的根源。

相关代码片段

Widget代码

class CreatePost extends StatelessWidget {
final GlobalKey<FormBuilderState> _formkey =
GlobalKey<FormBuilderState>(debugLabel: 'GlobalFormKey #SignIn ');
const CreatePost({required Key key}) : super(key: key);

@override
Widget build(BuildContext context) {

return BlocBuilder<PostBloc, PostState>(
  builder: (context, state) {
    if (state is AddImagePost) {
      return Scaffold(
        appBar: AppBar(
          iconTheme: const IconThemeData(color: Colors.black,),
        ),
        body: GestureDetector(
          onTap: () {
            ImagePicker().pickImage(
              source: ImageSource.gallery,)
                .then((im) =>context.read<PostBloc>().add(SetImagePost(im!)));
          },
          child: 
          Column(
            children: [
             FormBuilder(key: _formkey, child: /* 省略其他组件 */),
             _imageWidget(state.memoryImage),
            ],
          ),
        ),);
    }
    // 补充其他状态的返回逻辑
    return const SizedBox();
  },
);
}
}

Event代码

class SetImagePost extends PostEvent {
const SetImagePost(this.image);
final XFile image;
}

State代码

class AddImagePost extends PostState {
  final MemoryImage memoryImage;

  AddImagePost(this.memoryImage) : super();
}

Bloc代码

class PostBloc extends Bloc<PostEvent, PostState> {
final XFile image;

  PostBloc({required this.image})
      :super(CreatePostInit()){
    on<SetImagePost>(_setImagePost);
  }

void _setImagePost(SetImagePost event, Emitter<PostState> emit) async {
  var memoryImage = await apiRepository.postImage(event.image);
  emit(AddImagePost(memoryImage));
}
}

引发错误的TextFieldTags组件代码

TextFieldTags(
  textfieldTagsController: tagsControllerAnswers,
  initialTags: const ["yes","no","maybe"],
  textSeparators: const [' ', ','],
  letterCase: LetterCase.small,
  validator: (String tag) {
    if (tagsController.getTags!.contains(tag)) {
      return 'you already entered that';
    }
    if(tagsControllerAnswers.getTags!.length > 4){
      return 'Please enter no more than 5 tags.';
    }
    return null;
  },
  inputfieldBuilder:
      (context, tec, fn, error, onChanged, onSubmitted) {
    return ((context, sc, tags, onTagDelete) {
      return TextField(
        controller: tec,
        focusNode: fn,
        decoration: InputDecoration(
          isDense: true,
          focusedBorder: const OutlineInputBorder(
            borderSide: BorderSide(color: askTextFieldBorderFocused, width: 3.0),
          ),
          enabledBorder: const OutlineInputBorder(
            borderSide: BorderSide(color: askTextFieldBorder, width: 2.0),
          ),
          errorText: error,
          prefixIconConstraints:
          BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.74),
          prefixIcon: tags.isNotEmpty
              ? SingleChildScrollView(
            controller: sc,
            scrollDirection: Axis.horizontal,
            child: Row(
                children: tags.map((String tag) {
                  return Container(
                    decoration: const BoxDecoration(
                      borderRadius: BorderRadius.all(
                        Radius.circular(3.0),
                      ),
                      color: tagColor,
                    ),
                    margin: const EdgeInsets.symmetric(
                        horizontal: 5.0),
                    padding: const EdgeInsets.symmetric(
                        horizontal: 5.0, vertical: 5.0),
                    child: Row(
                      mainAxisAlignment:
                      MainAxisAlignment.spaceBetween,
                      children: [
                        InkWell(
                          child: Text(
                            tag,
                            style: CustomTextStyle.bodyText1(context)?.copyWith(color: tagFont ),
                          ),
                          onTap: () {
                            // print("$tag selected");
                          },
                        ),
                        const SizedBox(width: 3.0),
                        InkWell(
                          child: const Icon(
                            Icons.close_rounded,
                            size: 20.0,
                            color: tagFont,
                          ),
                          onTap: () {
                            onTagDelete(tag);
                          },
                        )
                      ],
                    ),
                  );
                }).toList()),
          )
              : null,
        ),
        onChanged: onChanged,
        onSubmitted: onSubmitted,
      );
    });
  },
),

排查方向建议

  • 检查tagsControllerAnswers的生命周期:如果控制器是全局变量或在StatelessWidget中被复用,可能导致重复初始化冲突。将CreatePost改为StatefulWidget,在initState中初始化控制器,dispose方法中销毁,确保每次重建都使用新的控制器实例或正确管理状态。
  • 确认TextFieldTags与FormBuilder的集成方式:确保TextFieldTags被正确包装在FormBuilderField中,未正确注册到FormBuilder会导致Form重建时组件内部状态异常。
  • 检查Bloc状态更新后的重建逻辑:Bloc emit新状态后,整个Scaffold会重建,若TextFieldTags的textSeparators等字段被重复赋值给已初始化的内部变量,会触发LateInitializationError。尝试将这些配置提取为全局常量,或确保组件重建时不会重复初始化内部字段。
  • 排查TextFieldTags组件的版本问题:该错误指向组件内部的_textSeparators字段重复初始化,可能是组件版本存在已知bug,尝试升级到最新稳定版或降级到之前的可用版本。
  • 验证Bloc的初始化逻辑:PostBloc的构造函数接收image参数,确认Bloc是否在状态更新时被重复创建,导致依赖的组件状态紊乱。确保Bloc是通过BlocProvider全局提供,而非在build方法中重复实例化。

内容的提问来源于stack exchange,提问作者midi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 06:36:22