Flutter 学习小记

2020-04-22  本文已影响0人  plantseeds

学习自视频 [千锋]2020全新Dart Flutter开发教程

  1. var 声明变量,如果初始化时不指定类型(即只声明不赋值),则其是动态类型 dynamic,可以给其赋值任意类型的值。如果指定了类型,则其类型就确定了,后续不能更改。

    var a; // 初始化没有指定类型,a 为动态类型(相当于:dynamic a;)
    a = 2; // ✅
    a = 'hello'; // ✅
    
    var b = 1; // 初始化就确定了值的类型,b的类型不能再更改(相当于:int b = 1;)
    b = 'hello'; // ❌
    
  2. final 修饰的变量在声明时必须初始化,且不能被再次赋值。

    void main() {
      int a; // ✅
      final a = 1; ✅
      final a; // ❌
    }
    
  3. const 声明的必须是 编译器常量

    int getNum() { return 1; }
    
    void main() {
      final a = getNum(); // ✅
      const b = getNum(); // ❌
    }
    
  4. intdouble 都是 num 类型的子类

    int a = 1;
    a = 1.1; // ❌ (不能把 double 类型赋值给 int 类型)
    
    num c = 3; // 用 num 类型声明 c,c 既可以是 int 类型,也可以是 double 类型
    c = 3.3 // ✅
    
  5. 通过提供一个 r 前缀,可以创建一个 原始raw 字符串

    var s = r"In a raw string, even \n isn't special.";
    print(s); // 不会产生换行效果,打印结果为:In a raw string, even \n isn't special.
    
  6. 如果一个对象等于 null,调用它的方法,运行时会报错

    List a;
    
    a.add(1); // ❌ Unhandled Exception: NoSuchMethodError: The method 'add' was called on null.
    
    a?.add(1); // ✅ 由于 ? 判断出 a 等于 null,便直接忽略掉后续操作:add()
    
  7. const 定义一个不可变的 List,如果执行修改操作,运行时会报错

    List a = const [1, 2];
    a.add(3); // ❌ Unhandled Exception: Unsupported operation: Cannot add to an unmodifiable list
    
  8. asisis! 操作符在运行时用于检查类型非常方便

    as:类型转换
    is:当对象是相应类型时返回 true
    is!:当对象不是相应类型时返回 true
    
  9. 赋值操作符 ??= ,仅当变量为 null 时赋值

    var a = 1;
    a ??= 2; // 
    print(a) // 输出 1
      
    var b;
    b ??= 2;
    print(b) // 输出 2
    
  10. 级联符号 ..

querySelector('#confirm') // 获取一个对象
  ..text = 'Confirm' // 使用它的成员
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('confirmed!'));

上述代码相当于:

var button = querySelector('#confirm');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('confirmed'));

可以看出,遵循级联符号的代码都是对第一个方法返回的 button 对象进行操作,而忽略任何可能返回的后续值。

级联操作符可以嵌套

final addressBook = (AddressBookBuilder()
      ..name = 'jenny'
      ..email = 'jenny@example.com'
      ..phone = (PhoneNumberBuilder()
            ..number = '415-555-0100'
            ..label = 'home')
          .build())
    .build();

注意

var a = StringBuffer();
a.write('foo') // 这儿是用的'.',而非级联运算符'..'
 ..write('bar'); // ❌ 第一个 a.write() 返回值是 void,返回值为 void 的方法则不能使用级联运算符


