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

Flutter泛型Builder函数类型不匹配问题求助

泛型Builder函数类型不匹配问题解决

问题描述

遇到类型转换错误:

type '(BuildContext, AppResources?, Widget?) => Widget' is not a subtype of type '((BuildContext, Object?, Widget?) => Widget)?'

明明AppResources?是Object?的子类型,为什么整个方法签名无法兼容?使用非泛型的SimpleBuilder时可以正常运行,请问如何正确使用泛型的GenericsBuilder<GEN>?

相关代码定义

typedef SimpleBuilder = Widget Function(BuildContext context, Object? object, Widget? child);
typedef GenericsBuilder<GEN extends Object> = Widget Function(BuildContext context, GEN? object, Widget? child);

class AppLocalization<RES extends Object> extends StatefulWidget {
  final Widget? child;
  final SimpleBuilder? builder;
  //final GenericsBuilder<RES>? builder;
  final Locale? defaultLocale;

  const AppLocalization({this.child, this.builder, this.defaultLocale, super.key})
      : assert(builder != null || child != null);

  @override
  AppLocalizationState createState() => AppLocalizationState<RES>();
}

class AppLocalizationState<RES extends Object> extends State<AppLocalization> {
  RES? _resources;

  @override
  void initState() {
    super.initState();
    _resources = AppResources(); // 实际项目中初始化逻辑不同
  }

  @override
  Widget build(BuildContext context) {
    return widget.builder?.call(context, _resources, widget.child) ?? widget.child!;
  }
}

使用代码

@override
Widget build(BuildContext context) {
  return AppLocalization<AppResources>(
    builder: (context, res, child) => res == null
        ? const SizedBox.shrink()
        : const Text('hurray')
    );
}

原因分析

Dart的函数参数类型遵循逆变规则:当需要一个接收Object?参数的函数时,传入的函数必须能接收Object?的超类型,而不是子类型。你传入的函数只接受AppResources?(Object?的子类型),无法处理任意Object?类型的参数,因此类型不兼容。

正确实现方案

调整AppLocalization类的定义,绑定泛型Builder类型,并修正State的泛型关联:

修正后的完整代码

typedef SimpleBuilder = Widget Function(BuildContext context, Object? object, Widget? child);
typedef GenericsBuilder<GEN extends Object> = Widget Function(BuildContext context, GEN? object, Widget? child);

class AppLocalization<RES extends Object> extends StatefulWidget {
  final Widget? child;
  // 替换为泛型Builder类型,与类的泛型参数RES绑定
  final GenericsBuilder<RES>? builder;
  final Locale? defaultLocale;

  const AppLocalization({this.child, this.builder, this.defaultLocale, super.key})
      : assert(builder != null || child != null);

  @override
  AppLocalizationState<RES> createState() => AppLocalizationState<RES>();
}

// 显式指定State的泛型参数为AppLocalization<RES>,确保widget类型正确推断
class AppLocalizationState<RES extends Object> extends State<AppLocalization<RES>> {
  RES? _resources;

  @override
  void initState() {
    super.initState();
    // 实际项目中若根据泛型创建实例,可省略as RES转换
    _resources = AppResources() as RES;
  }

  @override
  Widget build(BuildContext context) {
    return widget.builder?.call(context, _resources, widget.child) ?? widget.child!;
  }
}

使用代码无需修改

@override
Widget build(BuildContext context) {
  return AppLocalization<AppResources>(
    builder: (context, res, child) => res == null
        ? const SizedBox.shrink()
        : const Text('hurray')
    );
}

关键要点

  1. 将AppLocalization的builder字段类型改为GenericsBuilder<RES>,让Builder的参数类型与类的泛型参数RES强绑定,确保类型一致。
  2. 修正AppLocalizationState的泛型继承为State<AppLocalization<RES>>,避免泛型信息丢失,保证widget的类型被正确推断。
  3. 初始化_resources时,若实际是具体类型实例,需用as RES做显式转换(动态创建泛型实例的场景可省略)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 08:37:35