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

Flutter分类选择按钮状态未同步更新问题求助

分类选择按钮选中状态不更新问题修复

问题描述

通过API获取专业分类容器,点击不同分类时,对应专家列表能正常展示,但分类按钮的选中状态无法同步更新。

问题根源

  1. 状态管理冲突:同时使用组件本地selectedIndex和SpecialistBloc状态,两种状态源未保持一致。在BlocBuilder中直接修改本地变量不会触发组件重建,导致UI不更新。
  2. Bloc初始化错误:在StatefulWidget的build方法中创建MultiBlocProvider,每次组件重建都会重新初始化Bloc实例,造成状态丢失或混乱。
  3. Bloc事件处理缺失:若SpecialistBloc未正确处理UpdateSelectedIndexEvent并更新状态,UI无法感知选中变化。

修复方案

1. 统一状态管理,移除本地状态

将组件改为StatelessWidget,完全依赖SpecialistBloc管理选中索引,消除状态冲突。

2. 修正BlocProvider位置

把MultiBlocProvider移到页面级父组件,避免每次build重建Bloc:

// 父页面示例
@override
Widget build(BuildContext context) {
  return MultiBlocProvider(
    providers: [
      BlocProvider(
        create: (context) => CategoryBloc(
          CategoryRepository(
            Dio(),
            DioClient(TokenStorage()),
          ),
        )..add(LoadCategoryEvent()),
      ),
      BlocProvider(
        create: (context) => SpecialistBloc(
          SpecialistRepository(
            Dio(),
            DioClient(TokenStorage()),
          ),
        ),
      ),
    ],
    child: Scaffold(
      body: FilterContainersWidget(),
    ),
  );
}

3. 修改FilterContainersWidget代码

class FilterContainersWidget extends StatelessWidget {
  const FilterContainersWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<CategoryBloc, CategoryState>(
      builder: (context, categoryState) {
        if (categoryState is CategoryLoading) {
          return const Center(child: CircularProgressIndicator());
        }
        if (categoryState is CategorySuccess) {
          final categories = categoryState.categoryModel;
          return BlocBuilder<SpecialistBloc, SpecialistState>(
            builder: (context, specialistState) {
              // 从Bloc状态获取选中索引,默认0
              final selectedIndex = specialistState is SpecialistCategorySelected 
                  ? specialistState.selectedIndex 
                  : 0;

              return Row(
                children: List.generate(
                  categories.length,
                  (index) {
                    final category = categories[index];
                    return Padding(
                      padding: const EdgeInsets.only(left: 8),
                      child: _buildFilterContainer(
                        category: category.title,
                        index: index,
                        isSelected: selectedIndex == index,
                        icon: _buildIcon(category),
                        label: category.title,
                      ),
                    );
                  },
                ),
              );
            },
          );
        }
        if (categoryState is CategoryError) {
          return Center(child: Text(categoryState.error, style: const TextStyle(color: Colors.red)));
        }
        return const SizedBox(child: Text('no data'));
      },
    );
  }

  Widget _buildFilterContainer({
    required int index,
    required bool isSelected,
    required Widget icon,
    required String label,
    required String category,
  }) {
    return GestureDetector(
      onTap: () {
        // 直接通知Bloc更新索引并加载专家
        context.read<SpecialistBloc>().add(UpdateSelectedIndexEvent(index));
        context.read<SpecialistBloc>().add(LoadSpecialistEvent(category));
      },
      child: Container(
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(16),
          gradient: LinearGradient(
            colors: isSelected
                ? [firstColorGrad, secondColorGrad]
                : [lightRose, lightRose],
          ),
        ),
        child: Padding(
          padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              ShaderMask(
                shaderCallback: (bounds) => LinearGradient(
                  colors: isSelected
                      ? [Colors.white, Colors.white]
                      : [firstColorGrad, secondColorGrad],
                  begin: Alignment.centerLeft,
                  end: Alignment.centerRight,
                ).createShader(Rect.fromLTWH(0, 0, bounds.width, bounds.height)),
                child: icon,
              ),
              const SizedBox(height: 6),
              SizedBox(
                width: _getLabelWidthByIndex(index).toDouble(),
                child: Text(
                  textAlign: TextAlign.center,
                  label,
                  style: GoogleFonts.geologica(
                    fontSize: 12,
                    fontWeight: FontWeight.w400,
                    color: isSelected ? Colors.white : Colors.black,
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildIcon(CategoryModel category) {
    final imageUrl = 'http://141.148.245.77:5001/${category.image}';
    if (category.image.isEmpty) {
      return SvgPicture.asset('assets/specialists/overview.svg');
    }
    return SvgPicture.network(
      imageUrl,
      color: Colors.white,
      placeholderBuilder: (context) => const CircularProgressIndicator(),
    );
  }

  int _getLabelWidthByIndex(int index) {
    const widths = [28, 99, 74, 88, 74, 80, 81, 76, 71, 81];
    return widths[index];
  }
}

4. 确保SpecialistBloc正确处理事件

检查SpecialistBloc逻辑,保证收到UpdateSelectedIndexEvent后更新状态:

class SpecialistBloc extends Bloc<SpecialistEvent, SpecialistState> {
  final SpecialistRepository repository;

  SpecialistBloc(this.repository) : super(SpecialistInitial()) {
    on<UpdateSelectedIndexEvent>((event, emit) {
      emit(SpecialistCategorySelected(event.selectedIndex));
    });
    // 其他事件处理逻辑
  }
}

// 对应事件与状态类
class UpdateSelectedIndexEvent extends SpecialistEvent {
  final int selectedIndex;
  UpdateSelectedIndexEvent(this.selectedIndex);
}

class SpecialistCategorySelected extends SpecialistState {
  final int selectedIndex;
  SpecialistCategorySelected(this.selectedIndex);
}

关键修复总结

  • 统一状态源:完全使用Bloc管理选中状态,消除本地状态与Bloc状态的冲突
  • 正确初始化Bloc:将BlocProvider移到父组件,避免重复创建实例
  • 完善Bloc事件处理:确保选中索引变化能触发状态更新,让UI同步刷新

内容的提问来源于stack exchange,提问作者Назар Марцинко

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 23:13:15