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

Flutter中setState设置后searchResults列表未填充问题求助

Flutter搜索API数据无法填充到SliverList列表中

我正在开发一款调用API搜索食物的Flutter应用,搜索栏可以发送查询请求,并且已经能成功从API获取到食物数据,但这些数据无法填充到列表里。

显示搜索弹窗的代码

void _showSearchModal(BuildContext context) {
    showModalBottomSheet(
        context: context,
        isScrollControlled: true,
        builder: (BuildContext context) {
          return FractionallySizedBox(
              heightFactor: 0.8,
              child: Container(
                  color: Color(0xff2a2a2a),
                  child: CustomScrollView(
                    slivers: [
                      SliverAppBar(
                        // ... //
                      ),
                      SliverList(
                        delegate: SliverChildBuilderDelegate( // 这里是列表的构建器
                          (context, index) {
                            return FoodTile(
                                foodItem: searchResults[index]);
                          },
                          childCount: searchResults.length,
                        ),
                      )
                    ],
                  )));
        });
  }

搜索并更新结果的函数代码

List<FoodItem> searchResults = []; 

void _searchFood(String query) async {
    if (query.isEmpty) {
      setState(() {
        searchResults.clear();
      });
      return;
    }

    List<Map<String, dynamic>> fetchedResults = await fetchFood(query);

    List<FoodItem> foodItems = [];
    for (var foodMap in fetchedResults) {
      try {
        FoodItem foodItem = FoodItem.fromJson(foodMap);
        foodItems.add(foodItem);
      } catch (e) {
        print("Error creating FoodItem from JSON: $e");
      }
    }
    setState(() {
      searchResults = foodItems;
    });
  }

已尝试的解决方法

  • 将searchResults改为FoodTile列表,在SliverChildBuilderDelegate外创建这些FoodTile
  • 在设置状态后打印searchResults的长度和内容,确认其内容正确
  • 在SliverChildBuilderDelegate内创建局部变量并赋值为searchResults

问题原因及解决方法

问题出在showModalBottomSheet创建的弹窗是独立的子上下文,当前页面的setState只能刷新页面本身,无法触发弹窗内列表的重建。以下两种方法可以解决:

方法一:把弹窗内容封装成独立的StatefulWidget

将弹窗内的UI抽成单独的StatefulWidget,通过didUpdateWidget监听外部传入的搜索结果变化,触发内部重建:

class SearchModalContent extends StatefulWidget {
  final Function(String) onSearch;
  final List<FoodItem> results;

  const SearchModalContent({super.key, required this.onSearch, required this.results});

  @override
  State<SearchModalContent> createState() => _SearchModalContentState();
}

class _SearchModalContentState extends State<SearchModalContent> {
  late List<FoodItem> _localResults;

  @override
  void initState() {
    super.initState();
    _localResults = widget.results;
  }

  @override
  void didUpdateWidget(covariant SearchModalContent oldWidget) {
    super.didUpdateWidget(oldWidget);
    // 当外部结果更新时,同步并刷新
    if (widget.results != oldWidget.results) {
      setState(() {
        _localResults = widget.results;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return FractionallySizedBox(
      heightFactor: 0.8,
      child: Container(
        color: const Color(0xff2a2a2a),
        child: CustomScrollView(
          slivers: [
            SliverAppBar(
              // 这里添加你的搜索栏实现,调用widget.onSearch触发搜索
            ),
            SliverList(
              delegate: SliverChildBuilderDelegate(
                (context, index) => FoodTile(foodItem: _localResults[index]),
                childCount: _localResults.length,
              ),
            )
          ],
        ),
      ),
    );
  }
}

然后修改弹窗调用代码:

void _showSearchModal(BuildContext context) {
  showModalBottomSheet(
    context: context,
    isScrollControlled: true,
    builder: (context) => SearchModalContent(
      onSearch: _searchFood,
      results: searchResults,
    ),
  );
}

方法二:用ValueNotifier监听数据变化

把searchResults改成ValueNotifier,在弹窗内用ValueListenableBuilder监听数据更新,自动重建列表:

  1. 修改变量定义:
final ValueNotifier<List<FoodItem>> searchResults = ValueNotifier([]);
  1. 更新搜索函数:
void _searchFood(String query) async {
  if (query.isEmpty) {
    searchResults.value = [];
    return;
  }

  List<Map<String, dynamic>> fetchedResults = await fetchFood(query);
  List<FoodItem> foodItems = [];
  
  for (var foodMap in fetchedResults) {
    try {
      foodItems.add(FoodItem.fromJson(foodMap));
    } catch (e) {
      print("Error creating FoodItem from JSON: $e");
    }
  }
  
  searchResults.value = foodItems;
}
  1. 修改弹窗内的CustomScrollView部分:
CustomScrollView(
  slivers: [
    SliverAppBar(
      // ... 你的配置
    ),
    ValueListenableBuilder<List<FoodItem>>(
      valueListenable: searchResults,
      builder: (context, results, _) {
        return SliverList(
          delegate: SliverChildBuilderDelegate(
            (_, index) => FoodTile(foodItem: results[index]),
            childCount: results.length,
          ),
        );
      },
    ),
  ],
)

这样当searchResults.value更新时,ValueListenableBuilder会自动重建SliverList,数据就能正确显示在弹窗里了。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 09:55:57