Dart

Starter

笔记:

基础数据类型

明确的声明

String str1 = 'Hello';
String str2 = "It's me";
String str3 = """Hello
World
""";
print("$str1,$str2,$str3");

var name = "Tom";
var age = 10;
var height = 120;
var msg1 = "my name is ${name}, age is ${age}, height is ${height}";
var msg2 = "name is ${name}, type is ${name.runtimeType}";
print(msg1);
print(msg2);
  • 数字类型: int,double 可表示的范围并不是固定的,取决于运行Dart的平台
  • 字符类型: String 可使用${expression}实现字符串和其他变量或表达式拼接,${变量},则{}可省略,即 $exp
  • 布尔类型: bool 取值为true/false,没有非0/非空即true的规则,即不能使用if(非布尔类型的变量),assert(非布尔类型的变量)之类的代码

      var flag = true;
      if(flag){               // right !!
        print(flag);
      }
    
      var message="Hello";
      if(message){            // wrong !!
        print(message);
      }
    
  • 集合类型: ListSetMap

      var nameList = ["Tom", "Lucy", "Susan"];
      nameList.add("Jack");
      nameList.remove("Lucy");
      print(
      "$nameList, len: ${nameList.length}, contain Tom: ${nameList.contains('Tom')}");
    
      var movieSet = {"a", "b", "c"};
      print(
      "$movieSet,len: ${movieSet.length}, contain b: ${movieSet.contains('b')}");
    
      var infoMap = {"name": "Tom", "age": 18};
      print(
      "$infoMap,entries: ${infoMap.entries},keys: ${infoMap.keys},infoMap['age']=${infoMap['age']}");
    
      // 去重:
      var charList = ['a', 'b', 'b', 'c', 'a'];
      var noDupCharList = Set<String>.from(charList).toList();
      print(
      "charList:$charList, set: ${Set.from(charList)}, dup removed list: ${Set.from(charList).toList()}, noDupCharList:$noDupCharList");
    
  • 类:class (dart 中是没有关键字来定义接口的,默认所有的class都是隐式接口)

      class Person {
        String name;
        Person(String name) {
          this.name = name;
        }
      }
    
      class SingletonPerson {
        final String name;
        const SingletonPerson(this.name);
      }
    

类型推导 (var / final / const): 虽然没有明确指定变量类型,但变量还是有自己明确的类型的

  • 声明变量 var
      var score = 20; // 如果后面再设置 ` score="abc"; ` 会报错
      score = 30;
    
  • 声明常量 final / const

      // final 运行期间确定一个值,即可以通过计算/函数获取一个值
      // const 编译期间确定一个值,即须直接赋予一个值
      final date = DateTime.now();  // 正确
      const date = DateTime.now();  // 错误
    
      final price = 1.88; // 如果后面再设置 ` price=2.00; ` 会报错
      const address = "SuZhou"; // 如果后面再设置 ` address="ShangHai"; ` 会报错
    
      final p1 = Person("Tom"); // = new Person("Tom");
      final p2 = Person("Tom"); // = new Person("Tom");
      print(identical(p1, p2)); // => false  p1和p2是不同的对象
    
      const s1 = SingletonPerson("Tom"); // = const SingletonPerson("Tom");
      const s2 = SingletonPerson("Tom"); // = const SingletonPerson("Tom");
      const s3 = SingletonPerson("Lucy"); // = const SingletonPerson("Lucy");
      print(identical(s1, s2)); // => true  s1和s2是同一个对象
      print(identical(s1, s3)); // => false s1和s3不是同一个对象
    

object vs dynamic vs var

// 注: 关键字(var/const/final/...) vs 类型 (String,Object,dynamic,...)
Object a = "Hello";
// print("$a,${a.substring(3)}"); // 无法调用 String的substring 方法,编译时即报错了

dynamic b = "Hello";
print("$b, ${b.substring(3)}"); // 可调用 String的substring 方法,编译时不报错,但存在安全隐患,可能调用了一个不存在的方法,运行时报错
//print("$b, ${b.say()}"); // 编译时不报错,运行时报错

var c = "Hello";
print("$c,${c.substring(3)}"); // 可调用 String的substring 方法
// print("$c,${c.say()}");  // 编译时就报错了

函数

函数的参数

