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

Flutter Bloc状态不更新:输入内容无法同步至UI界面

Flutter Bloc状态不更新问题:弹窗编辑地址后UI无法同步输入内容

问题描述

使用Flutter Bloc时遇到状态不更新问题,弹窗编辑地址后,UI无法同步显示输入内容。已在输入框onChanged方法触发GetInput事件,也尝试在保存按钮onPressed中添加相同逻辑,但均无效。

相关代码

Bloc文件

import 'package:bloc/bloc.dart';

part 'address_event.dart';
part 'address_state.dart';

class AddressBloc extends Bloc<AddressEvent, AddressState> {
  AddressBloc() : super(AddressInitial());

  @override
  Stream<AddressState> mapEventToState(AddressEvent event) async* {
    if (event is GetInput) {
      yield* _mapGetInputEventToState(event);
    }
  }

  @override
  Stream<AddressState> _mapGetInputEventToState(GetInput event) async* {
    yield AddressInitial(inputText: event.inputText);
  }
}

Event文件

part of 'address_bloc.dart';

abstract class AddressEvent {}

class GetInput extends AddressEvent {
  String inputText;

  GetInput(this.inputText);
}

State文件

part of 'address_bloc.dart';

abstract class AddressState {}

class AddressInitial extends AddressState {
  String? inputText;

  AddressInitial({this.inputText});
}

UI文件Bloc部分

BlocBuilder<AddressBloc, AddressState>(
                              bloc: addressBloc,
                              builder: (context, state) {
                                if(state is AddressInitial){
                                  return Row(
                                    children: [
                                      Icon(Icons.location_on, size: 20),
                                      Space(24),
                                      Expanded(
                                        child: Column(
                                          crossAxisAlignment:
                                              CrossAxisAlignment.start,
                                          children: [
                                            Text(
                                              "Address",
                                              textAlign: TextAlign.start,
                                              style: TextStyle(
                                                  fontWeight: FontWeight.w900,
                                                  fontSize: 21),
                                            ),
                                            Space(4),
                                            if(textEditingController.text.isNotEmpty) 
                                              Text(
                                                "${state.inputText}",
                                                textAlign: TextAlign.start,
                                                style: TextStyle(
                                                  color: Colors.grey,
                                                  fontSize: 16,
                                                ),
                                              ),
                                            if(textEditingController.text.isEmpty) 
                                              Text(
                                                "2911, sec 9/11, Hisar",
                                                textAlign: TextAlign.start,
                                                style: TextStyle(
                                                  color: Colors.grey,
                                                  fontSize: 16,
                                                ),
                                              ),
                                          ],
                                        ),
                                      ),
                                      Space(8),
                                      IconButton(
                                          onPressed: () {
                                            showDialog(
                                                context: context,
                                                builder: (BuildContext context) {
                                                  return Dialog(
                                                    child: Container(
                                                      height: 250,
                                                      width: 500,
                                                      padding:
                                                          const EdgeInsets.all(
                                                              20),
                                                      child:
                                                          SingleChildScrollView(
                                                        child: Column(
                                                          children: [
                                                            CustomTextField(
                                                                controller: textEditingController,
                                                                maxLines: 3,
                                                                title: 'Address',
                                                                hasTitle: true,
                                                                initialValue: '',
                                                                onChanged:
                                                                    (value) {
                                                                      addressBloc.add(GetInput(textEditingController.text));
                                                                    }),
                                                            CustomTextField(
                                                                maxLines: 1,
                                                                title: 'LandMark',
                                                                hasTitle: true,
                                                                initialValue: '',
                                                                onChanged:
                                                                    (value) {}),
                                                            ElevatedButton(
                                                              style: ElevatedButton
                                                                  .styleFrom(
                                                                      backgroundColor:
                                                                          Colors
                                                                              .white),
                                                              onPressed: () {
                                                                Navigator.pop(
                                                                    context);
                                                              },
                                                              child: Text(
                                                                'Save',
                                                                style: Theme.of(
                                                                        context)
                                                                    .textTheme
                                                                    .headline5!
                                                                    .copyWith(
                                                                        color: Colors
                                                                            .black),
                                                              ),
                                                            )
                                                          ],
                                                        ),
                                                      ),
                                                    ),
                                                  );
                                                });
                                          },
                                          icon: Icon(Icons.edit)),
                                    ],
                                  );
                                
                                }
                                else{
                                  return SizedBox(height: 10,);
                                }
                              },
                            ),

