Flutter中Dart回调传入子类参数引发TypeError问题求助
问题原因分析
出现Type '(Job) => ListTile' is not a subtype of type '(Model) => Widget'错误,核心有两个问题:
_ListPageBuilderState未跟随父类使用泛型<T extends Model>,导致widget类型被推断为原始ListPageBuilder而非带泛型的ListPageBuilder<T>,此时listItemBuilder被视为Widget Function(Model),和传入的Widget Function(Job)类型不兼容。items列表声明为动态类型var items = [],未约束为List<T>,仓库返回的数据无法匹配泛型类型,传给listItemBuilder时触发类型错误。
解决方案
按以下步骤修改代码:
1. 给State类添加泛型支持
修改_ListPageBuilderState的定义,让它继承自带泛型的State<ListPageBuilder<T>>:
class _ListPageBuilderState<T extends Model> extends State<ListPageBuilder<T>> { // 其余代码后续调整 }
2. 约束items列表的类型
把items的声明从动态类型改为List<T>:
List<T> items = [];
3. 修正fetchItems中的类型转换
在fetchItems方法中,将仓库返回的结果转换为List<T>(如果repository.list本身支持泛型可直接使用,否则用cast<T>()强制转换,前提是确定返回实例为T类型):
void fetchItems() async { if (nextPage == null) return; try { var response = await widget.repository.list(getFilters()); var newItems = response.results.cast<T>(); nextPage = response.next != null ? getPage(response.next!) : null; _isLoading = false; setState(() { items.addAll(newItems); }); } on HttpException catch (e) { print(e); } }
4. 修正StatefulWidget的createState方法
更新ListPageBuilder的createState方法,返回带泛型的State实例:
@override State<ListPageBuilder<T>> createState() => _ListPageBuilderState<T>();
修改后的完整ListPageBuilder代码
class ListPageBuilder<T extends Model> extends StatefulWidget { final bool canView; final bool canAdd; final String modelVerboseName; final Repository repository; final void Function()? onItemTap; final Widget Function(T) listItemBuilder; const ListPageBuilder({ super.key, required this.modelVerboseName, required this.canView, required this.canAdd, required this.repository, required this.listItemBuilder, this.onItemTap, }); @override State<ListPageBuilder<T>> createState() => _ListPageBuilderState<T>(); } class _ListPageBuilderState<T extends Model> extends State<ListPageBuilder<T>> { @override void initState() { super.initState(); fetchItems(); } List<T> items = []; int? nextPage = 1; bool _isLoading = true; final _scrollController = ScrollController(); final _searchController = TextEditingController(); String? getSearchTerm() { return _searchController.text.length >= 3 ? _searchController.text : null; } Map<String, dynamic> getFilters() { var filters = <String, dynamic>{"page": nextPage}; var searchTerm = getSearchTerm(); if (searchTerm != null) { filters["search"] = searchTerm; } return filters; } void fetchItems() async { if (nextPage == null) return; try { var response = await widget.repository.list(getFilters()); var newItems = response.results.cast<T>(); nextPage = response.next != null ? getPage(response.next!) : null; _isLoading = false; setState(() { items.addAll(newItems); }); } on HttpException catch (e) { print(e); } } @override Widget build(BuildContext context) { _scrollController.addListener(() { var nextPageTriggerLimit = 0.75 * _scrollController.position.maxScrollExtent; if (_scrollController.position.pixels > nextPageTriggerLimit && !_isLoading) { _isLoading = true; fetchItems(); } }); return Scaffold( bottomNavigationBar: const AppNavigationBar(), floatingActionButton: widget.canAdd ? FloatingActionButton( onPressed: () { Navigator.pushNamed(context, "/customers/create"); }, child: const Icon(Icons.add), ) : null, body: SafeArea( child: Center( child: Padding( padding: const EdgeInsets.all(8), child: widget.canView ? Column( children: [ TextFormField( decoration: InputDecoration( hintText: "Rechercher un ${widget.modelVerboseName}", suffixIcon: const Icon(Icons.search), ), controller: _searchController, onChanged: (value) { if (value.isEmpty || value.length >= 3) { setState(() { items = []; nextPage = 1; fetchItems(); }); } }, ), (items.isEmpty && _isLoading) ? const SizedBox( width: 50, height: 50, child: CircularProgressIndicator(), ) : Expanded( child: ListView.separated( controller: _scrollController, itemCount: items.length, itemBuilder: (context, index) { var item = items[index]; return widget.listItemBuilder(item); }, separatorBuilder: (context, index) => const Divider(), ), ) ], ) : const Center( child: Text( "Vous n'avez pas la permission de consulter cette page", ), ), ), ), ), ); } }
说明
- 泛型State类确保
widget类型为ListPageBuilder<T>,此时listItemBuilder会被正确识别为Widget Function(T),和传入的(Job) => ListTile完全匹配。 - 约束
items为List<T>后,传给listItemBuilder的每个item都是明确的T类型(此处为Job),不会再触发类型转换错误。
内容的提问来源于stack exchange,提问作者Parfait_D
相关产品推荐
相关产品推荐

