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

如何在ReorderableListView中使用BlocBuilder?拖拽项报错

问题现象

在ReorderableListView中使用BlocBuilder时,拖拽列表项会抛出如下错误:

Could not find the correct Provider<ThemeCubit> above this BlocBuilder<ThemeCubit, ThemeState> Widget

该错误仅在拖拽触发时出现,如果使用context.read<ThemeCubit>()方式访问Bloc数据,不会触发任何报错。

初始问题示例代码:

List<int> _items = List<int>.generate(50, (int index) => index);

ReorderableListView(
      children: <Widget>[
        for (int index = 0; index < _items.length; index += 1)
          Container(
            key: Key(index.toString()),
            child: BlocBuilder<ThemeCubit, ThemeState>(
              builder: (___, ____) {
                return Text('Item ${_items[index]}');
              }
            )
          )
      ],
      onReorder: (int oldIndex, int newIndex) {},
    )

最小可复现代码(main.dart),拖拽列表项即可触发问题:

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

class SimpleCubit extends Cubit<int>
{
  SimpleCubit() : super(0);
}

void main() async {
  runApp(MyTrip());
}

class AppName extends StatelessWidget {
  AppName({super.key});

  final List<int> _items = List<int>.generate(50, (int index) => index);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: MultiBlocProvider(
          providers: [
            BlocProvider<SimpleCubit>(
              create: (_) => SimpleCubit()
            ),
          ],
          child: ReorderableListView(
            children: <Widget>[
              for (int index = 0; index < _items.length; index += 1)
                Container(
                  key: Key(index.toString()),
                  child: BlocBuilder<SimpleCubit, int>(
                    builder: (_, state) {
                      return Text(state.toString());
                    }
                  )
                )
            ],
            onReorder: (int oldIndex, int newIndex) {},
          ),
        ),
      )
    );
  }
}
问题原因

Flutter中ReorderableListView拖拽列表项时,被拖拽的组件会临时挂载到应用根级别的Overlay上渲染,脱离原本的组件树上下文。

  • context.read()是一次性读取逻辑,仅在组件首次build时执行上下文查找,拖拽过程中不会重新触发查找逻辑,因此不会报错。
  • BlocBuilder会持续监听Bloc状态变化,组件被移到Overlay后会触发重新build,此时会从Overlay对应的上下文向上查找BlocProvider,而原代码中BlocProvider挂载在Scaffold的body层级,不在Overlay的父链上,自然找不到对应实例,直接抛出Provider查找失败的错误。
解决方案

二选一即可:

  • 将BlocProvider的挂载位置上移到MaterialApp的上层,保证Overlay、页面组件都处于BlocProvider的子树范围内,无论组件挂载在哪个位置都能通过上下文找到对应的Bloc实例。
  • 提前在列表外层获取Bloc实例,通过构造函数参数传递给每个列表项,列表项内部直接使用传入的Bloc实例做状态监听,不通过上下文做Provider查找。

方案1修复后的可运行代码:

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

class SimpleCubit extends Cubit<int> {
  SimpleCubit() : super(0);
}

void main() async {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    // BlocProvider上移到MaterialApp上层,覆盖全局上下文包括Overlay
    return MultiBlocProvider(
      providers: [
        BlocProvider<SimpleCubit>(
          create: (_) => SimpleCubit()
        ),
      ],
      child: MaterialApp(
        home: Scaffold(
          body: AppName(),
        )
      ),
    );
  }
}

class AppName extends StatelessWidget {
  AppName({super.key});

  final List<int> _items = List<int>.generate(50, (int index) => index);

  @override
  Widget build(BuildContext context) {
    return ReorderableListView(
      children: <Widget>[
        for (int index = 0; index < _items.length; index += 1)
          Container(
            key: Key(index.toString()),
            child: BlocBuilder<SimpleCubit, int>(
              builder: (_, state) {
                return Text(state.toString());
              }
            )
          )
      ],
      onReorder: (int oldIndex, int newIndex) {},
    );
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 03:33:31