Dart 基本用法

110 阅读1分钟

A basic Dart program

void printInteger(int aNumber) {
    print('The number is $aNumber');
 }
 
 void main() {
     var nummber = 43;
     printInteger(number);
 }
 
 // 注释
 void : 返回参数, void表示无返回
 int : 数据类型
 $aNumber : 格式化输出变量 或者 ${aNumber}
 main : 主函数, 程序入口
 var : 声明变量

Important Concepts

  1. 一切皆对象, 每个对象都是一个类的实例, 所有的对象都继承自Object
  2. 尽管Dart是强类型, 但是声明的时候类型是可以省略的, Dart可以推断类型
  3. Dart从2.12开始有Null Safety, 空类型安全, 声明一个变量有可能为null的时候, 可以在类型后面加上? 比如: int?, 如果很确定不为空则可以使用!
  4. 如果声明的类型可以是任何对象则可以使用Object?
  5. Dart支持泛型List<int>
  6. Dart没有修饰符, 如果想表示私有变量则可以使用_开头
  7. Dart支持三目运算符condition ? expr1 : expr2也支持if-else

内置类型

  • Numbers(int, double)
  • Strings(String)
  • Booleans(bool)
  • Lists(List, also know as arrays)
  • Sets(Set)
  • Map(Map)
  • Runes(Runes)
  • Symbols(Symbol)
  • null(Null)

还有一些比较特殊的类型

  • Object: 是所有对象的父类,包括Null
  • Enum: 所有enums的父类
  • Future&Stream: 在异步中
  • Iterable: 遍历
  • Never: 表示永远无法执行的语句?
  • dynamic: 禁止静态检查?
  • void: return type

List中有一个比较好用的, list展开符号 ...

var list = [1, 2, 3];

var list_a = [4, ...list]; // 得到 [4, 1, 2, 3]

如果是非null的时候才展开, 就可以使用...?
var list_b = [5, ...?list];

collection if

var nav = [1, 2, 3, if(true) 4];

collection for

var list_a = [1, 2, 3];

var list_b = [4, for (var i in list_a) i];

Sets跟list比起来就是一个无序集合, 没有下标

var names = <String>{}; // 字符集合
names.add('a');
names.addAll(another sets);
// list支持的操作 ... ...? if for 什么的, set都支持

var names = {}; // 这是一个map不是一个set

Maps 字典

// 类型可以省略
var gifts = Map<String, String>{
    "key1": "value1",
    "key2": "value2"
};

获取value: gifts["key1"]

甚至可以用数字做key
var gift = {
    1: "value1",
    2: "value2"
};

获取value: gift[1] // 注意, 这里不需要引号

如果获取一个不存在的, 则会返回null

list 的那些操作 ... ...? if for 也都支持

Runes and grapheme clusters

import 'package:characters/characters.dart';
...
var hi = 'Hi 🇩🇰';
print(hi);
print('The end of the string: ${hi.substring(hi.length - 1)}');
print('The last character: ${hi.characters.last}\n');

Fuctions

Dart是一个面向对象的语言, 即使是functions也有一个类型Function

bool isNoble(int aNumber) {
    return true
}

可以简写为
bool isNoble(int aNumber) => false  // 也就是所谓的箭头语法

命名参数

// 必须是用大括号
void enableFlags({bool? bold, bool? hidden}) {...}

enableFlags(bold: true, hidden: false);

如果参数不可为空, 那么就设置默认值
void enableFlags({bool? bold = false})

或者
void enableFalgs(bool? bold, [bool? hidden = false])

Lexical scope

Function makeAdder(int addBy) {
  return (int i) => addBy + i; // 返回的是 接收int, 然后返回 addBy + 接收过来的参数和
}

void main() {
  // Create a function that adds 2.
  var add2 = makeAdder(2); // add2 是一个函数

  // Create a function that adds 4.
  var add4 = makeAdder(4);

  assert(add2(3) == 5); // 3 + 2
  assert(add4(3) == 7);
}