var b = StringBuffer();
b
  ..write('foo')
  ..write('bar'); // ✅
  1. 使用 dynamic 方式定义方法

    getUser(name) => '$name is very good!'; // 参数和返回值类型都是 dynamic 的
    
    print(getUser('zhangsan'));
    
  2. 方法的参数:

    1. 必要参数;

      getProduct(num id, String description) {};
      
      getProduct(1, 'description'); // ✅
      getProduct(id: 1, description: 'description'); // ❌ 调用时 不能写参数名
      
    2. 可选参数:

      1. 命名参数,表达形式:{k: v} 键值对;

        getProduct({bool flag, String name}) {};
        
        getProduct(); // ✅
        getProduct(flag: true); // ✅
        getProduct(name: 'zhangsan'); // ✅
        getProduct(flag: true, name: 'zhangsan'); // ✅
        
      2. 位置参数,表达形式:[type name]

        getProduct([bool flag, String name]) {};
        
        getProduct(true); // ✅
        getProduct(true, 'zhangsan'); // ✅
        getProduct(null, 'zhangsan'); // ✅
        getProduct('zhangsan'); // ❌ 必须传前面的 flag 参数,否则会提示 String和bool 类型不匹配
        getProduct(flag: true, name: 'zhangsan'); // ❌ 调用时 不能写参数名
        

    注意:不能同时使用可选的位置参数和可选的命名参数。必要参数定义在参数列表前面,可选参数则定义在必要参数后面。

  3. Dart 中的构造方法是不支持重载的,可以通过 命名的构造方法 来实现构造方法的重载:

    class Student {
      String name;
      final int gender;
    
      Student(this.name, this.gender);
    
      // 命名的构造方法
      Student.withName(this.gender) {}
    }
    

    注意:用 final 修饰的属性 gender,不能用如下写法来初始化:

    Student(String name, int gender) {
      this.name = name;
      this.gender = gender; // ❌ 'gender' can't be used as a setter because it is final.
    }
    
  4. 常量构造方法,需要把该类的构造方法用 const 修饰,并且该类的所有实例属性必须是 final

    class Student {
      final String name;
      final int age;
    
      // 常量构造方法 const
      const Student(this.name, this.age);
    }
    
    const s = Student('zhangsan', 18);
    
  5. getter 方法

    class Student {
      int _age;
      
      int get age => _age; // 或 int get age { return _age; }
    }
    
  6. setter 方法

    class Student {
      int _age;
      
      set age(int age) => _age = age; // 或 set age(int age) { _age = age; }
    }
    
  7. factory 工厂构造函数:使用 factory 关键字标识的构造函数,意味着使用该构造函数 构造类的实例时,并非总是会返回新的实例对象。例如,工厂构造函数可能会从缓存中返回一个实例,或者返回一个子类型的实例。

    class Person {
      static final Map<String, Person> _cache = {};
    
      // 工厂构造方法
      factory Person() {
        return _cache.putIfAbsent('p', () => Person._inner());
      }
    
      // 命名的私有构造方法
      Person._inner();
    }
    
    var a = Person(); // 缓存中没有,a 是新建的实例,并存入 缓存 _cache 中
    var b = Person(); // 缓存中有了,b 是从缓存 _cache 中读取的实例
    print(a == b); // 输出 true
    
  8. 如果类实现了 call 方法,则该类的对象可以作为方法使用

    class Student {
      call(int x, int y) { // 可以不加参数
        print(x + y);
      }
    }
    
    var a = Student();
    a(1, 2); // 输出 3
    
  9. abstract 抽象类中的抽象方法,子类必须重写

    abstract class Person {
      say() {} // 普通方法或属性
    
      sleep(); // 抽象方法
    }
    
    class Student extends Person {
      @override
      sleep() {
        // TODO: implement sleep
        return null;
      }
    }
    

    抽象类常用于声明接口方法,通常不能被实例化,但有时也会有具体的方法实现。如果想让抽象类同时可被实例化,可以为其定义 工厂构造函数

  10. Dart 是单继承,但一个类可以 实现implements 多个接口。

    class Point implements Comparable, Location {...}
    
  11. import 时使用 as 可以为模块中的代码重命名(可以避免不同模块中的同名代码冲突),白可以使用 showhide 关键字来 指定 暴露或隐藏 模块中的部分代码。

    import './one.dart' as lib1;
    import './two.dart' as lib2 show method2; // lib2 中仅 method2 方法可见
    import './three.dart' as lib3 hide method3, mehtod33; // lib3 中的 method3 和 method33 方法不可见
    
  12. Container 中嵌套 Container,如果不设置 外层 Containeralignment,则内层的 Container 会填满外层的 Container,内层 Container 设置的宽高会被忽略。

上一篇下一篇

猜你喜欢

热点阅读