如何在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
相关产品推荐
相关产品推荐