函数的 必选参数 & 可选参数(位置可选参数,命名可选参数) 注:dart中无函数重载,即不可以声明两个同名函数

  1. 必选参数(必传,且无法设置默认参数)

     void sayHello1(String name) {
       print(name);
     }
    
     sayHello1("Tom");
    
  2. 可选参数

    • 位置可选参数 []

        void sayHello2(String name, [int age, double height]) {
          print("$name,$age,$height");
        }
      
        sayHello2("Lucy"); // Lucy,null,null
        sayHello2("Lucy", 18); // Lucy,18,null
        sayHello2("Lucy", 18, 165.5); // Lucy,18,165.5
      
    • 命名可选参数 {}

        void sayHello3(String name, {int age, double height}) {
          print("$name,$age,$height");
        }
      
        sayHello3("Lucy"); // Lucy,null,null
        sayHello3("Lucy", height: 165.5); // Lucy,null,165.5
        sayHello3("Lucy", age: 18, height: 165.5); // Lucy,18,165.5
      
    • 可设置默认参数

        void sayHello4(String name, {int age = 16, double height = 120}) {
          print("$name,$age,$height");
        }
      
        sayHello4("Lucy", height: 165.5); // Lucy,16,165.5
      

函数作为参数/返回值

函数是一等公民,即函数可以赋给一个变量,作为参数/返回值来回传递(在一般的面向对象的语言中,如Java,一般class/object是第一公民,可作为参数/返回值来回传递)

  1. 函数作为另一个函数的参数

    • 直接使用Function -- 对函数无限制

        void test1(Function foo) {
          foo(); // foo(20,30),...对函数无限制
        }
      
        void bar([String name = "Hi"]) {
          print(name);
        }
      
        // 传入函数 - 直接使用某个定义的函数
        test1(bar);
      
        // 传入函数 - 使用匿名函数
        test1(() {
          print("test 匿名函数");
        });
      
        // 传入函数 - 使用箭头函数(这种函数体必须只有一行代码)
        test1(() => print("test 箭头函数"));
      
    • 传入声明式函数,注意:对函数有限制,如参数,返回值等
        // void test2(int foo(int a, int b)) {    // 太长,阅读性差
        //   var result = foo(20, 30);
        //   print(result);
        // }
        typedef Calcuate = int Function(int a, int b); // 函数签名
        void test2(Calcuate cal) {
          var result = cal(20, 30);
          print(result);
        }
      
  2. 函数作为返回值

     Calcuate getTestFunc() {
       return (a, b) {
         return a + b;
       };
     }
    
     Function getTestFunc2() {
       return (a, b) {
         return a + b;
       };
     }
    
     // 函数作为返回值
     var ts = getTestFunc();
     print(ts(20, 30));
    
     var ts2 = getTestFunc2();
     print(ts2(20, 50));
    

操作符

??=

void test1([String name]) {
  name ??= "Lucy"; // name = (name!=null? name:"Lucy") 或等同于 [String name="Lucy"]
  print(name);
}

test1();
test1("Tom");

??

void test2([String name]) {
  var temp = name ?? "Lucy"; // temp = ( name!=null? name:"Lucy")
  print(temp);
}

test2();
test2("Tom");

::

级联运算符

class Person {
  String name;

  void run() {
    print("$name running");
  }

  void eat() {
    print("$name eating");
  }
}

main(List<String> args) {
    var p1 = Person();
    p1.name = "Tom";
    p1.run();
    p1.eat();

    print("---------------------");

    var p2 = Person()
    ..name = "Lucy"
    ..run()
    ..eat();
}

