flutter(002)dart我来了

定义变量

  • var 、dynamic 、Object 区分
    • var:声明变量,可以赋值任意对象 。Dart中var变量一旦赋值,类型便会确定,则不能再改变其类型.
    • dynamic泛型:声明变量,可以赋值任意对象。声明的变量可以在后期改变赋值类型。
    • Object 是Dart所有对象的根基类,也就是说所有类型都是Object的子类(包括Function和Null),所以任何类型的数据都可以赋值给Object声明的对象。
  • final、const、static
    • final、const相同点:
      两者都是声明不可更改变量,变量只能设置一次;
      变量类型可以省略;
    • final、const不同点:
      • final:
        声明文件中的变量:必须在声明时赋值;
        声明类的成员变量:可以在声明时赋值,也可以通过构造函数赋值语法糖ClassName({this.variable}),或者初始化列表的方式赋值;
      • const:
        编译时常量,必须定义的时候初始化;
        const 变量是类级别的,需要标记为 static const;
        const修饰类的构造函数时,它要求该类的所有成员都必须是final的。
        作为修饰值的时候,对象的整个深度状态可以在编译时完全确定,并且对象将被冻结并且完全不可变,eg:const Point(0, 0);
  • 明确声明
    1
    2
    3
    4
    String name = 'coderwhy';
    int age = 18;
    double height = 1.88;
    print('${name}, ${age}, ${height}');
  • 类型推导
    1
    var/dynamic/const/final 变量名称 = 赋值;
  • var 变量
    1
    2
    3
    var name = 'coderwhy';
    name = 'kobe';
    print(name.runtimeType); // String
  • dynamic
    • 如果确实希望这样做,可以使用dynamic来声明变量:
    • 但是在开发中, 通常情况下不使用dynamic, 因为类型的变量会带来潜在的危险
    1
    2
    3
    4
    dynamic name = 'coderwhy';
    print(name.runtimeType); // String
    name = 18;
    print(name.runtimeType); // int
  • finalconst
    • finalconst都是用于定义常量的, 也就是定义之后值都不可以修改
    • const在赋值时, 赋值的内容必须是在编译期间就确定下来的
    • final在赋值时, 可以动态获取, 比如赋值一个函数
    1
    2
    3
    4
    5
    6
    7
    8
    String getName() {
    return 'coderwhy';
    }

    main(List<String> args) {
    const name = getName(); // 错误的做法, 因为要执行函数才能获取到值
    final name = getName(); // 正确的做法
    }
  • const
    • const放在赋值语句的右边,可以共享对象,提高性能:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Person {
    const Person();
    }

    main(List<String> args) {
    final a = const Person();
    final b = const Person();
    print(identical(a, b)); // true
    final m = Person();
    final n = Person();
    //判断是不是同一个对象
    print(identical(m, n)); // false
    }

数字和字符串转换

  • 字符串和数字之间的转化:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 1.字符串转数字
    var one = int.parse('111');
    var two = double.parse('12.22');

    // 2.数字转字符串
    var num1 = 123;
    var num2 = 123.456;
    var num1Str = num1.toString();
    var num2Str = num2.toString();
    var num2StrD = num2.toStringAsFixed(2); // 保留两位小数

集合类型

  • List (数组)
    1
    2
    3
    4
    5
    6
    7
    var letters = ['a', 'b', 'c', 'd']; // 1.使用类型推导定义   
    List<int> numbers = [1, 2, 3, 4]; // 2.明确指定类型
    int count = letters.length; //获取长度
    numbers.add(5); // 添加
    numbers.remove(1); //删除
    bool isCon = numbers.contains(2); //包含元素
    numbers.removeAt(3); //根据index删除元素
  • set (无序数组,并且元素是不重复的)
    1
    2
    3
    4
    5
    6
    var lettersSet = {'a', 'b', 'c', 'd'};  // 1.使用类型推导定义
    Set<int> numbersSet = {1, 2, 3, 4}; // 2.明确指定类型
    int count = lettersSet.length //获取长度
    numbersSet.add(5);//添加
    numbersSet.remove(1);//删除
    bool isCon =numbersSet.contains(2); //包含元素
  • Map(字典)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    var infoMap1 = {'name': 'why', 'age': 18};// 1.使用类型推导定义   
    Map<String, Object> infoMap2 = {'height': 1.88, 'address': '北京市'}; // 2.明确指定类型
    int count = infoMap1.length; //获取长度
    // 1.根据key获取value
    String name = infoMap1['name'];//根据key获取value

    // 2.获取所有的entries
    print('${infoMap1.entries} ${infoMap1.entries.runtimeType}'); // (MapEntry(name: why), MapEntry(age: 18)) MappedIterable<String, MapEntry<String, Object>>

    // 3.获取所有的keys
    print('${infoMap1.keys} ${infoMap1.keys.runtimeType}'); // (name, age) _CompactIterable<String>

    // 4.获取所有的values
    print('${infoMap1.values} ${infoMap1.values.runtimeType}'); // (why, 18) _CompactIterable<Object>

    // 5.判断是否包含某个key或者value
    print('${infoMap1.containsKey('age')} ${infoMap1.containsValue(18)}'); // true true

    // 6.根据key删除元素
    infoMap1.remove('age');
    print('${infoMap1}'); // {name: why}

