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

Flutter开发疑难:ListView.builder的Expanded使用抉择、Provider状态不更新及RenderFlex溢出修复

解决ListView加载提示与自动加载的问题

咱们一步一步来搞定你遇到的这些问题:

1. 修复isLoading不更新的问题

你在MyStockSet的build方法里用了Provider.of<StockSet>(context,listen: false),这个写法会让组件完全不监听StockSet的状态变化,所以哪怕isBusyLoading改了,UI也不会跟着刷新。把它改成:

var stockSet = context.watch<StockSet>();

这样当StockSet里的isBusyLoading或者stocks变化时,组件会自动重建,加载提示的状态就能正确同步了。

2. 解决RenderFlex溢出 + 加载提示位置问题

之前把ListView.builder和加载提示放在Column里再套Expanded的思路走偏了——ListView本身就是可滚动容器,应该把加载提示直接作为列表的最后一个item,这样既不会有溢出,提示也会自然跟在列表内容后面。

修改_StockListView的build方法:

class _StockListViewState extends State<_StockListView> {
  late ScrollController _scrollController;

  @override
  void initState() {
    super.initState();
    _scrollController = ScrollController();
    // 后续自动加载要用到这个控制器
  }

  @override
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    var stockSet = context.watch<StockSet>();
    return ListView.builder(
      controller: _scrollController,
      // 去掉shrinkWrap,让ListView自适应内容高度
      // shrinkWrap: true,
      itemCount: stockSet.stocks.length + (stockSet.isBusyLoading ? 1 : 0),
      itemBuilder: (context, index) {
        // 如果是最后一个item且正在加载,返回加载提示
        if (index == stockSet.stocks.length) {
          return LoadingStockListItemNotif(isLoading: stockSet.isBusyLoading);
        }
        // 否则返回正常列表项
        return InkWell(
          child: StockListItem(
            stock: stockSet.stocks[index],
          ),
          onTap: () => Navigator.pushNamed(
            context,
            '/stocks/stock',
            arguments: ScreenArguments(stockSet.stocks[index]),
          ),
        );
      },
    );
  }
}

同时简化MyStockSet里的body结构,去掉多余嵌套:

body: Column(
  children: [
    Row(
      children: [
        ElevatedButton(
          onPressed: () => developer.log('Btn pressed.'),
          child: Text('Btn')),
      ],
    ),
    // 直接用Expanded让列表占满剩余空间
    Expanded(child: _StockListView()),
  ],
),

这样就不会有RenderFlex溢出,加载提示会乖乖跟在列表末尾,内容满屏时自动滚动。

3. 实现自动加载(滚动触底触发)

接下来搞定自动加载,用ScrollController监听滚动位置,当用户滚动到接近底部时触发数据请求:

在_StockListViewState里添加滚动监听逻辑:

@override
void initState() {
  super.initState();
  _scrollController = ScrollController();
  _scrollController.addListener(_scrollListener);
}

void _scrollListener() {
  var stockSet = context.read<StockSet>();
  // 正在加载时不重复请求
  if (stockSet.isBusyLoading) return;

  // 计算当前滚动位置与底部的距离
  final currentPos = _scrollController.position.pixels;
  final maxPos = _scrollController.position.maxScrollExtent;

  // 距离底部200像素时触发加载(可根据需求调整阈值)
  if (currentPos >= maxPos - 200) {
    stockSet.fetch();
  }
}

另外建议把StockSet里的isBusyLoading初始化改得更规范:

// 去掉late,直接初始化
bool isBusyLoading = false;

这样滚动到接近底部时,就会自动触发fetch()加载新数据,直到填满屏幕。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 22:57:40