dart:super-给父类属性赋值

子类在构造时必须给父类属性赋值

  1. 子类继承了父类的属性
    • 在内存里,子类对象包含父类那部分字段。
    • 所以在创建子类对象时,必须把父类的那部分也初始化好。
  2. Dart 的 final 和空安全 (null-safety)
    • 如果父类里有 final 或者 非空类型 的字段(比如 final Key key; 或者 Key key;),那么 Dart 要求它们必须在构造时被初始化。
    • 否则对象会处于“不完整状态”,编译器直接报错。
  3. super(...) 的作用
    • super(...) 就是子类在创建时,调用父类构造函数来初始化父类字段。
    • 这样父类的属性才能有值,保证对象完整、安全。
class Parent {
  final String name;
  Parent(this.name); // 必须传 name,否则报错
}

class Child extends Parent {
  final int age;

  // ❌ 错误:没有调用 super,name 没有赋值
  // Child(this.age);

  // ✅ 正确:调用父类构造函数,初始化 name
  Child(String name, this.age) : super(name);
}

void main() {
  var c = Child("Alice", 20);
  print("${c.name}, ${c.age}"); //Alice, 20
}

子类在实例化的时候只是创建了一个对象

var c = Child("Alice", 20);:内存里创建的只是 一个 Child 实例。这个实例里面包含了 Parent 部分,但那不是单独的对象,而是 同一个对象中的一部分内存区域

classDiagram
    class Parent {
        +String name
        +Parent(name)
    }

    class Child {
        +int age
        +Child(name, age)
    }

    Child --|> Parent : extends

    %% 内存结构
    class ChildInstance["Child Instance"] {
        +Parent 部分: name = "Alice"
        +Child 部分: age = 20
    }

    ChildInstance ..> Child : is instance of
    ChildInstance ..> Parent : contains Parent fields

实际情况

MaterialDemoApp({super.key})

在flutter里面,我们经常可以看到这样的写法:

void main() {
  runApp(const MaterialDemoApp());
}

class MaterialDemoApp extends StatefulWidget {
  const MaterialDemoApp({super.key});

  @override
  State<MaterialDemoApp> createState() => _MaterialDemoAppState();
}

这是因为MaterialDemoApp继承了StatefulWidget,而StatefulWidget继承WidgetWidget里面有一个key属性

@immutable
abstract class Widget extends DiagnosticableTree {
  /// Initializes [key] for subclasses.
  const Widget({this.key}); 
  final Key? key; // 注意这里是可空类型
}

但是,实际上我们并没有给key传值,因为key是可以为空的。

没传 key,实际上就是给父类构造函数里的 key 传了 null

由于 Key? 允许为空,所以不会报错。


super.key的这种写法,其实是一种语法糖的写法。

传统写法(2.17 之前)

class MaterialDemoApp extends StatefulWidget {
  const MaterialDemoApp({Key? key}) : super(key: key);
}
  • 子类的构造函数接收 Key? key 参数。

  • : super(key: key) 把它传递给父类的构造函数。

    • 第一个 key 是父类的参数名称
    • 第二个key是子类的传参

语法糖写法(2.17+):与上面等价

class MaterialDemoApp extends StatefulWidget {
  const MaterialDemoApp({super.key});
}

它的作用就是:自动把子类构造函数的参数传递给父类构造函数


class MyWidget extends StatelessWidget {
  final String title;

  // super.key 把 key 参数直接传给 StatelessWidget 的构造函数
  const MyWidget({super.key, required this.title});

  @override
  Widget build(BuildContext context) {
    return Text(title);
  }
}

这里 super.key 就相当于帮你写了:

const MyWidget({Key? key, required this.title}) : super(key: key);

FormField进行封装

class InputFormFieldWidget extends FormField<String> {
  InputFormFieldWidget({
    super.key,
    required this.labelText,
    this.tipText,
    this.initValue,
    this.onChanged,
    this.controller,
    this.placeholder,
    this.prefix,
    this.suffix,
    this.obscureText,
    this.cleanable,
    this.keyboardType,
    this.autofocus,
    Function(String?)? validator,
  }) : super(
          initialValue: initValue ?? controller?.text,
          validator: (val) {
            if (validator != null) {
              return validator(val);
            }
            return null;
          },
          builder: (field) {
            void onChangedHandler(String value) {
              field.didChange(value);
              onChanged?.call(value);
            }

            return <Widget>[
              // 字段说明
              TextWidget.label(
                labelText,
              ).paddingLeft(AppSpace.card),

              // 输入框
              InputWidget(
                placeholder: placeholder,
                prefix: prefix,
                suffix: suffix,
                controller: controller,
                obscureText: obscureText ?? false,
                cleanable: cleanable ?? true,
                onChanged: onChangedHandler,
              ),

              // 提示词
              if (tipText != null)
                TextWidget.muted(
                  tipText,
                ).paddingLeft(AppSpace.card),

              // 错误提示
              if (field.errorText != null)
                TextWidget.muted(
                  field.errorText!,
                  color: field.context.colors.scheme.error,
                ).paddingLeft(AppSpace.card),
            ].toColumnSpace(
              crossAxisAlignment: CrossAxisAlignment.start,
            );
          },
        );

  /// 字段文字
  final String labelText;

  /// 提示词
  final String? tipText;

  /// 初始值
  final String? initValue;

  /// 输入框控制器
  final TextEditingController? controller;

  /// 占位符
  final String? placeholder;

  /// 前缀
  final Widget? prefix;

  /// 后缀
  final Widget? suffix;

  /// 是否隐藏文本
  final bool? obscureText;

  /// 是否可清空
  final bool? cleanable;

  /// 值被改变时的回调
  final void Function(String?)? onChanged;

  /// 输入法类型
  final TextInputType? keyboardType;

  /// 自动焦点
  final bool? autofocus;

  @override
  InputFormWidgetFieldState createState() => InputFormWidgetFieldState();
}

class InputFormWidgetFieldState extends FormFieldState<String> {
  @override
  InputFormFieldWidget get widget => super.widget as InputFormFieldWidget;

}

这里使用 super(...) 的目的,就是在 子类构造函数里把参数传递给父类 FormField 的构造函数,从而对 FormField<String> 进行封装,做一个自定义表单组件。

InputFormFieldWidget 继承自 FormField<String>,而 FormField 的构造函数大致是这样的:

class FormField<T> extends StatefulWidget {
  /// Creates a single form field.
  const FormField({
    super.key,
    required this.builder,
    this.onSaved,
    this.forceErrorText,
    this.validator,
    this.errorBuilder,
    this.initialValue,
    this.enabled = true,
    AutovalidateMode? autovalidateMode,
    this.restorationId,
  }) : autovalidateMode = autovalidateMode ?? AutovalidateMode.disabled;

构造函数签名 :......:初始化列表

在 Dart 里,: 出现在构造函数签名后,叫 初始化列表 (initializer list)

其中的 super(...) 就是 调用父类构造函数,并且在子类构造函数执行之前运行。

class Parent {
  Parent(String msg) {
    print("Parent 构造: $msg");
  }
}

class Child extends Parent {
  Child(String msg) : super(msg) {
    print("Child 构造");
  }
}

void main() {
  Child("Hello");
}

/*输出:
Parent 构造: Hello
Child 构造
*/

可以看到:父类的构造函数 执行,子类的构造函数体 执行。

关于更多初始化列表的内容可以查看:面向对象那个章节

×

喜欢就点赞,疼爱就打赏