函数

  • 基本定义
    1
    2
    3
    4
    5
    6
    // 返回值可以省略
    int sum(num num1, num num2) {
    return num1 + num2;
    }
    //只有一个表达式, 可以使用箭头函数
    sum(num1, num2) => num1 + num2;
  • 可选参数
    命名可选参数: {param1, param2, ...}
    位置可选参数: [param1, param2, ...]
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 命名可选参数
    printInfo1(String name, {int age, double height}) {
    print('name=$name age=$age height=$height');
    }
    printInfo1('why', height: 1.88);

    // 定义位置可选参数 height = 18.0
    printInfo2(String name, [int age, double height]) {
    print('name=$name age=$age height=$height');
    }
    printInfo2('why', 18);
  • 参数默认值
    1
    2
    3
    printInfo4(String name, {int age = 18, double height=1.88}) {
    print('name=$name age=$age height=$height');
    }
  • 函数是一等公民
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    main(List<String> args) {
    // 1.将函数赋值给一个变量
    var bar = foo;
    print(bar);
    // 2.将函数作为另一个函数的参数
    test(foo);
    // 3.将函数作为另一个函数的返回值
    var func =getFunc();
    func('kobe');
    }
    // 1.定义一个函数
    foo(String name) {
    print('传入的name:$name');
    }

    // 2.将函数作为另外一个函数的参数
    test(Function func) {
    func('coderwhy');
    }

    // 3.将函数作为另一个函数的返回值
    getFunc() {
    return foo;
    }
  • 匿名函数的使用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     main(List<String> args) {
    // 1.定义数组
    var movies = ['盗梦空间', '星际穿越', '少年派', '大话西游'];

    // 2.使用forEach遍历: 有名字的函数
    printElement(item) {
    print(item);
    }
    movies.forEach(printElement);

    // 3.使用forEach遍历: 匿名函数
    movies.forEach((item) {
    print(item);
    });
    movies.forEach((item) => print(item));
    }

运算符

  • 除法、整除、取模运算
    1
    2
    3
    4
    var num = 7;
    print(num / 3); // 除法操作, 结果2.3333..
    print(num ~/ 3); // 整除操作, 结果2;
    print(num % 3); // 取模操作, 结果1;
  • ??=赋值操作
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     //当变量为null时,使用后面的内容进行赋值。
    //当变量有值时,使用自己原来的值。
    main(List<String> args) {
    var name1 = 'coderwhy';
    print(name1);
    // var name2 = 'kobe';
    var name2 = null;
    name2 ??= 'james';
    print(name2); // 当name2初始化为kobe时,结果为kobe,当初始化为null时,赋值了james
    }
  • 条件运算符:??
    1
    2
    3
    4
    var temp = 'why';
    var temp = null;
    var name = temp ?? 'kobe';
    print(name);
  • 级联语法:..
    1
    2
    3
    4
    5
    final p2 = Person()
    ..name = "why"
    ..run()
    ..eat()
    ..swim();

流程控制

  • if和else
    和swift一样,不支持非空即真或者非0即真
  • for 循环
    1
    2
    3
    4
    5
    6
    7
    for (var i = 0; i < 5; i++) {
    print(i);
    }
    var names = ['why', 'kobe', 'curry'];
    for (var name in names) {
    print(name);
    }
  • switch-case
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //默认情况下必须以一个break结尾
    switch (direction) {
    case 'east':
    print('东面');
    break;
    case 'south':
    print('南面');
    break;
    default:
    print('其他方向');
    }