解决方案

1. 重写状态类的相等性判断方法

Bloc通过比较状态对象的相等性决定是否触发UI重建,默认==比较对象引用而非属性值。修改AddressInitial类,重写==和hashCode:

part of 'address_bloc.dart';

abstract class AddressState {}

class AddressInitial extends AddressState {
  String? inputText;

  AddressInitial({this.inputText});

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is AddressInitial &&
          runtimeType == other.runtimeType &&
          inputText == other.inputText;

  @override
  int get hashCode => inputText.hashCode;
}

2. UI逻辑改为依赖Bloc状态而非TextEditingController

原UI用textEditingController.text判断显示内容,导致与Bloc状态不同步,改为直接使用Bloc的state.inputText:

BlocBuilder<AddressBloc, AddressState>(
  bloc: addressBloc,
  builder: (context, state) {
    if(state is AddressInitial){
      return Row(
        children: [
          Icon(Icons.location_on, size: 20),
          Space(24),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  "Address",
                  textAlign: TextAlign.start,
                  style: TextStyle(
                      fontWeight: FontWeight.w900,
                      fontSize: 21),
                ),
                Space(4),
                // 直接依赖Bloc状态判断
                if(state.inputText?.isNotEmpty ?? false) 
                  Text(
                    "${state.inputText}",
                    textAlign: TextAlign.start,
                    style: TextStyle(
                      color: Colors.grey,
                      fontSize: 16,
                    ),
                  ),
                if(state.inputText?.isEmpty ?? true) 
                  Text(
                    "2911, sec 9/11, Hisar",
                    textAlign: TextAlign.start,
                    style: TextStyle(
                      color: Colors.grey,
                      fontSize: 16,
                    ),
                  ),
              ],
            ),
          ),
          Space(8),
          IconButton(
              onPressed: () {
                // 弹窗打开时同步Bloc状态到控制器
                textEditingController.text = state.inputText ?? '';
                showDialog(
                    context: context,
                    builder: (BuildContext context) {
                      return Dialog(
                        child: Container(
                          height: 250,
                          width: 500,
                          padding: const EdgeInsets.all(20),
                          child: SingleChildScrollView(
                            child: Column(
                              children: [
                                CustomTextField(
                                    controller: textEditingController,
                                    maxLines: 3,
                                    title: 'Address',
                                    hasTitle: true,
                                    initialValue: '',
                                    // 直接传onChanged的value即可,无需取控制器文本
                                    onChanged: (value) {
                                      addressBloc.add(GetInput(value));
                                    }),
                                CustomTextField(
                                    maxLines: 1,
                                    title: 'LandMark',
                                    hasTitle: true,
                                    initialValue: '',
                                    onChanged: (value) {}),
                                ElevatedButton(
                                  style: ElevatedButton.styleFrom(
                                      backgroundColor: Colors.white),
                                  onPressed: () {
                                    Navigator.pop(context);
                                  },
                                  child: Text(
                                    'Save',
                                    style: Theme.of(context)
                                        .textTheme
                                        .headline5!
                                        .copyWith(color: Colors.black),
                                  ),
                                )
                              ],
                            ),
                          ),
                        ),
                      );
                    });
              },
              icon: Icon(Icons.edit)),
        ],
      );
    }
    else{
      return SizedBox(height: 10,);
    }
  },
),

额外优化点

  • 弹窗打开时,将Bloc当前的inputText赋值给textEditingController,确保编辑初始值与UI显示一致。
  • onChanged回调直接使用参数value,无需通过textEditingController.text获取输入内容,逻辑更简洁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 19:19:53