Type test operators

as 类型转换   (employee as Person).firstName = 'Bob';
is 类型判断   if (employee is Person) employee.firstName = 'Bob';
is! True if the object doesn’t have the specified type
b ??= value  如果b为空, 就赋值

Conditional expressions 条件表达式


三目运算符  isTrue ? 'a' : 'b';
expr1 ?? expr2 如果 expr1 non-null 则返回expr1的返回值, 否则 返回expr2的值

Cascade notation 可以操作同一个对象

var paint = Paint()
  ..color = Colors.black
  ..strokeCap = StrokeCap.round
  ..strokeWidth = 5.0;
  
// 这里返回的对象, 不一定存在 所以使用?..
querySelector('#confirm') // Get an object.
  ?..text = 'Confirm' // Use its members.
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'))
  ..scrollIntoView();

条件过滤

var list = [1, 23, 4, 5];

 list.where((element) => element > 3).forEach((element) {
   print(element);
 });

Throw 异常 Catch

throw FormatException('Expected at least 1 section');

或者
throw 'out of memory';

try {

} on OutOfLlamasException {

} on Exception catch (e){

} catch (e) {

} finally {

}

Getting an object’s type

var a = [1, 23, 4];
print(a.runtimeType); // List<int>

Instance variables

Class

class Point {
  double? x; // default null
  double? y;
  double z = 0;

  // 构造函数
  Point(this.x, this.y);
}

类的继承

class Person {
  String? firstName;

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

class Employee extends Person {
  // 这里必须调用父类的fromJson
  Employee.fromJson(super.data) : super.fromJson() {
    print("in employee");
  }
}

如果父类有构造方法

class Vector2d {
  final double x;
  final double y;

  Vector2d(this.x, this.y);
}

class Vector3d extends Vector2d {
  final double z;

  // 这里需要调用父类, 或者可以这么写
  // Vecoor3d(final double x, final double y, this.z) : super(x, y);
  Vector3d(super.x, super.y, this.z);
}

如果父类构造方法有 require关键字

class Vector2d {
  // ...

  Vector2d.named({required this.x, required this.y});
}

class Vector3d extends Vector2d {
  // ...

  // 把y值传递给父类的构造方法
  // Vector3d.yzPlane({required double y, required this.z})
  //       : super.named(x: 0, y: y);
  Vector3d.yzPlane({required super.y, required this.z}) : super.named(x: 0);
}

代理给主构造方法

class Point {
  double x, y;

  // The main constructor for this class.
  Point(this.x, this.y);

  // Delegates to the main constructor.
  Point.alongXAxis(double x) : this(x, 0);
}

Operators 操作符重写

class Vector {
  final int x, y;

  Vector(this.x, this.y);

  Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
  Vector operator -(Vector v) => Vector(x - v.x, y - v.y);

  // Operator == and hashCode not shown.
  // ···
}

void main() {
  final v = Vector(2, 3);
  final w = Vector(2, 2);

  assert(v + w == Vector(4, 5));
  assert(v - w == Vector(0, 1));
}

Getters and 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);
}

Abstract classes 抽象类 和 接口

abstract class AbstractContainer {
  // Define constructors, fields, methods...

  void updateChildren(); // Abstract method.
}
//person 类有一个 greet() 方法
class Person {
  // In the interface, but visible only in this library.
  final String _name;

  // Not in the interface, since this is a constructor.
  Person(this._name);

  // In the interface.
  String greet(String who) => 'Hello, $who. I am $_name.';
}

// implements 实现一个接口, 可以实现多个 , 分开
class Impostor implements Person {
  String get _name => '';

  String greet(String who) => 'Hi $who. Do you know who I am?';
}

String greetBob(Person person) => person.greet('Bob');

void main() {
  print(greetBob(Person('Kathy')));
  print(greetBob(Impostor()));
}

Extending a class 继承一个类