类和对象

  • 类的定义
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Person {
    String name;
    eat() {
    //Dart的开发风格中,在方法中通常使用属性时,会省略this,但是有命名冲突时,this不能省略
    print('${name}在吃东西');
    }
    }
    //从Dart2开始,new关键字可以省略
    var p = new Person(); // 直接使用Person()也可以创建
  • 构造方法
    类中没有明确指定构造方法时,将默认拥有一个无参的构造方法
    当有了自己的构造方法时,默认的构造方法将会失效
    Dart本身不支持函数的重载
    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
     class Person {
    String name;
    int age;
    //当
    //1. 普通构造方法
    Person(String name, int age) {
    this.name = name;
    this.age = age;
    }
    // 等同于
    Person(this.name, this.age);

    //2. 命名构造方法
    Person.withArgments(String name, int age) {
    this.name = name;
    this.age = age;
    }

    //3. 重定向构造方法
    Person.fromName(String name) : this(name, 0);
    }

    //4. 常量构造方法
    //-- 所有的成员变量必须是final修饰的
    //-- 创建出相同的对象,不再使用 new关键字,而是使用const关键字
    var p1 = const Person('why');
    var p2 = const Person('why');
    class Person {
    final String name;
    const Person(this.name);
    }

    //5. 工厂构造方法 factory
    //-- 必须返回对象
    var p1 = Person('why');
    var p2 = Person('why');
    print(identical(p1, p2)); // true
    class Person {
    String name;
    static final Map<String, Person> _cache = <String, Person>{};
    factory Person(String name) {
    if (_cache.containsKey(name)) {
    return _cache[name];
    } else {
    final p = Person._internal(name);
    _cache[name] = p;
    return p;
    }
    }
    Person._internal(this.name);
    }
  • 初始化列表 :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Point {
    final num x;
    final num y;
    final num distance;
    // 错误写法
    // Point(this.x, this.y) {
    // distance = sqrt(x * x + y * y);
    // }
    // 正确的写法 初始化列表可以进行函数等操作
    Point(this.x, this.y) : distance = sqrt(x * x + y * y);
    }
  • settergetter
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    main(List<String> args) {
    final d = Dog("黄色");
    d.setColor = "黑色";
    print(d.getColor);
    }

    class Dog {
    String color;
    String get getColor {
    return color;
    }
    set setColor(String color) {
    this.color = color;
    }
    Dog(this.color);
    }
  • 类的继承 extends
    父类中的所有成员变量和方法都会被继承,,但是构造方法除外。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class Animal {
    int age;
    Animal(this.age);
    run() {
    print('在奔跑ing');
    }
    }

    class Person extends Animal {
    String name;
    //如果父类没有无参默认构造方法,
    //则子类的构造方法必须在初始化列表中通过super显式调用父类的某个构造方法。
    Person(String name, int age) : name=name, super(age);
    //重写父类方法
    @override
    run() {
    print('$name在奔跑ing');
    }
    }
  • 抽象类 abstract
    **抽象类不能实例化.
    **抽象类中的抽象方法必须被子类实现, 抽象类中的已经被实现方法, 可以不被子类重写.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    abstract class Shape {
    getArea();
    }

    class Reactangle extends Shape {
    double w;
    double h;

    Reactangle(this.w, this.h);

    @override
    getArea() {
    return w * h;
    }
    }
  • 隐式接口implements
    Dart中的接口比较特殊, 没有一个专门的关键字来声明接口.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    abstract class Runner {
    run();
    }

    abstract class Flyer {
    fly();
    }
    //类中所有的方法都必须被重新实现(无论这个类原来是否已经实现过该方法)。
    class SuperMan implements Runner, Flyer {
    @override
    run() {
    print('超人在奔跑');
    }

    @override
    fly() {
    print('超人在飞');
    }
    }
  • Mixin混入 with

    在通过implements实现某个类时,类中所有的方法都必须被重新实现(无论这个类原来是否已经实现过该方法)。
    但是某些情况下,一个类可能希望直接复用之前类的原有实现方案,怎么做呢?
    使用继承吗?但是Dart只支持单继承,那么意味着你只能复用一个类的实现。
    Dart提供了另外一种方案: Mixin混入的方式
    除了可以通过class定义类之外,也可以通过mixin关键字来定义一个类
    只是通过mixin定义的类用于被其他类混入使用,通过with关键字来进行混入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    main(List<String> args) {
    var superMan = SuperMain();
    superMan.run();
    superMan.fly();
    }

    mixin Runner {
    run() {
    print('在奔跑');
    }
    }

    mixin Flyer {
    fly() {
    print('在飞翔');
    }
    }

    // implements的方式要求必须对其中的方法进行重新实现
    // class SuperMan implements Runner, Flyer {}
    class SuperMain with Runner, Flyer {

    }
  • 类成员和方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class Student {
    String name;
    int sno;
    //类成员变量
    static String time;
    //对象方法
    study() {
    print('$name在学习');
    }
    // 类方法
    static attendClass() {
    print('去上课');
    }
    }
  • external@patch
    external声明的方法,通过@patch注解实现
    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
    class Object {
    const Object();
    external bool operator ==(other);
    external int get hashCode;
    external String toString();
    @pragma("vm:entry-point")
    external dynamic noSuchMethod(Invocation invocation);
    external Type get runtimeType;
    }
    @patch
    class Object {
    ...
    @patch
    bool operator ==(Object other) native "Object_equals";

    static final _hashCodeRnd = new Random();

    static int _objectHashCode(obj) {
    var result = _getHash(obj);
    if (result == 0) {
    // We want the hash to be a Smi value greater than 0.
    result = _hashCodeRnd.nextInt(0x40000000);
    do {
    result = _hashCodeRnd.nextInt(0x40000000);
    } while (result == 0);
    _setHash(obj, result);
    }
    return result;
    }

    @patch
    int get hashCode => _objectHashCode(this);


    @patch
    String toString() native "Object_toString";

    @patch
    @pragma("vm:exact-result-type", "dart:core#_Type")
    Type get runtimeType native "Object_runtimeType";
    ...
    }

