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

Flutter中Autocomplete组件重建时为何不更新建议列表?

问题原因分析

你的问题核心在于Autocomplete组件的optionsBuilder回调触发机制:这个回调只会在文本编辑控制器的内容发生变化时自动执行,而Bloc状态更新导致的BlocBuilder重建,并不会主动触发optionsBuilder重新计算选项。

虽然BlocBuilder每次状态变化都会重新构建Autocomplete,但只要文本输入框的内容没有改变,Autocomplete就不会调用optionsBuilder,也就不会读取新的suggestions数据,导致建议列表无法更新。

修复方案

这里提供两种有效修复方式,你可以根据需求选择:

方式一:让optionsBuilder直接读取Bloc最新状态

修改optionsBuilder,不再依赖build方法中的suggestions变量,而是直接在回调内获取Bloc的当前状态。这样每次optionsBuilder被调用时,都会拿到最新的建议数据:

optionsBuilder: (TextEditingValue textEditingValue) {
  // 直接从Bloc读取当前状态
  final currentState = context.read<PlaceResearchBloc>().state;
  List<String> suggestions = [];
  
  if (currentState is PlaceSuggestionsLoaded) {
    suggestions = currentState.placeSuggestions.map((p) => p.name).toList();
  } else if (currentState is PlaceSuggestionsError) {
    // 可以在这里处理错误状态,比如返回空列表或提示文本
    suggestions = [];
  }

  print("Autocomplete suggestions :  $suggestions");
  return suggestions.where(
    (s) => s.toLowerCase().contains(textEditingValue.text.toLowerCase()),
  );
},

方式二:通过Key强制Autocomplete重建

给Autocomplete添加一个与suggestions绑定的Key,当suggestions变化时,Key也会变化,从而强制Autocomplete重新实例化并调用optionsBuilder:

builder: (context, state) {
  List<String> suggestions = [];
  Key autocompleteKey = const ValueKey("initial");

  if (state is PlaceSuggestionsLoaded) {
    suggestions = state.placeSuggestions.map((p) => p.name).toList();
    // 用suggestions列表作为Key,确保只有数据变化时才重建
    autocompleteKey = ValueKey<List<String>>(suggestions);
  } else if (state is PlaceSuggestionsError) {
    autocompleteKey = ValueKey<String>(state.message);
  }

  print("Bloc builder Suggestions :  $suggestions");

  return Autocomplete<String>(
    key: autocompleteKey, // 添加绑定的Key
    optionsBuilder: (TextEditingValue textEditingValue) {
      print("Autocomplete suggestions :  $suggestions");
      return suggestions.where(
        (s) => s.toLowerCase().contains(textEditingValue.text.toLowerCase()),
      );
    },
    // 其他属性保持不变
  );
}
完整修复后的代码

结合方式一(更优雅,无需强制重建组件),同时添加加载状态提示,完整代码如下:

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class Place {
  final String name;
  Place({required this.name});
}

abstract class PlaceResearchState {}

class PlaceResearchInitial extends PlaceResearchState {}

class PlaceSuggestionsLoading extends PlaceResearchState {}

class PlaceSuggestionsLoaded extends PlaceResearchState {
  final List<Place> placeSuggestions;
  PlaceSuggestionsLoaded(this.placeSuggestions);
}

class PlaceSuggestionsError extends PlaceResearchState {
  final String message;
  PlaceSuggestionsError(this.message);
}

class PlaceResearchBloc extends Cubit<PlaceResearchState> {
  PlaceResearchBloc() : super(PlaceResearchInitial());

  void searchSuggestions(String query) {
    if (query.isEmpty) {
      emit(PlaceResearchInitial());
      return;
    }
    emit(PlaceSuggestionsLoading());
    Future.delayed(const Duration(milliseconds: 500), () {
      final suggestions = _fetchPlaceSuggestions(query);
      if (suggestions.isNotEmpty) {
        emit(PlaceSuggestionsLoaded(suggestions));
      } else {
        emit(PlaceSuggestionsError("No suggestion found"));
      }
    });
  }

  List<Place> _fetchPlaceSuggestions(String query) {
    List<Place> allPlaces = [
      const Place(name: 'New York'),
      const Place(name: 'Paris'),
      const Place(name: 'London'),
      const Place(name: 'Hamburg'),
      const Place(name: 'Barcelona')
    ];
    return allPlaces.where((place) => place.name.toLowerCase().contains(query.toLowerCase())).toList();
  }
}

class SearchBarWithBlocListener extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Autocomplete MRE")),
      body: Stack(
        children: [
          Positioned(
            top: 20,
            left: 10,
            right: 10,
            child: Material(
              elevation: 4,
              borderRadius: BorderRadius.circular(8),
              child: BlocProvider(
                create: (_) => PlaceResearchBloc(),
                child: BlocBuilder<PlaceResearchBloc, PlaceResearchState>(
                  builder: (context, state) {
                    // 处理加载状态,显示loading指示器
                    Widget? loadingIndicator;
                    if (state is PlaceSuggestionsLoading) {
                      loadingIndicator = const Positioned(
                        right: 10,
                        top: 8,
                        child: SizedBox(
                          width: 20,
                          height: 20,
                          child: CircularProgressIndicator(strokeWidth: 2),
                        ),
                      );
                    }

                    return Stack(
                      children: [
                        Autocomplete<String>(
                          optionsBuilder: (TextEditingValue textEditingValue) {
                            final currentState = context.read<PlaceResearchBloc>().state;
                            List<String> suggestions = [];
                            
                            if (currentState is PlaceSuggestionsLoaded) {
                              suggestions = currentState.placeSuggestions.map((p) => p.name).toList();
                            }

                            print("Autocomplete suggestions :  $suggestions");
                            return suggestions.where(
                              (s) => s.toLowerCase().contains(textEditingValue.text.toLowerCase()),
                            );
                          },
                          displayStringForOption: (String option) => option,
                          onSelected: (String selection) {
                            print('Selected suggestion : $selection');
                          },
                          fieldViewBuilder: (BuildContext context, TextEditingController fieldTextEditingController, FocusNode fieldFocusNode, VoidCallback onFieldSubmitted) {
                            return TextField(
                              controller: fieldTextEditingController,
                              focusNode: fieldFocusNode,
                              onChanged: (value) {
                                context.read<PlaceResearchBloc>().searchSuggestions(value);
                              },
                              decoration: const InputDecoration(
                                hintText: "Search for a place",
                                border: InputBorder.none,
                                contentPadding: EdgeInsets.symmetric(horizontal: 15, vertical: 10),
                              ),
                            );
                          },
                        ),
                        if (loadingIndicator != null) loadingIndicator,
                      ],
                    );
                  },
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 11:59:53