变量与常量的声明
-
var声明变量
var age = 20; // age = "abc"; //错误,上面已经推导age为数字类型 age = 30; -
final声明常量:可以通过计算/函数来获取一个值(可以在运行期间赋值)
final height = 1.88; //height = 2.00;//错误,常量不能改变 -
const声明常量:声明的时候必须赋值,且必须为常量值(编译期间需要该值)
const address = "北京市"; //address = "上海市";//错误,常量不能改变
数据类型
- 数字类型
- 布尔类型,取值true/false。dart中没有非空即真或非零即真
- 字符串类型,
- 定义字符串三种方式
var str1 = 'hello'; var str2 = "hello"; var str3 = """" hello, world """; - 字符串拼接:${}
${}中,若其中包含的是单纯的变量,如上面代码所示,那么{}是可以省略的,即var name = "zhangsan"; var msg = "hello,${name}";var msg = "hello,$name";
- 定义字符串三种方式
- 集合类型
- list
var names = ["a","b","c"]; - set
var movies = {"星际穿越","大话西游","盗梦空间"}; - map
var info = { "name":"zhangsan", "age":18 }; - dynamic
1.动态任意类型,所有dart对象的基础类型 2.通过dynamic定义的变量,会关闭类型检查 3.由于关闭了类型检查,所以有的非法代码会编译通过,但是在运行时可能会崩溃
- list
函数
-
定义
int sum(int n1, int n2){ return n1 + n2; }dart中,函数返回值可以省略,开发中不推荐省略
sum(int n1, int n2){ return n1 + n2; } -
参数
函数的参数分为:必选参数和可选参数,可选参数又可以分为位置可选参数和命名可选参数
参数默认值:只有可选参数才可以有默认值
main(List<String> args) { //位置可选参数的使用 sayHello1("zhangsan", 18, 179.0); //命名可选参数的使用 sayHello2("lisi", age:20, height:185.0); sayHello2("wangwu", height:180.0); } /** * 位置可选参数,调用函数时候,即形参跟实参是根据位置匹配的 */ void sayHello1(String name, [int age, double height]){ print("$name,age is $age, height is $height"); } /** * 命名可选参数 */ void sayHello2(String name,{int age = 18, double height}){ print("$name,age is $age, height is $height"); } -
函数是一等公民
这意味着可以将函数赋值一个变量,也可以将函数作为另外一个函数的参数或者返回值来使用
- 函数作为参数
在上面的代码中,可以将test2中的参数使用typedef进行定义:main(List<String> args) { //匿名函数做参数 test1((){ print("匿名函数被调用"); }); //普通函数做参数 test1(bar); //箭头函数做参数:dart中箭头函数跟js中的不同, //dart的箭头函数函数体只能有一行代码 test1(()=>print("箭头函数被调用")); test2((n1,n2){ return n1+n2; }); } void test1(Function foo){//此处的函数签名很简单 foo(); } void bar(){ print("bar was called"); } //注意,此处的函数签名与test1要求传入的函数签名不同 void test2(int foo(int n1, int n2)){ final ret = foo(1,2); print("foo的运算结果是:${ret}"); }typedef Calculate = int Function(int n1, int n2); void test3(Calculate func){ final ret = func(2,2); print('calcluate运算结果是:${ret}'); } - 函数作为返回值
main(List<String> args) { final returnedFunc = test(); print(returnedFunc(3,4)); } typedef Calculate = int Function(int n1, int n2) Calculate test(){ return (n1, n2){ return n1 * n2; }; }
- 函数作为参数
运算符(dart中的特殊运算符)
-
??=
赋值规则:若左侧变量有值,则不赋值,否则,将右侧值赋值给左侧变量
main(List<String> args) { var name = "zhangsan"; name ??= "lisi"; print(name);//zhangsan var age = null; age ??= 18; print(age);//18 } -
??
运算结果:若??左侧有值,优先使用左侧值,否则使用右侧的值
main(List<String> args) { var gender = null; // gender = gender ?? "male"; gender = "male" ?? gender; print(gender); } -
..(级联运算符),当作链式调用来使用即可
流程控制
- for...in
final names = ["zhangsan","lisi","wangwu"]; for (var name in names) { print(name); }
类
- 默认地,所有的类都继承自Object
- 类的构造器
-
类中若实现了自定义构造函数,则默认构造函数失效
class Person { String name; int age; Person(String name, int age){ this.name = name; this.age = age; } }上面代码中,实现了一个自定义的构造函数,则默认的构造函数失效。 dart提供了一种语法糖,可以代替上面的构造函数写法:
Person(this.name,this.age); -
由于dart中不支持函数重载,所以类中不能有多个构造函数。但是我们又需要多个构造函数,如下:
class Person { String name; int age; double height; Person(this.name, this.age, this.height); }上面代码中,有时候我们只想通过name和age来实例化一个Person实例。显然,上面的代码无法满足这样的需求。 一种解决方法是,将height修改为命名可选参数:
Person(this.name, this.age, {this.height});更好的解决方法是,使用命名构造函数:
main(List<String> args) { final p1 = Person("zhangsan", 18); print(p1); final p2 = Person.withAll("lisi", 20, 170); print(p2); final p3 = Person.fromMap({ "name":"wangwu", "age":21, "height":175.0 }); print(p3); } class Person { String name; int age; double height; Person(this.name, this.age); //命名构造函数 Person.withAll(this.name,this.age,this.height); Person.fromMap(Map<String,dynamic>map){ this.name = map["name"]; this.age = map["age"]; this.height = map["height"]; } @override String toString() { return "Name:$name, Age:$age, Height:$height"; } } -
类的初始化列表
如下代码:
class Person { final String name;//常量 final int age;//常量 Person(this.name,this.age); }由于常量的特性:初始化的时候必须赋值,所以,如若将代码修改为下面的代码,实例化并访问age的时候,则报错:
class Person { final String name; final int age; // Person(this.name,this.age); Person(this.name){ this.age = 10; } }相关报错信息:
Error: Final field 'age' is not initialized.Try to initialize the field in the declaration or in every constructor. 上面代码中,已经在构造方法中初始化了age,但还是说age没有被初始化。 原因: 当代码执行到花括号内的时候,表示该实例对象已经初始化完毕。但实际上,age并没有被初始化。所以会有上面的错误产生。
解决方法:使用初始化列表。
main(List<String> args) { final p1 = Person("zhangsan"); print(p1); final p2 = Person("lisi",age: 18); print(p2); } class Person { final String name; final int age; // Person(this.name,this.age); // Person(this.name):age=10{ // } Person(this.name, {int age}): this.age = age ?? 10{//初始列表 } @override String toString() { return "${this.name},${this.age}"; } }为什么不直接使用如下方式来给age赋值呢?
Person(this.name,{this.age=10});当然可以使用这种方式赋值,在此只是演示初始化列表的使用。另外,初始化列表的功能比直接使用上面的方式赋值更强大。可选参数不能使用表达式赋值,只能直接赋值。但是初始化列表可以使用表达式赋值。
-
重定向构造函数
main(List<String> args) { final p = Person("zhangsan"); print(p.age); } class Person { String name; int age; //构造函数重定向 Person(String name): this._internal(name, 0); Person._internal(this.name, this.age); } -
常量构造函数
用const修饰的构造函数是常量构造函数。通过常量构造函数获得的实例,指向同一块内存。
main(List<String> args) { const p1 = const Person("张三"); const p2 = const Person("张三"); print(identical(p1,p2));//true } class Person { final String name; const Person(this.name); }注意:
- 拥有常量构造方法的类中,所有的成员必须是final修饰的。
- 为了可以通过常量构造方法创建出相同的对象,不使用new关键字,而使用const关键字。但是若将结果赋值给const修饰的常量的时候,可以省略const。如下:
main(List<String> args) { final p1 = Person("张三"); final p2 = Person("张三"); print(identical(p1,p2));//false const p3 = Person("李四"); const p4 = Person("李四"); print(identical(p3,p4));//true }
-
工厂构造函数
普通的构造函数会默认返回创建出来的对象。工厂构造函数需要自己手动返回一个对象。
需求:如果名字相同,返回同一个对象。如下:
main(List<String> args) { final p1 = Person.withName("zhangsan"); final p2 = Person.withName("zhangsan"); print(identical(p1,p2)); } class Person { String name; String color; static final Map<String, Person> _nameCache = {}; factory Person.withName(String name){ if (_nameCache.containsKey(name)) { return _nameCache[name]; } else{ final person = Person(name, ''); _nameCache[name] = person; return person; } } Person(this.name, this.color); } -
setter和getter(关键字set/get)
main(List<String> args) { final p = Person(); p.setName = "zhangsan"; print(p.getName); } class Person { String name; //setter set setName(String name){ this.name = name; } //getter String get getName{ return this.name; } } -
继承(关键字:extends)
-
抽象类的使用
1.抽象类使用关键字abstract声明; 2.抽象类中的方法可以有方法体,也可以不实现; 3.继承抽象类的类,必须实现抽象类中没有实现的抽象方法; 4.抽象类若没有工厂构造方法,则不能被实例化。如:Map类是一个抽象类,但是它可以被实例化,就是因为它有一个工厂构造方法;
-
隐式接口
- dart中,没有关键字可以用来定义接口
- 默认地,所有的类都是隐式接口.当一个类被当做接口使用的时候,实现这个接口的类,必须实现其中的所有方法。
如下:
class Running {
void run(){}
}
class Flying {
void flying(){}
}
//将上面两个类当作隐式接口来使用,必须实现其中的所有方法
class Superman implements Running, Flying {
@override
void run() {
print("superman is runnning");
}
@override
void flying() {
print("superman is flying");
}
}
混入
使用隐式接口的时候,需要在实现的类中完全实现接口中所有的方法,哪怕其中的方法已经在隐式接口(也就是类,普通类中的方法必须有)中实现。这种情况下,就可以使用混入了。
混入语法:
- 定义类(隐式接口)的时候需要使用mixin关键字,而非class关键字
- 在实现隐式接口的类中,用with关键字来实现混入
main(List<String> args) {
final superman = Superman();
superman.fly();
superman.run();
}
mixin Running {
void run(){
print("running...");
}
}
mixin Flying {
void fly(){}
}
class Superman with Running, Flying {
@override
void fly() {
print("superman is flying");
}
}
类属性和类方法(使用static关键字修饰的属性及方法)
库
dart中,一个dart文件就是一个库文件。
-
使用系统库。固定格式,如下:
import "dart:库名称";,如:import "dart:async";
-
使用自定义库。
import "自定义库路径";
-
如果导入的库与当前文件有冲突,比如函数名冲突,这时候可以使用as关键字来给导入的库一个别名。
import "some/lib/some_lib.dart" as someLib;
这样在调用相关函数的时候,可以带着别名来调用,以区别于当前文件中的函数。
-
使用第三方库(类似于node)
- 在pubspec.yaml文件中添加响应的依赖库,在进入到该文件所在的目录,执行pub get,即可完成库的安装。
- 在使用该库的地方,按如下格式导入即可(以http库为例)。 > import "package:http/http.dart";
-
默认地,导入一个库的时候,会将库中所有的内容全部导入。如果要导入指定内容,则需要使用show关键字;要隐藏导入的某些内容,使用hide关键字。
-
只导入部分内容
import "some/lib/some_lib.dart" show func1,func2
-
隐藏部分内容,其余的全部导入
import "some/lib/some_lib.dart" hide func1,func2
-
-
export
使用场景:假设有一个功能,实现分散在几个dart文件中,在需要时用该功能的地方导入的时候,我们可能需要一个库(文件)一个库的导入,这时我们可以定义一个文件,将需要用的库,使用export关键字将之导出,然后在用到该功能的地方,导入这一个文件即可。
简言之,上述操作相当于使用了一个公共头文件。
如下:my_math_lib中包含加减乘除运算的四个库(文件)和一个index文件
|-- my_math_lib |---- division.dart(包含double divide(double n1, double n2)函数) |---- multi.dart(包含int multiply(int n1, int n2)函数) |---- sub.dart(包含int sub(int n1, int n2)函数) |---- sum.dart(包含int sum(int n1, int n2)函数) |---- index.dartindex.dart中内容如下:
export 'division.dart'; export 'multi.dart'; export 'sub.dart'; export 'sum.dart';在需要使用该库的地方,只需要导入index.dart即可。如下:
import 'my_math_lib/index.dart'; main(List<String> args) { print(sum(4, 2)); }