定义变量
- 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);
- final:
- final、const相同点:
- 明确声明
1
2
3
4String name = 'coderwhy';
int age = 18;
double height = 1.88;
print('${name}, ${age}, ${height}'); - 类型推导
1
var/dynamic/const/final 变量名称 = 赋值;
var变量1
2
3var name = 'coderwhy';
name = 'kobe';
print(name.runtimeType); // Stringdynamic- 如果确实希望这样做,可以使用
dynamic来声明变量: - 但是在开发中, 通常情况下不使用dynamic, 因为类型的变量会带来潜在的危险
1
2
3
4dynamic name = 'coderwhy';
print(name.runtimeType); // String
name = 18;
print(name.runtimeType); // int- 如果确实希望这样做,可以使用
final和constfinal和const都是用于定义常量的, 也就是定义之后值都不可以修改const在赋值时, 赋值的内容必须是在编译期间就确定下来的final在赋值时,可以动态获取, 比如赋值一个函数
1
2
3
4
5
6
7
8String 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
13class 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
7var 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
6var 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
21var 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
3printInfo4(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
24main(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
16main(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
4var 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
4var temp = 'why';
var temp = null;
var name = temp ?? 'kobe';
print(name); - 级联语法:
..1
2
3
4
5final p2 = Person()
..name = "why"
..run()
..eat()
..swim();
流程控制
- if和else
和swift一样,不支持非空即真或者非0即真 - for 循环
1
2
3
4
5
6
7for (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
9class 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
51class 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
11class 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);
} setter和getter1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16main(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
19class 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);
//重写父类方法
run() {
print('$name在奔跑ing');
}
} - 抽象类
abstract
**抽象类不能实例化.
**抽象类中的抽象方法必须被子类实现, 抽象类中的已经被实现方法, 可以不被子类重写.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15abstract class Shape {
getArea();
}
class Reactangle extends Shape {
double w;
double h;
Reactangle(this.w, this.h);
getArea() {
return w * h;
}
} 隐式接口和implements
Dart中的接口比较特殊, 没有一个专门的关键字来声明接口.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19abstract class Runner {
run();
}
abstract class Flyer {
fly();
}
//类中所有的方法都必须被重新实现(无论这个类原来是否已经实现过该方法)。
class SuperMan implements Runner, Flyer {
run() {
print('超人在奔跑');
}
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
23main(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
14class 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
42class Object {
const Object();
external bool operator ==(other);
external int get hashCode;
external String toString();
("vm:entry-point")
external dynamic noSuchMethod(Invocation invocation);
external Type get runtimeType;
}
class Object {
...
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;
}
int get hashCode => _objectHashCode(this);
String toString() native "Object_toString";
("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
13enum 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
25class 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";