构造,初始化

  1. 构造方法

    • 没有明确指定构造方法时,将默认拥有一个无参的构造方法
    • 有了自己的构造方法时,默认的构造方法将会失效
    • Dart本身不支持函数的重载 => 创建相同名称的构造方法 => Solution: 命名构造方法

      class Person {
      String name;
      int age;
      double height;
      
      // 1. 构造函数语法糖
      // Person(String name, int age) {
      //   this.name = name;
      //   this.age = age;
      // }
      // => 等同于:
      Person(this.name, this.age);
      
      // 2. 命名构造方法(名称可自定义)
      Person.withArguments(this.name, this.age, this.height);
      Person.fromMap(Map<String, dynamic> map) {
        this.name = map['name'];
        this.age = map['age'];
        this.height = map['height'];
      }
      
      // 3. 重定向构造函数:在一个构造方法中去调用另外一个构造方法,冒号后面使用this调用
      Person.fromName(String name) : this(name, 20);
      Person.fromName2(String name) : this.withArguments(name, 30, 166.8);
      
      // 重写toString方法
      @override
      String toString() {
        return "$name,$age,$height";
      }
      }
      
      main(List<String> args) {
      var p1 = Person("Tom", 18);
      var p2 = Person.withArguments("Lucy", 16, 165);
      // var p3 = Person.fromMap({"height": 170);  // wrong !! Unhandled exception: type 'int' is not a subtype of type 'double'
      var p3 = Person.fromMap(
          {"age": 20, "name": "Susan", "height": 170.5, "sex": "female"});
      print("p1 : $p1 , p2 : $p2 , p3 : $p3 ");
      
      var p4 = Person.fromName("Kelly1");
      var p5 = Person.fromName2("Kelly2");
      print("p4 : $p4 , p5 : $p5");
      }
      
  2. 初始化列表(Initializer list)

     class FinalPerson {
       final String name;
       final int age;
    
       // FinalPerson(this.name) {
       //   this.age = 10; // wrong !! age是final类型,编译不通过
       // }
       // => 正确写法:
       FinalPerson(this.name) : this.age = 10;
       //FinalPerson.withOptional(this.name, {this.age = 10});     // right !!,可使用表达式
       FinalPerson.withOptional(this.name, {int age})
           : this.age = age ?? 10; // 推荐,后面可跟多条语句,`,`分隔
    
       // 重写toString方法
       @override
       String toString() {
         return "$name,$age";
       }
     }
    
     var fp1 = FinalPerson("Tom");
     print("fp1 : $fp1");
    
     var fp2 = FinalPerson.withOptional("Lucy");
     print("fp2 : $fp2");
    
     var fp3 = FinalPerson.withOptional("Susan", age: 20);
     print("fp3 : $fp3");
    
  3. 常量构造

     /*
       注意:
         1. 拥有常量构造方法的类中,所有的成员变量必须是final修饰
         2. 为了可以通过常量构造方法,创建出相同的对象,不再使用 new关键字,而是使用const关键字 
           (如果是将结果赋值给const修饰的标识符时,const可以省略)
     */
     class ConstPerson {
       final String name;
       const ConstPerson(this.name);
     }
    
     // var cp1 = const ConstPerson("Jack");
     // var cp2 = const ConstPerson("Jack");
     // print(identical(cp1, cp2)); // => true
     const cp1 = ConstPerson("Jack"); // 等同于 var cp1 = const ConstPerson("Jack");
     const cp2 = ConstPerson("Jack");
     print(identical(cp1, cp2)); // => true
    
  4. 工厂构造 factory 可以手动返回一个对象(普通的构造函数不能手动返回)

     class FactoryPerson {
       String name;
       String color;
    
       FactoryPerson(this.name, this.color);
    
       // eg 需求: 希望name或者color相同时返回的是同一个对象
       static final Map<String, FactoryPerson> _nameCache = {};
       static final Map<String, FactoryPerson> _colorCache = {};
    
       factory FactoryPerson.withName(String name) {
         if (_nameCache.containsKey(name))
           return _nameCache[name];
         else {
           final p = FactoryPerson(name, "default");
           _nameCache[name] = p;
           return p;
         }
       }
    
       factory FactoryPerson.withColor(String color) {
         if (_colorCache.containsKey(color))
           return _colorCache[color];
         else {
           final p = FactoryPerson("default", color);
           _colorCache[color] = p;
           return p;
         }
       }
     }
    
     var facP1 = FactoryPerson.withName("Mark");
     var facP2 = FactoryPerson.withName("Mark");
     print(identical(facP1, facP2)); // => true
    

getter/setter

Dart中类定义的属性是可以直接被外界访问的,但通过使用setter和getter,可以监控这个类的属性被访问的过程


// _xxx 下划线,当前模块中可使用

class Person {
  String name;

  // setter - `set`
  // set setName(String name) {
  //   this.name = name;
  // }
  set setName(String name) => this.name = name;

  // getter - `get`
  // String get getName {
  //   return this.name;
  // }
  String get getName => this.name;
}

final p = Person();
p.name = "Tom";
print(p.name);

p.setName = "Jack";
print(p.getName);

继承,抽象,接口,混入类

  1. 继承类 extends

     class Animal {
       int age;
       Animal(this.age);
    
       void eating() {
         print("Animal is eating");
       }
     }
    
     class Cat extends Animal {
       String name;
       Cat(this.name, int age) : super(age);
    
       @override
       void eating() {
         print("$name is eating");
       }
     }
    
     var c = Cat("Miao", 2);
     c.eating();
    
  2. 抽象类 abstract

     //(不能实例化,但可通过factory构造函数实例化)
     // 参看 Map抽象类, 有个extenal factory Map();
     // external: 将方法的声明和实现分离,用个注解@patch在其他地方对方法进行实现 => 好处: 针对不同平台,可有不同的实现
     // map_patch.dart
     abstract class Shape {
       // 抽象方法 (无方法体)
       void getArea();
    
       // 普通方法(有方法体)
       void getInfo() {
         print("Shape");
       }
     }
    
     // Dart 只支持单继承,不能多继承,即只能extends一个类
     class Rectangle extends Shape {
       // 必须实现抽象方法
       @override
       void getArea() {
         print("Rectangle getArea!");
       }
     }
    
     var s = Rectangle();
     s.getArea();
    
  3. 接口 implements(Dart中,默认情况下所有类都是接口)

     class Runner {
       void running() {
         print("Running");
       }
     }
    
     // Dart中可以implements多个类,但必须重新实现@override其中的所有方法
     class Wind implements Runner {
       @override
       void running() {
         print("Wind is running");
       }
     }
    
     var w = Wind();
     w.running();
    
  4. 混入类(Mixin定义类,with混入类)

     // 可复用之前类的原有实现方案
     mixin Swimer {
       void swiming() {
         print("Swiming");
       }
     }
    
     mixin Flier {
       void flying() {
         print("Flying");
       }
     }
    
     class SpaceShip with Swimer, Flier {
       @override
       void swiming() {
         print("SpaceShip can swiming!");
       }
     }
    
     var ss = SpaceShip();
     ss.flying();
     ss.swiming();
    

