Flutter 学习小记

学习自视频 [千锋]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 设置的宽高会被忽略。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容