flutter基础-dart语法1

2024/3/13 flutterdart

Dart语言 (opens new window)是由Google开发的一种用于构建Web、移动和服务器端应用程序的现代化编程语言。它具有强类型面向对象支持异步编程等特性,被广泛应用于Flutter移动应用开发框架中。

🌙 dart语言特点

  • 一切变量皆是对象,每个对象都是类的实例。int、double、函数、 null 等都是对象,所有对象都继承自 Object 类
  • Dart可基于AOT(Ahead Of Time)编译,即编译成平台的本地代码,运行性能高。
  • Dart也可基于JIT(Just In Time)编译,编译快速,可热加载,使开发周期加倍提升(Flutter亚秒级有状态热重载)
  • Dart可以更轻松地创建以60fps运行的流畅动画和转场。Dart在没有锁的情况下进行对象分配和垃圾回收
  • Dart语法结合Java与JavaScript语法特点,几乎没有令人不适的怪异语法,使Java程序员倍感亲切,快速上手
  • Dart 没有关于 public、protected、private 的关键字。通过为变量标识符添加下划线前缀,表明该标识符对其库是私有的
  • Dart支持顶级函数、静态函数、实例函数,也允许在函数中嵌套函数,即局部函数。类似的,dart 也支持顶级变量、静态变量和实例变量
  • Dart 是强类型语言,但由于具备类型推导功能所以类型声明是可选的

🌙 安装

由于目的是学习flutter开发,按照第二个方式开始。此处不再赘言。

🌙 命令行

安装完毕,命令行输入dart:

PS C:\Users\Administrator> dart
A command-line utility for Dart development.

Usage: dart <command|dart-file> [arguments]

Global options:
-v, --verbose               Show additional command output.
    --version               Print the Dart SDK version.
    --enable-analytics      Enable analytics.
    --disable-analytics     Disable analytics.
    --suppress-analytics    Disallow analytics for this `dart *` run without changing the analytics configuration.
-h, --help                  Print this usage information.

Available commands:
  analyze    Analyze Dart code in a directory.
  compile    Compile Dart to various formats.
  create     Create a new Dart project.
  devtools   Open DevTools (optionally connecting to an existing application).
  doc        Generate API documentation for Dart projects.
  fix        Apply automated fixes to Dart source code.
  format     Idiomatically format Dart source code.
  info       Show diagnostic information about the installed tooling.
  pub        Work with packages.
  run        Run a Dart program.
  test       Run tests for a project.

Run "dart help <command>" for more information about a command.
See https://dart.dev/tools/dart-tool for detailed documentation.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

🌙 基础

  • 强静态类型;
  • 语法极其灵活,类似typescript、 python、java 的结合,集百家所长;
  • 每行需要逗号结尾;
  • 面向对象
  • JIT:即时编译,开发期间,更快编译,更快的重载
  • AOT:事前编译,release期间,更快更流畅

🌙 注释

  • 单行注释://
  • 多行注释:‘/*’ and ‘*/’
  • 文档注释: “///”(C# Style) and “/**…..*/”(JavaDoc Style), 推荐使用///

比如hello.dart代码如下:

// 单行注释
// This is a single line comment.

/*
多行注释
These are 
multiple line 
of comments 
* */