枚举

  • 定义
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    enum Colors {
    red,
    green,
    blue
    }
    main(List<String> args) {
    // index: 用于表示每个枚举常量的索引, 从0开始.
    print(Colors.red.index);
    print(Colors.green.index);
    print(Colors.blue.index);
    // values: 包含每个枚举值的List.
    print(Colors.values);
    }

泛型

  • list和map的泛型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 创建List的方式
    var names1 = ['why', 'kobe', 'james', 111];
    print(names1.runtimeType); // List<Object>

    // 限制类型
    var names2 = <String>['why', 'kobe', 'james', 111]; // 最后一个报错
    List<String> names3 = ['why', 'kobe', 'james', 111]; // 最后一个报错

    // 创建Map的方式
    var infos1 = {1: 'one', 'name': 'why', 'age': 18};
    print(infos1.runtimeType); // _InternalLinkedHashMap<Object, Object>

    // 对类型进行显示
    Map<String, String> infos2 = {'name': 'why', 'age': 18}; // 18不能放在value中
    var infos3 = <String, String>{'name': 'why', 'age': 18}; // 18不能放在value中

  • 自定义泛型
    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
    class Location<T> {
    T x;
    T y;
    Location(this.x, this.y);
    }
    // 使用
    Location l2 = Location<int>(10, 20);
    print(l2.x.runtimeType); // int

    Location l3 = Location<String>('aaa', 'bbb');
    print(l3.x.runtimeType); // String
    //------------------------------------------------------
    //如果我们希望类型只能是num类型, 怎么做呢?
    class Location<T extends num> {
    T x;
    T y;
    Location(this.x, this.y);
    }
    // 正确写法, 类型必须继承自num
    Location l2 = Location<int>(10, 20);
    print(l2.x.runtimeType);

    // 错误的写法, 类型必须继承自num
    Location l3 = Location<String>('aaa', 'bbb');
    print(l3.x.runtimeType);

库的使用

  • 导入
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //来自dart标准版
    //dart:前缀表示Dart的标准库,如dart:io、dart:html、dart:math
    import 'dart:io';

    //当然,你也可以用相对路径或绝对路径的dart文件来引用
    import 'lib/student/student.dart';

    //Pub包管理系统中有很多功能强大、实用的库,可以使用前缀 package:
    import 'package:flutter/material.dart';

    //**show关键字:**可以显示某个成员(屏蔽其他)
    import 'lib/student/student.dart' show Student, Person;

    //**hide关键字:**可以隐藏某个成员(显示其他)
    import 'lib/student/student.dart' hide Person;

    //当各个库有命名冲突的时候,可以使用as关键字来使用命名空间
    import 'lib/student/student.dart' as Stu;
  • 库的定义
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //library关键字
    //通常在定义库时,我们可以使用library关键字给库起一个名字。
    library math;

    //part关键字 不过官方已经不建议使用这种方式了
    //----

    //export关键字,可以把多个dart文件集中到一起,引入utils就可以
    library utils;
    export "mathUtils.dart";
    export "dateUtils.dart";

参考文章

Flutter中文网
Flutter系列教程