枚举

/*
常见属性:
  index: 用于表示每个枚举常量的索引, 从0开始
  values: 包含每个枚举值的List.
注意:
  不能子类化、混合或实现枚举
  不能显式实例化一个枚举
 */
enum Colors { red, green, blue }

print(Colors.blue);
print(Colors.green.index);
print(Colors.values);

泛型

class Location<T extends num> {
  T x;
  T y;
  Location(this.x, this.y);

  @override
  String toString() {
    return "$x,$y";
  }
}

T getListFirst<T>(List<T> list) {
  return list[0];
}

var names = ["Tom", "Lucy"];
var first = getListFirst(names);
print("$first,${first.runtimeType}");

var ages = [20, 30];
var a = getListFirst(ages);
print("$a,${a.runtimeType}");

print("---------------------------");

var l1 = Location(10, 20);
print(l1);

var l2 = Location<int>(20, 30);
print(l2);

var l3 = Location<double>(20.5, 30.9);
print(l3);

//var l4 = Location<String>("20", "39");    // wrong !!

  1. Dart中任何一个dart文件都是一个库,即使没有用libary声明(库拆分: part/export - 推荐使用export)

     // mathUtils.dart:
     int sum(int num1, int num2) {
       return num1 + num2;
     }
    
     // dateUtils.dart:
     String dateFormat(DateTime date) {
       return "2020-12-12";
     }
    
     // utils.dart:
     library utils;
     export "mathUtils.dart";
     export "dateUtils.dart";
    
     // test_libary.dart:
     import "lib/utils.dart";
     main(List<String> args) {
         print(sum(10, 20));
         print(dateFormat(DateTime.now()));
     }
    
  2. 导入库 import (Dart中任何一个dart文件都是一个库)

    • import '库所在的uri';
    • 库 URI 三种不同的形式:
      • dart标准库 eg: 'dart:io','dart:html','dart:math' ('dart:core'默认已导入)
      • 相对路径 eg: 'lib/student/student.dart'
      • Pub包管理工具管理的库(包括自己的配置以及一些第三方的库) eg: 'package:flutter/material.dart'
    • show/hide 显示/隐藏某个成员 eg: import 'lib/student/student.dart' show Student, Person;
    • as 命名空间,解决命名冲突 eg: import 'lib/student/student.dart' as Stu; Stu.Student s = new Stu.Student();

future

https://www.imooc.com/article/305495

Future<String> testFuture() {
  //  return Future.value("Hello");
 return Future.error("haha 创造个error");// => onError会打印【代码中有onError回调,catchError不会执行】
//  throw "an Error";   // => 代码会直接异常,不会走 catchError方法,因为throw返回的数据类型不是Future<T>类型
}

testFuture().then((value){
    print(value);
  },onError: (e) {    // onError为可选参数,代码异常时会被调用【代码中有onError回调,catchError不会执行】
    print("onError: $e");
  }).catchError((e){
    print("catchError: $e");
  });

// 类似于java中try{}catch(){}finally{}异常捕获的finally
Future.error("haha make a error")
  .then(print)  
  .catchError(print)
  .whenComplete(() => print("Done!!!"));
//执行结果:
//haha make a error
//Done!!!
testAsync() async {
  var result = await Future.delayed(Duration(milliseconds: 2000), ()=>Future.value("hahha"));
  print("time = ${DateTime.now()}");
  print(result);
}

print("time start = ${DateTime.now()}");
testAsync();
print("time end= ${DateTime.now()}");

//执行结果:
//time start = 2019-05-15 19:24:14.187961
//time end= 2019-05-15 19:24:14.200480
//time = 2019-05-15 19:24:16.213213
//hahha
Future.delayed(Duration(milliseconds: 3000), () => "hate")
      .timeout(Duration(milliseconds: 2000))
      .then(print)
      .catchError(print);
//TimeoutException after 0:00:00.002000: Future not completed