【问题标题】:Flutter Dart constructorFlutter Dart 构造函数
【发布时间】:2018-12-03 10:28:21
【问题描述】:

在flutter示例页面中,有一个名为“Sending Data to a new screen”的项目。我对第 65 行的构造函数有疑问。

Sending Data to a new screen

  // In the constructor, require a Todo
  DetailScreen({Key key, @required this.todo}) : super(key: key);

什么是超级(键:键)?请我解释一下整行。代码在这里....

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

class Todo {
  final String title;
  final String description;

  Todo(this.title, this.description);
}

void main() {
  runApp(MaterialApp(
    title: 'Passing Data',
    home: TodosScreen(
      todos: List.generate(
        20,
            (i) => Todo(
          'Todo $i',
          'A description of what needs to be done for Todo $i',
        ),
      ),
    ),
  ));
}

class TodosScreen extends StatelessWidget {
  final List<Todo> todos;

  TodosScreen({Key key, @required this.todos}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Todos'),
      ),
      body: ListView.builder(
        itemCount: todos.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(todos[index].title),
            // When a user taps on the ListTile, navigate to the DetailScreen.
            // Notice that we're not only creating a DetailScreen, we're
            // also passing the current todo through to it!
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => DetailScreen(todo: todos[index]),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

class DetailScreen extends StatelessWidget {
  // Declare a field that holds the Todo
  final Todo todo;

  // In the constructor, require a Todo
  DetailScreen({Key key, @required this.todo}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // Use the Todo to create our UI
    return Scaffold(
      appBar: AppBar(
        title: Text("${todo.title}"),
      ),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Text('${todo.description}'),
      ),
    );
  }
}

【问题讨论】:

标签: dart flutter


【解决方案1】:

这里是我对Flutter构造函数的详细理解

一个类中只有一个构造函数,但可以有多个工厂方法。

  1. 位置参数

这些是传统的参数,按照构造函数中定义的顺序传递。

MyWidget(String param1, int param2) {
  debugPrint("Positional Optional $param1, $param2");
}

打电话

MyWidget('Param 1 Passed',2,)
  1. 命名参数

标准做法在我们引用param_name:value 的地方随波逐流。下面是例子

MyWidget(String param1, {int? param2}) {
  debugPrint("Positional Optional $param1, $param2");
}

打电话

MyWidget('Param 1 Passed',param2: 2,)
  1. 可选参数

可选参数用方括号括起来[ ]

MyWidget(String param1, [int? param2]) {
  debugPrint("Positional Optional, $param1, $param2");
}

现在,由于参数 2 介于 [ ] 之间,因此参数 2 是可选的。所以下面是两种调用方式。

方式1:不传递参数2,构造函数如果不传递则将其视为null。

MyWidget('Param 1 Passed',)

方式2:同时传递两个参数

MyWidget('Param 1 Passed',123)
  1. 必需参数

当强制传递参数时,我们可以使用关键字required{ }

规则required 不能与可选参数一起使用,即[ ]。显然,如果它是必需的参数,它不能被包裹在可选的[ ]

MyWidget(String param1, {required int param2}) {
  debugPrint("Positional Optional, $param1, $param2");
}

打电话

MyWidget('Param 1 Passed', param2: ,123)
  1. 冒号

冒号主要用于构造函数参数的初始化。

规则:不能对参数进行初始化,在这种情况下只能对类strTxt中声明的变量进行初始化。

String strTxt;

MyWidget(String param1, int param2) : strTxt = "Some Value" {
  debugPrint("Positional Optional, $param1, $param2, $strTxt");
}

打电话

MyWidget('Param 1 Passed', 123)

结果:注意我们在: 中设置的debugPrint 日志中的“某些值”

Positional Optional, Param 1 Passed, 123, Some value

  1. 身体 它只是花括号内的内容。