class Television {
  void turnOn() {
    _illuminateDisplay();
    _activateIrSensor();
  }
  // ···
}

class SmartTelevision extends Television {
  void turnOn() {
    super.turnOn(); // xi
    _bootNetworkInterface();
    _initializeMemory();
    _upgradeApps();
  }
  // ···
}

重写属性的话, 需要使用 @override 关键字 

@override
set contrast(num value) {...}

重写方法也一样
@override
void noSuchMethod(Invocation invocation) {
   print('You tried to use a non-existent member: '
        '${invocation.memberName}');
}

枚举

enum Color { red, green, blue }

assert(Color.red.index == 0);
print(Color.red.name);

Adding features to a class: mixins

在多重继承中复用某个类中代码的方法模式, 使用with关键字, 后面跟上Mixin类的名字来使用 想要实现Mixin的话, 这个类必须继承自Object, 并且没有构造函数 可以使用on来指定哪些类可以使用该Mixin类

mixin a {}
mixin b {}

class c extends d with a {}
class e extends d with a, b {}

mixin f on x {} // 指定只有x这个类可以  如果有别的类想要mixin f 必须继承自x

Asynchrony support 异步

// 要使用await 必须 函数后面必须使用async
Future<void> checkVersion() async {
  var version = await lookUpVersion();
  // Do something with version
}

await for 是等待一个循环遍历返回

Generators

  • Synchronous generator: Returns an Iterable object.
  • Asynchronous generator: Returns a Stream object.

Isolates

Dart支持并发: async-await, isolates, 比如: Future和Stream

void main() async {
  // Read some data.
  final fileData = await _readFileAsync();
  final jsonData = jsonDecode(fileData);

  // Use that data.
  print('Number of JSON keys: ${jsonData.length}');
}

Future<String> _readFileAsync() async {
  final file = File(filename);
  final contents = await file.readAsString();
  return contents.trim();

Isolate的使用

void main() async {
  // Read some data.
  final jsonData = await _parseInBackground();

  // Use that data
  print('Number of JSON keys: ${jsonData.length}');
}

// Spawns an isolate and waits for the first message
// 一旦isolate 开始执行, main isolate就会等待结果, 因为ReceivePort 实现了Stream
Future<Map<String, dynamic>> _parseInBackground() async {
  final p = ReceivePort(); // 允许isolate 发消息给 main isolate
  // spawn 创建并开始一个isolate 在后台工作
  // 第一个参数是要执行的任务, 第二个是用来通信的port
  await Isolate.spawn(_readAndParseJson, p.sendPort); 
  return await p.first as Map<String, dynamic>;
}

// 而且这里需要注意, 如果是多次发送数据, 则是请求 --> 然后发送这种模式下进行的
Future<void> _readAndParseJson(SendPort p) async {
  final fileData = await File(filename).readAsString();
  final jsonData = jsonDecode(fileData);
  // 解析完成之后, 退出isolate,
  //  SendPort.send 发送的是数据的copy, 如果使用Isolate.exit()的话不会copy, 但是会跨Isolate
  Isolate.exit(p, jsonData);
}

Typedefs 别名

typedef IntList = List<int>;
IntList il = [1, 2, 3];

typedef ListMapper<X> = Map<X, List<X>>;
Map<String, List<String>> m1 = {}; // Verbose.
ListMapper<String> m2 = {}; // Same thing but shorter and clearer.

Metadata 为code提供额外的信息, 比如 过期 deprecated

class Television {
  /// Use [turnOn] to turn the power on instead.
  @Deprecated('Use turnOn instead')
  void activate() {
    turnOn();
  }

  /// Turns the TV's power on.
  void turnOn() {...}
  // ···
}

也可以定义自己的metadata

library todo;

class Todo {
  final String who;
  final String what;

  const Todo(this.who, this.what);
}

使用的时候

import 'todo.dart';

@Todo('seth', 'make this do something')
void doSomething() {
  print('do something');
}