void main() {
  String msg = "hello dart !";
  // 文档注释
  /// This is
  /// a documentation
  /// comment 
  /// print hello dart
  print(msg);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

使用dart hello.dart执行,可以看到控制台输出hello dart

也可以在这里线上测试dart (opens new window)

🌙 数据类型

内置类型 (opens new window)

1.Numbers (int, double) 数字
    int: -253 to 253 - 1
    double: 64-bit (double-precision)
    
2.Strings (String) 字符串,类似python,可以使用单引号、双引号、三引号

3.Booleans (bool) 布尔
    false、true

4.Records ((value1, value2)) 记录 (3.0以上版本)
    var record = ('first', a: 2, b: true, 'last');

5.Lists (List, also known as arrays) 列表
    var list = [1, 2, 3];

6.Sets (Set) 集合(无序、唯一)
    var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
 
7.Maps (Map) 映射
    var gifts = {
      // Key:    Value
      'first': 'partridge',
      'second': 'turtledoves',
      'fifth': 'golden rings'
    };

8.Runes (Runes; often replaced by the characters API) 符文
    In Dart, runes expose the Unicode code points of a string.

9.Symbols (Symbol) 符号, 使用#开头
    A Symbol object represents an operator or identifier declared in a Dart program.
    例如:
    #radix
    #bar

10.null (Null) null值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void main() {
  #Symbol;
  #test;

  var n = null;
  print('n=null: $n'); // n=null: null
  print(#test); // Symbol("test")
  
  // numbers
  const PI = 3.14;
  double pi = 3.14;
  int age = 16;

  // string
  var s1 = 'dart';
  var s2 = 'hello ' + s1;
  var s3 = 'hello $s1';
  String s4 = "hello world";
  String s5 = '''
    hello world,
    $s1
    $s2
  ''';

  // booleans
  var isAdult = age > 18;
  bool good = true;
  const ok = false;
  bool flag;
  if (ok) {
    flag = true;
  }

  late bool doIt;
  if (good) {
    doIt = true;
  }

  // record
  var r1 = ('first', a: 2, b: false, c: 'c', 'last');
  // get element
  var first = r1.$1;
  var last = r1.$2;
  var a = r1.a;

  // 定义record
  ({int a, bool b}) r2;
  // 赋值record
  r2 = (a: 1, b: false);
  print(r2.runtimeType); // ({int a, bool b})
  
  // 直接初始化record
  (int x, int y, int z) point = (1, 2, 3);
  (int r, int g, int b) color = (1, 2, 3);
  // 比较
  print(point == color); // Prints 'true'.

  // 全部取出来
  var (x, y, z) = point;

  // lists
  var list = [1, 2, 3];
  print('list: ${list}'); // list: [1, 2, 3]

  print('list.length: ${list.length}'); // list.length: 3

  print('list.first: ${list.first}'); //  list.first: 1
  print('list.last: ${list.last}'); // list.last: 3

  print('list[1]: ${list[1]}'); // list[1]: 2

  list.add(4);
  list.removeLast();
  list.addAll([4, 5, 6]);
  print('list: ${list}'); // list: [1, 2, 3, 4, 5, 6]
  list.removeAt(0);
  // list.removeAt(-1); // error
  print('list.contains(1): ${list.contains(1)}'); // list.contains(1): false
  print('list.reversed;: ${list.reversed}'); // list.reversed;: (6, 5, 4, 3, 2)
  // print('list[-1];: ${list[-1]}'); error
  var list2 = [0, ...list, 6];
  print('list2;: ${list2}'); //  [0, 2, 3, 4, 5, 6, 6]

  // 类似python,支持if,if-case操作
  var nav = ['Home', 'Furniture', 'Plants', if (ok) 'Outlet'];

  // sets
  var fruits = {'apple', 'orange', 'banana'};
  print('fruits: ${fruits}');
  fruits.add('apple'); // 不会加进去
  print('fruits: ${fruits}'); // fruits: {'apple', 'orange', 'banana'}

  var nums = <int>{};
  print('nums.length: ${nums.length}'); //nums.length: 0
  nums.add(0);
  nums.addAll([1, 2, 3, 4, 3, 3, 2, 1]);
  print('nums: ${nums}'); // nums: {0, 1, 2, 3, 4}

  // maps 类似ts中的object
  var gifts1 = {
    // Key:    Value
    'first': 'partridge',
    'second': 'turtledoves',
    'fifth': 'golden rings'
  };

  var nobleGases1 = {
    2: 'helium',
    10: 'neon',
    18: 'argon',
  };

  var gifts2 = Map<String, String>();
  gifts2['first'] = 'partridge';
  gifts2['second'] = 'turtledoves';
  gifts2['fifth'] = 'golden rings';

  var nobleGases2 = Map<int, String>();
  nobleGases2[2] = 'helium';
  nobleGases2[10] = 'neon';
  nobleGases2[18] = 'argon';
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123

🌙 变量

变量申明:

  • type 申明,类似var,可初始化或不初始化
  • var 一旦初始化,类型不可变(自动推断),可以再次赋相同类型的值

var和type类型申明不可以同时使用。


String name1 = 'bob';
// var String he = 'je'; // 报错

var name2 = 'bob';
1
2
3
4
5
  • const 编译期常量,不可修改, 编译为常量(自身和成员都不可变)
  • final 运行时常量,不可修改, 只能赋值一次, 定义变量(自身不可变,成员可变)

final有两种使用场景

  • 类中的变量声明,必须在声明时赋值。
  • 类中的成员变量声明,可以在声明时候赋值,也可以通过构造函数赋值

Instance variables can be final but not const.

class Demo {
  static final age = 10; // static使用const(推荐,不建议使用final)
  final String name; // Instance variables不能使用const
  final DateTime start = DateTime.now();
}
1
2
3
4
5
  • dynamic 随意修改
  • late 先申明,后初始化,可修改
void main() {
  var name1 = 'dart';
  name1 = 1; // A value of type 'int' can't be assigned to a variable of type 'String'.

  // 使用type类型申明
  String name2 = 'dart';
  name2 = 2; // A value of type 'int' can't be assigned to a variable of type 'String'.
  name2 =
  22 as String; // 断言,但编译失败 Unhandled exception:type 'int' is not a subtype of type 'String' in type cast

  Object name3 = 'dart';
  name3 = 3; // ok

  dynamic name4 = 'dart';
  name4 = 4; // ok

  int a; //先申明
  a = 10; // 在初始化(可以不初始化)

  // 声明一个在其声明后初始化的非空变量
  late String aa;
  // 必须初始化, 否则运行时报错
  aa = '123';

  // 直接申明并初始化
  late int hh = 18;
  hh = 19; // 再次修改
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  • 定义单个:type variable_name;

  • 定义多个:type variable1_name, variable2_name, variable3_name, ....variableN_name;

  • 规则:

Conditions to write variable name or identifiers are as follows:
1.Variable name or identifiers can’t be the keyword.
2.Variable name or identifiers can contain alphabets and numbers.
3.Variable name or identifiers can’t contain spaces and special characters, except the underscore(_)
and the dollar($) sign.
4.Variable name or identifiers can’t begin with number.

变量名或标识符的命名规则如下:
1.变量名或标识符不能是关键字。
2.变量名或标识符可以包含字母和数字。
3.变量名或标识符不能包含空格和特殊字符,除了下划线(_)和美元符号($)。
4.变量名或标识符不能以数字开头。
1
2
3
4
5
6
7
8
9
10
11
12
void main() {
  // Declaring and initialising a variable
  int age = 18;

  // Declaring another variable
  double pi = 3.14; // must declare double a value or it will throw error
  bool male = false; // must declare boolean a value or it will throw error

  // Declaring multiple variable
  String like1 = "dart",
      like2 = "flutter";

  // Printing values of all the variables
  print(age); // Print 18
  print(pi); // Print 3.14
  print(male); // Print false
  print(like1); // Print dart
  print(like2); // Print flutter
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

🌙 关键词 (opens new window)

Keywords are the set of reserved words which can’t be used as a variable name or identifier because they are standard identifiers whose function are predefined in Dart. 

关键字是一组保留字,不能用作变量名或标识符,因为它们是在Dart中预定义功能的标准标识符。
1
2
3

🌙 操作符

基本类似typescript语言。参考文档:https://dart.dev/language/operators

支持三元条件(a ? 1 : 2)、类型断言(as、is、is!)、值断言(a ?? 'null')、可选参数(?)、扩展(...)、级联操作(..或?..)

void main() {
  // 类型断言 Type test operators
  // as
  (employee as Person).firstName = 'Bob';
  // is、is!
  if (employee is Person) {
    // Type check
    employee.firstName = 'Bob';
  }


  // 值断言 Assign value to a
  a = value;
  // Assign value to b if b is null; otherwise, b stays the same
  b ??= value;

  var visibility = isPublic ? 'public' : 'private';

  String playerName(String? name) => name ?? 'Guest';


  // 不使用级联操作(..或?..)
  var paint1 = Paint();
  paint1.color = Colors.black;
  paint1.strokeCap = StrokeCap.round;
  paint1.strokeWidth = 5.0;

  // 使用级联操作(..)
  var paint2 = Paint()
    ?..color = Colors.black // 如果可以为null的话
    ..strokeCap = StrokeCap.round
    ..strokeWidth = 5.0;

// If SomeClass is non-null, set a variable equal to its abc value.
  var a = SomeClass?.abc;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

🌙 循环和分支

for-while 循环:

  • for loops
  • while and do while loops
  • break and continue
void main() {
  var message = StringBuffer('Dart is fun');
  for (var i = 0; i < 5; i++) {
    message.write('!');
  }

  var callbacks = [];
  for (var i = 0; i < 2; i++) {
    if (i == 1) {
      continue;
    }
    callbacks.add(() => print(i));
  }

  // Iterable for-in
  for (final c in callbacks) {
    c();
  }

  // Iterable forEach
  var collection = [1, 2, 3];
  collection.forEach(print); // 1 2 3


  // while和do-while
  while (!isDone()) {
    if (shutDownRequested()) break;
    doSomething();
  }

  do {
    printLine();
  } while (!atEndOfPage());


  // Iterable
  candidates
      .where((c) => c.yearsExperience >= 5)
      .forEach((c) => c.interview());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

if-switch分支: 和typescript类似,参考文档:https://dart.dev/language/branches

  • if-case 可以做类型守卫
import 'package:flutter/cupertino.dart';
import 'dart:math';

Point? main() {
  const pair = [1, 2];
  if (pair case [int x, int y]) return Point(x, y);

  return null;
}


class Point {
  final int x;
  final int y;

  // Sets the x and y instance variables
  // before the constructor body runs.
  Point(this.x, this.y);

  double distanceTo(Point other) {
    var dx = x - other.x;
    var dy = y - other.y;
    return sqrt(dx * dx + dy * dy);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  • switch-case
void main() {
  var command = 'OPEN';
  switch (command) {
    case 'CLOSED':
      executeClosed();
    case 'PENDING':
      executePending();
    case 'APPROVED':
      executeApproved();
    case 'DENIED':
      executeDenied();
    case 'OPEN':
      executeOpen();
    default:
      executeUnknown();
  }

  // 使用label
  switch (command) {
    case 'OPEN':
      executeOpen();
      continue newCase; // Continues executing at the newCase label.

    case 'DENIED': // Empty case falls through.
    case 'CLOSED':
      executeClosed(); // Runs for both DENIED and CLOSED,

    newCase:
    case 'PENDING':
      executeNowClosed(); // Runs for both OPEN and PENDING.
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

🌙 函数

类似于java语法,函数返回值(类型)、函数参数(必选、可选、命名参数)、词法作用域、闭包等等, 参考文档 (opens new window)

不使用function关键字

请看代码:

import 'dart:mirrors';

void main() {
  print('test function');
  var (a, b) = (1, 2);
  print((1, 2).runtimeType); // (int, int)

  print('add1(a, b): ${add1(a, b)}'); // add1(a, b): 3
  print('add2(): ${add2()}'); // add2(): 0
  print('add3(): ${add3()}'); // add3(): 0
  print('add3(x: 1, y: 2): ${add3(x: 1, y: 2)}'); // add3(x: 1, y: 2): 3

  print('addDouble(x: 1): ${addDouble(x: 1)}'); // addDouble(x: 1): 3
  print(
      'addDouble(x: 1, y:4): ${addDouble(x: 1, y: 4)}'); // addDouble(x: 1, y:4): 5

  print('addThree(1,5): ${addThree(1, 5)}'); // addThree(1,5): 6

  print('addThree(1,5,2): ${addThree(1, 5, 2)}'); // addThree(1,5,2): 8

  var double = multi(2);
  print('double(3): ${double(3)}'); // double(3): 6
}

// 必须都要传
int add1(int x, int y) {
  return x + y;
}

// 都可选,无默认值
int add2({int? x, int? y}) {
  return (x ?? 0) + (y ?? 0);
}

// 命名参数:都可选,有默认值, 传值的话必须指明参数名
int add3({int x = 0, int y = 0}) {
  return x + y;
}

// 命名参数:x必须要传,y可选
int addDouble({required int x, int y = 2}) {
  return x + y;
}

// x, y 必须要传, z可选
int addThree(int x, int y, [int z = 0]) {
  return x + y + z;
}

int double(int x) {
  return () => x * 2;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

🌙 对象

类似于java语法, 但不支持构造函数的重载,采用了命名构造函数为一个类实现多个构造函数。

使用dart创建一个class Point,使得Point支持Point(2, 2)Point.fromJson({'x': 1, 'y': 2}) 以及 new Point.fromJson({'x': 1, 'y': 2})new Point(2, 2)四种方式调用:

直接看代码:

import 'dart:math';

class Point {
  // int x;
  // int y;
  final int x;
  final int y;

  // Point(int x, int y): this.x = x, this.y=y;
  //  简写如下
  Point(this.x, this.y);

  // 定义常量构造函数, 必须使用final修饰x,y
  const Point.Const(this.x, this.y);

  Point.fromJson(Map<String, dynamic> json)
      : x = json['x'],
        y = json['y'];

  // 支持覆盖
  
  String toString() {
    return 'Point(x: $x, y: $y)';
  }

  double distanceTo(Point other) {
    var dx = x - other.x;
    var dy = y - other.y;
    return sqrt(dx * dx + dy * dy);
  }
}

void main() {
  // 使用 Point(2, 2) 方式调用
  Point p1 = Point(2, 2);
  print(p1); // 输出:Point(x: 2, y: 2)

  // 使用 Point.fromJson({'x': 1, 'y': 2}) 方式调用
  Point p2 = Point.fromJson({'x': 1, 'y': 2});
  print(p2); // 输出:Point(x: 1, y: 2)

  // 使用 new Point(2, 2) 方式调用
  Point p3 = new Point(2, 2);
  print(p3); // 输出:Point(x: 2, y: 2)

  // 使用 new Point.fromJson({'x': 1, 'y': 2}) 方式调用
  Point p4 = new Point.fromJson({'x': 1, 'y': 2});
  print(p4); // 输出:Point(x: 1, y: 2)

  // 使用 const Point.Const(2, 2) 方式调用
  Point p5 = const Point.Const(2, 2);
  print(p5); // 输出:Point(x: 2, y: 2)
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

在Dart中,实例化一个类时使用new关键字和不使用new关键字是没有区别的。 自Dart 2.1版本开始,官方已经移除了new关键字的强制使用,因此在Dart中可以选择是否使用new关键字来实例化一个类。

  • extends继承:
class Person {
  String? firstName;

  Person.fromJson(Map data) {
    print('in Person');
  }
}

class Employee extends Person {
  // Person does not have a default constructor;
  // you must call super.fromJson().
  Employee.fromJson(super.data) : super.fromJson() {
    print('in Employee');
  }
}

void main() {
  var employee = Employee.fromJson({});
  print(employee);
  // Prints:
  // in Person
  // in Employee
  // Instance of 'Employee'
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  • getters和setters
class Rectangle {
  double left, top, width, height;

  Rectangle(this.left, this.top, this.width, this.height);

  // Define two calculated properties: right and bottom.
  double get right => left + width;
  set right(double value) => left = value - width;
  double get bottom => top + height;
  set bottom(double value) => top = value - height;
}

void main() {
  var rect = Rectangle(3, 4, 20, 15);
  assert(rect.left == 3);
  rect.right = 12;
  assert(rect.left == -8);
} 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  • abstract methods支持抽象类和抽象方法, 使用extends具体实现
 abstract class Doer {
  // Define instance variables and methods...

  void doSomething(); // Define an abstract method.
}

class EffectiveDoer extends Doer {
  void doSomething() {
    // Provide an implementation, so the method is not abstract here...
  }
}
1
2
3
4
5
6
7
8
9
10
11
  • enum枚举
enum Color { red, green, blue }

void main() {
  final favoriteColor = Color.blue;
  if (favoriteColor == Color.blue) {
    print('Your favorite color is blue!');
  }

  assert(Color.red.index == 0);
  assert(Color.green.index == 1);
  assert(Color.blue.index == 2);
}
1
2
3
4
5
6
7
8
9
10
11
12

🌙 错误处理

使用throw抛出错误, 使用try-on-catch-finally 捕获错误:

void main() {
  try {
    // throw FormatException('Expected at least 1 section');
    // throw 'test error!';
    // throw 111;
  } on FormatException {  // 具体的Exception
    try {
      print('FormatException');
      throw 'test error!';
    } catch (e) { // 捕获Exception
      print('Exception: $e');
    }
  } on String { // String Exception
    print('String Exception');
  } on Object { // Int Exception
    print('Object Exception');
  } catch (e) { // 捕获Exception
    print('Exception catch: $e');
  } finally {
    print('finally');
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  • on可以捕获到某一类的异常,但是获取不到异常对象;
  • catch可以捕获到异常对象。这个两个关键字可以组合使用。
  • rethrow可以重新抛出捕获的异常。