Flutter 学习小记
-
用
var
声明变量,如果初始化时不指定类型(即只声明不赋值),则其是动态类型dynamic
,可以给其赋值任意类型的值。如果指定了类型,则其类型就确定了,后续不能更改。var a; // 初始化没有指定类型,a 为动态类型(相当于:dynamic a;) a = 2; // ✅ a = 'hello'; // ✅ var b = 1; // 初始化就确定了值的类型,b的类型不能再更改(相当于:int b = 1;) b = 'hello'; // ❌
-
final
修饰的变量在声明时必须初始化,且不能被再次赋值。void main() { int a; // ✅ final a = 1; ✅ final a; // ❌ }
-
const
声明的必须是编译器常量
。int getNum() { return 1; } void main() { final a = getNum(); // ✅ const b = getNum(); // ❌ }
-
int
和double
都是num
类型的子类int a = 1; a = 1.1; // ❌ (不能把 double 类型赋值给 int 类型) num c = 3; // 用 num 类型声明 c,c 既可以是 int 类型,也可以是 double 类型 c = 3.3 // ✅
-
通过提供一个
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.
-
如果一个对象等于
null
,调用它的方法,运行时会报错List a; a.add(1); // ❌ Unhandled Exception: NoSuchMethodError: The method 'add' was called on null. a?.add(1); // ✅ 由于 ? 判断出 a 等于 null,便直接忽略掉后续操作:add()
-
用
const
定义一个不可变的List
,如果执行修改操作,运行时会报错List a = const [1, 2]; a.add(3); // ❌ Unhandled Exception: Unsupported operation: Cannot add to an unmodifiable list
-
as
、is
、is!
操作符在运行时用于检查类型非常方便as:类型转换 is:当对象是相应类型时返回 true is!:当对象不是相应类型时返回 true
-
赋值操作符
??=
,仅当变量为null
时赋值var a = 1; a ??= 2; // print(a) // 输出 1 var b; b ??= 2; print(b) // 输出 2
-
级联符号
..
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'); // ✅
-
使用
dynamic
方式定义方法getUser(name) => '$name is very good!'; // 参数和返回值类型都是 dynamic 的 print(getUser('zhangsan'));
-
方法的参数:
-
必要参数;
getProduct(num id, String description) {}; getProduct(1, 'description'); // ✅ getProduct(id: 1, description: 'description'); // ❌ 调用时 不能写参数名
-
可选参数:
-
命名参数,表达形式:{k: v} 键值对;
getProduct({bool flag, String name}) {}; getProduct(); // ✅ getProduct(flag: true); // ✅ getProduct(name: 'zhangsan'); // ✅ getProduct(flag: true, name: 'zhangsan'); // ✅
-
位置参数,表达形式:[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'); // ❌ 调用时 不能写参数名
-
注意:不能同时使用可选的位置参数和可选的命名参数。必要参数定义在参数列表前面,可选参数则定义在必要参数后面。
-
-
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. }
-
常量构造方法,需要把该类的构造方法用
const
修饰,并且该类的所有实例属性必须是final
的class Student { final String name; final int age; // 常量构造方法 const const Student(this.name, this.age); } const s = Student('zhangsan', 18);
-
getter
方法class Student { int _age; int get age => _age; // 或 int get age { return _age; } }
-
setter
方法class Student { int _age; set age(int age) => _age = age; // 或 set age(int age) { _age = age; } }
-
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
-
如果类实现了
call
方法,则该类的对象可以作为方法使用class Student { call(int x, int y) { // 可以不加参数 print(x + y); } } var a = Student(); a(1, 2); // 输出 3
-
abstract
抽象类中的抽象方法,子类必须重写abstract class Person { say() {} // 普通方法或属性 sleep(); // 抽象方法 } class Student extends Person { @override sleep() { // TODO: implement sleep return null; } }
抽象类常用于声明接口方法,通常不能被实例化,但有时也会有具体的方法实现。如果想让抽象类同时可被实例化,可以为其定义 工厂构造函数。
-
Dart 是单继承,但一个类可以 实现
implements
多个接口。class Point implements Comparable, Location {...}
-
import
时使用as
可以为模块中的代码重命名(可以避免不同模块中的同名代码冲突),白可以使用show
或hide
关键字来 指定 暴露或隐藏 模块中的部分代码。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 方法不可见
-
在
Container
中嵌套Container
,如果不设置 外层Container
的alignment
,则内层的Container
会填满外层的Container
,内层Container
设置的宽高会被忽略。