  1. 通过参数直接赋值

您可能还记得在 Android 和其他一些语言中,我们在参数中传递值,然后将其等同于类级别变量。这在 Flutter 中完全通过使用 this.class_level_var_name 来缩短。它只是将传递的值分配给该类级别的变量。

MyWidget(this.strTxt, int param2) {
  debugPrint("Positional Optional, $strTxt, $param2");
}

打电话

MyWidget('Param 1 Passed', 123)

结果

Positional Optional, Param 1 Passed, 123
  1. 可空 ?required 声明

仅当我们将参数设为可选[ ] 或命名为{ } 时才需要声明可为空,对于正常定义的参数,不需要required 或可为空的?

param2中通知?

MyWidget(this.strTxt, {int? param2}) {
  debugPrint("Positional Optional, $strTxt, $param2, $strTxt");
}

通知required

MyWidget(this.strTxt, {required int param2}) {
  debugPrint("Positional Optional, $strTxt, $param2, $strTxt");
}

【讨论】:

    【解决方案2】:

    通过创建与其类同名的函数来声明构造函数(可选地,还可以添加一个附加标识符,如Named constructors 中所述)。最常见的构造函数形式,生成构造函数,会创建一个类的新实例:

    class Point {
      double x, y;
    
      Point(double x, double y) {
        // There's a better way to do this, stay tuned.
        this.x = x;
        this.y = y;
      }
    }
    

    this 关键字指的是当前实例。

    注意:仅在名称冲突时使用。否则,Dart 样式会省略 this。

    将构造函数参数分配给实例变量的模式非常普遍,Dart 有语法糖让它变得简单:

    class Point {
      double x, y;
    
      // Syntactic sugar for setting x and y
      // before the constructor body runs.
      Point(this.x, this.y);
    }
    

    默认构造函数

    如果您不声明构造函数,则会为您提供默认构造函数。默认构造函数没有参数,并调用超类中的无参数构造函数。

    构造函数不会被继承

    子类不会从它们的超类继承构造函数。不声明构造函数的子类只有默认(无参数,无名称)构造函数。

    命名构造函数

    使用命名构造函数为一个类实现多个构造函数或提供额外的清晰度:

    class Point {
      double x, y;
    
      Point(this.x, this.y);
    
      // Named constructor
      Point.origin() {
        x = 0;
        y = 0;
      }
    }
    

    请记住,构造函数不是继承的,这意味着超类的命名构造函数不会被子类继承。如果要使用在超类中定义的命名构造函数创建子类,则必须在子类中实现该构造函数。

    更多信息请见:https://dart.dev/guides/language/language-tour#constructors

    【讨论】:

      【解决方案3】:

      这是一个补充Günter Zöchbauer's explanation的例子。它是Align 小部件的构造函数。

      class Align extends SingleChildRenderObjectWidget {
      
        // constructor
        const Align({
          Key key,                                                   // named parameter
          this.alignment = Alignment.center,                         // named parameter
          this.widthFactor,                                          // named parameter
          this.heightFactor,                                         // named parameter
          Widget child                                               // named parameter
        }) : assert(alignment != null),                              // initializer list
             assert(widthFactor == null || widthFactor >= 0.0),      // initializer list
             assert(heightFactor == null || heightFactor >= 0.0),    // initializer list
             super(key: key, child: child);                          // initializer list
      
        // class variables
        final AlignmentGeometry alignment;
        final double widthFactor;
        final double heightFactor;
      

      更多注释:

      • 不带this.前缀的参数是超类的变量。
      • this. 开头的参数是当前类中定义的变量。

      【讨论】:

        【解决方案4】:

        构造函数有两个命名参数。
        默认情况下,命名参数是可选的。
        @required 是 Dart 分析器识别的注解,如果在构建时调用时未传递,则会产生警告(在运行时无效)。

        : 启动“初始化列表”,这是一个逗号分隔的表达式列表,在超类的构造函数之前执行,因此也在构造函数主体之前执行。
        它通常用于使用断言检查参数值并使用计算值初始化最终字段。
        一个限制是,表达式不能读取访问 this.(隐式或显式),因为在执行超级构造函数之前对象初始化未完成。

        如果省略,初始化器中的最后一个元素是对超类的默认构造函数的隐式调用,或者如果给定,则是对当前类或超类的特定构造函数的调用。

        在您问题的示例中,传递给构造函数的key 参数被转发到超类的未命名构造函数的命名参数key

        【讨论】:

          猜你喜欢
          • 2020-10-30
          • 1970-01-01
          • 2018-09-16
          • 1970-01-01
          • 2018-08-25
          • 2019-12-19
          • 1970-01-01
          • 2020-10-06
          • 2020-05-05
          相关资源
          最近更新 更多