相关代码存放于:https://github.com/LinXunFeng/DartStudy
一、变量/常量 明确声明 1 2 3 4 5 String name = 'lxf' ;int age = 18 ;double height = 1.8 ;print ('$name , $age , $height ' );
类型推导 var runtimeType
: 用于获取变量当前的类型
1 2 3 var count = 1 ; print (count.runtimeType);
dynamic
dynamic
声明的变量可以赋值为任意类型
在开发中一般不这种做,因为会带来潜在的危险
1 2 3 4 5 6 7 dynamic type = 'lxf' ;print (type.runtimeType); type = 18 ; print (type.runtimeType);
final & const
final
和 const
都用来定义变量
1 2 3 4 final gender = 'male' ;const country = 'China' ;
区别:
const
: 必须赋值,接收一个常量值(即编译期间就需要确定的值)
final
: 可以通过计算/函数动态获取值(即运行时能确定的值)
1 2 3 4 5 6 7 8 String getName() { return 'lxf' ; } main(List <String > args) { final myName = getName(); }
二、数据类型
注:int
和 double
表示的范围不是固定的,具体取决于运行 Dart
的平台
整型 1 2 3 4 5 int age = 18 ;int hexAge = 0x12 ;print (age); print (hexAge);
浮点型 1 2 3 double height = 1.8 ;print (height);
布尔类型 1 2 var isFlag = true ;print ('$isFlag ${isFlag.runtimeType} ' );
1 2 3 4 var msg = 'lxf' ;if (msg) { print (msg); }
字符串 1 2 3 var s1 = 'hello lxf' ;var s2 = "hello lxf" ;
多行字符串
1 2 3 4 5 var s3 = ''' hello hey lxf''' ;
字符串拼接
字符串和其他变量或表达式拼接: 使用 ${expression}
, 如果表达式是一个标识符, 那么 {}
可以省略
1 2 3 4 5 var s4 = s1 + s2;print (s4); var s5 = 's1 = $s1 and s4 = ${s1 + s2} ' ;print (s5);
三、集合 List
1 2 3 4 5 var letters = ['a' , 'b' , 'c' , 'd' ];print ('$letters ${letters.runtimeType} ' ); List <int > numbers = [1 , 2 , 3 , 4 ];print ('$numbers ${numbers.runtimeType} ' );
List
特有的操作
1 2 3 4 numbers.removeAt(3 ); print ('$numbers ' );
Set
使用 {}
字面量
与 List
的两个不同点:Set
是无序的,且元素不重复
1 2 3 4 5 var lettersSet = {'a' , 'b' , 'c' , 'd' };print ('$lettersSet ${lettersSet.runtimeType} ' ); Set <int > numbersSet = {1 , 2 , 3 , 4 };print ('$numbersSet ${numbersSet.runtimeType} ' );
Map 1 2 3 4 5 6 7 var infoMap1 = {'name' : 'lxf' , 'age' : 18 };print ('$infoMap1 ${infoMap1.runtimeType} ' );Map <String , Object > infoMap2 = {'height' : 1.8 , 'country' : 'China' };print ('$infoMap2 ${infoMap2.runtimeType} ' );
Map
特有的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 print (infoMap1['name' ]); print ('${infoMap1.entries} ${infoMap1.entries.runtimeType} ' );print ('${infoMap1.keys} ${infoMap1.keys.runtimeType} ' );print ('${infoMap1.values} ${infoMap1.values.runtimeType} ' );print ('${infoMap1.containsKey('age' )} ${infoMap1.containsValue(18 )} ' );infoMap1.remove('age' ); print ('${infoMap1} ' );
集合共有的操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 print (letters.length); print (lettersSet.length); print (infoMap1.length); numbers.add(5 ); numbersSet.add(5 ); print ('$numbers $numbersSet ' ); numbers.remove(1 ); numbersSet.remove(1 ); print ('$numbers $numbersSet ' ); print (numbers.contains(2 )); print (numbersSet.contains(2 ));
四、函数
函数也是对象,类型为 Function
函数定义
注意点:Dart
不支持函数重载!(函数重载:函数名称相同,参数不同)
1 2 3 4 返回值 函数的名称(参数列表) { 函数体 return 返回值 }
普通函数
1 2 3 4 5 num sum1(num num1, num num2) { return num1 + num2; } print (sum1(1 , 2 ));
省略了类型注释也可以正常工作,但是不建议这么做
1 2 3 4 5 sum2(num1, num2) { return num1 + num2; } print (sum2(1 , 2 ));
如果函数的实现只有一个表达式,则可以使用箭头语法
1 2 3 sum3(num num1, num num2) => num1 + num2; print (sum3(1 , 2 ));
可选参数 命名可选参数
1 2 3 4 5 6 7 printInfo1(String name, {int ? age, double ? height}) { print ('name = $name age = $age height = $height ' ); }
参数默认值
1 2 3 printInfo3(String name, {int age = 18 , double height = 1.8 }) { print ('name = $name age = $age height = $height ' ); }
调用
1 2 3 4 5 6 7 8 printInfo1('lxf' ); printInfo1('lxf' , age: 18 ); printInfo1('lxf' , age: 18 , height: 1.8 ); printInfo1('lxf' , height: 1.8 ); printInfo3('lxf' );
位置可选参数
定义:位置可选参数: [param1, param2, ...]
注意:位置可选参数无法使用 required
关键字
1 2 3 4 printInfo2(String name, [int ? age, double ? height]) { print ('name = $name age = $age height = $height ' ); }
参数默认值
1 2 3 printInfo4(String name, [int age = 18 , double height = 1.8 ]) { print ('name = $name age = $age height = $height ' ); }
调用
1 2 3 4 5 6 7 printInfo2('lxf' ); printInfo2('lxf' , 18 ); printInfo2('lxf' , 18 , 1.8 ); printInfo4('lxf' );
五、运算符 常见的运算符 1 2 3 4 var num = 10 ;print (num / 3 ); print (num ~/ 3 ); print (num % 3 );
赋值运算符
??=
: 当变量为 null
时,使用后面的内容进行赋值
1 2 3 4 5 6 7 8 var name = null ;name ??= 'lxf' ; print (name); var name1 = 'lin' ;name1 ??= 'lxf' ; print (name1);
条件运算符
??
: 当变量为 null
时,则使用后面的值,否则使用变量的值
1 2 3 var temp = null ;var other = temp ?? 'lxf' ;print (other);
级联
..
: 可以对对象进行连续操作
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 class Person { String name; Person(this .name); void run() { print ("$name is running" ); } void eat() { print ('$name is eating' ); } void swim() { print ('$name is swimming' ); } } main(List <String > args) { final p1 = Person('lxf' ); p1.run(); p1.eat(); p1.swim(); final p2 = Person('lin' ) ..run() ..eat() ..swim(); }
六、流程控制 if
和 else
不支持非空即真或非0即真,必须是明确的 bool
类型
1 2 3 4 5 6 var isHot = true ;if (isHot) { print ('热门' ); } else { print ('冷门' ); }
for
1 2 3 4 5 6 7 8 9 10 for (var i = 0 ; i < 10 ; i++) { print (i); } var names = ['lxf' , 'lin' ];for (var name in names) { print (name); }
while
1 2 3 4 5 var index = 0 ;while (index < 5 ) { print ('index -- $index ' ); index++; }
do-while
1 2 3 4 5 var index = 0 ;do { print ('index -- $index ' ); index++; } while (index < 10 );
break
1 2 3 4 5 6 7 8 9 var i = 1 ;while (i <= 10 ) { if (i % 5 == 0 ) { print ("满足条件,退出循环" ); break ; } i++; }
continue
1 2 3 4 5 6 7 8 9 10 var num = 0 ;var count = 0 ;for (num = 0 ; num <= 20 ; num ++) { if (num % 2 == 0 ) { continue ; } count++; } print ("累加次数: $count " );
switch-case
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var direction = 'east' ;switch (direction) { case 'east' : print ('东面' ); break ; case 'south' : print ('南面' ); break ; case 'west' : print ('西面' ); break ; case 'north' : print ('北面' ); break ; default : print ('其他方向' ); }
六、类 类的定义如下:
1 2 3 4 5 6 class 类名 { 类型 成员名; 返回值类型 方法名(参数列表) { 方法体 } }
late
关键字
作用:显式声明一个非空的变量,但不初始化,延迟变量的初始化
默认的构造函数是无参构建函数,这意味着属性无法从默认构建函数得到初始化,这时有两个选择,
选择一:自定义一个构造函数对属性进行初始化
选择二:使用 late
关键字修饰属性,延迟变量的初始化
加上 late
关键字可以通过静态检查,但是会带来运行时风险,你需要保证属性在使用前有进行初始化!
1 2 3 4 5 6 7 8 9 10 11 class Person { late String name; late int age; } main(List <String > args) { var p1 = Person(); p1.name = 'lxf' ; print (p1.name); print (p1.age); }
构造方法 普通构造方法 当类中没有指定构造方法时,默认会有一个无参的构造方法
1 2 3 4 5 6 7 8 9 class Person { String ? name; int ? age; } main(List <String > args) { var p1 = Person(); }
可以根据自已的需求,定义自已的构建方法,不过需要注意的是:
当定义了自已的构建方法,则默认的无参构造方法会失效,无法使用!
1 2 3 4 5 6 class Person { String name; int age; Person(this .name, this .age); }
命名构造方法 由于 Dart
不支持方法重载,所以没办法创建相同名称的构造方法,如果希望能实现更多的构造方法,这时就需要使用命名构造方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Person { String ? name; int ? age; Person.withArgs(String name, int age) { this .name = name; this .age = age; } } main(List <String > args) { var p1 = Person.withArgs('lxf' , 18 ); print (p1.name); print (p1.age); }
重定向构造方法
在一个构造方法中,调用另一个构造方法进行初始化
1 2 3 4 5 6 7 8 9 class Person { String name; int age; Person(this .name, this .age); Person.from(String name): this (name, 0 ); }
常量构造方法
使用 const
修饰构造方法,当传入相同参数时,可以确保创建出来的对象是相同的
注意点:
拥有常量构造方法的类中,所有的成员变量必须是final修饰的
为了创建出相同的对象,在使用常量构造方法时,需要使用 const
关键字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Person { final String name; const Person(this .name); } main(List <String > args) { var p1 = const Person('lxf' ); var p2 = const Person('lxf' ); var p3 = const Person('lxf1' ); const p4 = Person4('lxf' ); print (identical(p1, p2)); print (identical(p1, p3)); print (identical(p1, p4)); }
工厂构造方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Person { late String name; static final Map <String , Person> _cache = <String , Person>{}; factory Person(String name) { var p = _cache[name]; if (p == null ) { p = Person._internal(name); _cache[name] = p; } return p; } Person._internal(this .name); } main(List <String > args) { var p1 = Person5('lxf' ); var p2 = Person5('lxf' ); print (identical(p1, p2)); }
注意:当工厂构造方法与默认构造方法重名时,无法被继承,会报错
1 2 3 The default constructor of superclass 'Father' (called by the implicit default constructor of 'Son') must be a generative constructor, but factory found. 父类的构造器必须是一个生成的构造器,而不能是工厂构造器
如下方代码所示
1 2 3 4 5 6 7 8 9 10 class Father { factory Father() { return Father._internal(); } Father._internal(); } class Son extends Father {}
需要添加一个生成构造器,并将工厂构造器改为命名构造器
1 2 3 4 5 6 7 8 9 10 11 class Father { Father(); factory Father.factory () { return Father._internal(); } Father._internal(); } class Son extends Father {}
getter
和 setter
当希望类中定义的属性不轻易给外部访问时,就可以用到 getter
和 setter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Marker { String _color; String get color => _color; set color(String color) { _color = color; } Marker(this ._color); } main(List <String > args) { final m = Marker('red' ); m.color = 'blue' ; print (m.color); }
继承
继承使用 extends
关键字,子类中使用 super
来访问父类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Animal { String name; Animal(this .name); eat() { print ("吃吃吃" ); } } class Dog extends Animal { Dog(String name) : super (name); } main(List <String > args) { var dog = Dog("Spike" ); dog.eat(); print (dog.name); }
重写父类的方法,@override
是注释,用来标明当前为重写的方法,可有可无,但一般保留着
1 2 3 4 5 6 7 8 9 class Dog extends Animal { Dog(String name) : super (name); @override eat() { print ("$name 在吃" ); } }
抽象类
抽象类:使用 abstract
声明的类,用于定义通用接口,无法被实例化,方法可以没有实现,该方法称为抽象方法,交由子类去实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 abstract class Building { getHeight(); getArea() { print ("hello" ); } } class Tower extends Building { @override getHeight() { print ("55m" ); } } main(List <String > args) { var tower = Tower(); tower.getArea(); tower.getHeight(); }
隐式接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Runner { run() { print ("running" ); } } abstract class Fly { fly(); } class SuperPerson implements Runner , Fly { @override run() { print ('自由自在的奔跑' ); } @override fly() { print ('起飞' ); } }
Mixin混入 Dart
不支持多继承,使用接口时需要实现接口中的所有方法,而我们需要在当前类可以复用其它类中的实现时就需要用到 Mixin混入
的方式。
特性:
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 mixin Runner on Animal { run() { print ('跑起来了' ); } } class Animal { run() { print ('Animal run' ); } } class SuperPeople extends Animal with Runner {} main(List <String > args) { var p = SuperPeople(); p.run(); }
当前类、父类与混入的实现优先级
总结:当前类 > 混入 > 父类
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 class A { void method() { print ('I am A' ); } } class B { void method() { print ('I am B' ); } } class C extends A with B { } main(List <String > args) { var c = C(); c.method(); }
七、枚举 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 enum Color { red, blue, green }main(List <String > args) { print (Color.red); print (Color.red.index); print (Color.blue.index); print (Color.values); var color = Color.green; switch (color) { case Color.red: print ("红" ); break ; case Color.blue: print ("蓝" ); break ; case Color.green: print ("绿" ); break ; } }
八、泛型 List
使用泛型1 2 3 4 5 6 7 8 9 10 var myList = ['lxf' , 18 ];print (myList.runtimeType); var numList = <int >[10 , 20 ];List <String > nameList = ["lxf" , "lin" ];
Map
使用泛型1 2 3 4 5 6 7 8 9 10 var msgMap = {1 : 'one' , 'name' : 'lxf' , 'age' : 18 };print (msgMap.runtimeType); var infoMap = <String , String >{'name' : 'lxf' , 'age' : '18' };
类定义泛型 将类的属性以泛型做为类型声明,由外部来决定最终的类型
1 2 3 4 5 6 7 8 9 10 11 class Point <T > { T x; T y; Point(this .x, this .y); } main(List <String > args) { Point p = Point<double >(10.5 , 20.5 ); print (p.x.runtimeType); }
如果希望属性的类型只能是数字,可以使用 extends
来限制泛型 T
只能为 num
的子类
1 2 3 4 5 6 7 8 9 10 11 12 class Size <T extends num > { T width; T height; Size(this .width, this .height); } main(List <String > args) { Size s = Size<int >(20 , 10 ); }
九、库的使用 库的导入 Dart
标准库
Dart
标准库的导入前缀为 dart:
1 2 3 import 'dart:io' ;import 'dart:html' ;import 'dart:math' ;
自定义库 libs/math_utils.dart
文件的内容:
1 2 3 4 5 6 7 num sum(num num1, num num2) { return num1 + num2; } num multiply(num num1, num num2) { return num1 * num2; }
使用
1 2 3 4 5 6 7 import 'libs/math_utils.dart' ;main(List <String > args) { print (sum(10 , 20 )); print (multiply(10 , 20 )); }
第三方库 在项目目录下创建 pubspec.yaml
文件,在 dependencies
下添加需要依赖的第三方的库名与版本
第三库的使用说明可在 https://pub.flutter-io.cn 上查找
1 2 3 4 5 6 name: dartStudy description: this is lxf's study project dependencies: dio: ^4.0.0 environment: sdk: ">=2.10.0 <3.0.0"
填写好依赖后,在终端下执行 dart pub get
命令,终端会自动拉取对应版本的第三方文件,之后在代码中就可以导入并使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import 'package:dio/dio.dart' ;main(List <String > args) { getHttp(); } void getHttp() async { try { var response = await Dio().get ('http://www.google.cn' ); print (response); } catch (e) { print (e); } }
延迟加载
作用:在需要的时候才进行加载
好处:减少 App
的启动时间
使用:使用 deferred as
1 2 import 'package:dio/dio.dart' deferred as diolib;
在使用延迟加载的库中,需要调用 loadLibrary
以加载声明为延迟加载的库
1 2 3 4 5 main(List <String > args) async { await diolib.loadLibrary(); diolib.Dio().get ('http://www.google.cn' ); }
给库起别名
一般在与别的库的内容冲突时使用
1 2 3 4 5 6 import 'libs/math_utils.dart' as MathUtils; main(List <String > args) { print (MathUtils.sum(10 , 20 )); print (MathUtils.multiply(10 , 20 )); }
指定库内容的显示与隐藏 1 2 import 'libs/math_utils.dart' show sum, multiply; import 'libs/math_utils.dart' hide sum;
library
、part
和 part of
作用:
library
:定义库名
part
:关联分库文件
part of
:声明隶属哪个库,使用的是 library
定义的库名
特点:
library
、part
和 part of
都必须定义在文件的第一行,否则会报错
主库可以访问分库中的私有成员
如果需要导入其它库,只能在主库中导入,无法在分库中导入,且 import
只能放在 library
和 part
之间
如果外部需要使用到分库的内容,只需要导入主库即可
文件目录结构:
1 2 3 4 5 6 ├── 16_库的定义.dart └── lib └── part ├── fitsize.dart ├── fitsize_part1.dart └── fitsize_part2.dart
fitsize.dart
文件内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 library fitsize;part 'fitsize_part1.dart' ;part 'fitsize_part2.dart' ;void fit() { part1PublicMethod(); _part1PrivateMethod(); part2PublicMethod(); _part2PrivateMethod(); }
fitsize_part1.dart
文件内容
1 2 3 4 5 6 7 8 9 part of fitsize; part1PublicMethod() { print ("part1 public method" ); } _part1PrivateMethod() { print ("part1 private method" ); }
fitsize_part2.dart
文件内容
1 2 3 4 5 6 7 8 9 part of fitsize; part2PublicMethod() { print ("part2 public method" ); } _part2PrivateMethod() { print ("part2 private method" ); }
这样三个 dart
文件就组成了 fitsize
库,如果外部需要使用到分库的内容,只需要导入主库即可。
如果需要导入其它库,只能在主库中导入,无法在分库中导入,且 import
只能放在 library
和 part
之间,否则会报错。
1 2 3 4 5 6 7 8 9 10 library fitsize;import 'dart:io' ; part 'fitsize_part1.dart' ;part 'fitsize_part2.dart' ;
export
官方不再推荐使用 part
关键字,而是推荐使用 export
使用:在一个 dart
文件里使用 export
关键字,将其它库文件导出
作用:这样外部在使用时只需要导入一个文件即可使用完整的库内容
文件目录结构:
1 2 3 4 5 6 7 ├── 16_库的定义.dart └── lib └── export ├── lxf_utils.dart └── source ├── lxf_file_util.dart └── lxf_math_util.dart
lxf_utils.dart
文件内容:
1 2 3 4 5 6 library lxf_utils;export './source/lxf_math_util.dart' ;export './source/lxf_file_util.dart' ;
lxf_math_util.dart
文件内容:
1 2 3 num sum(num num1, num num2) { return num1 + num2; }
lxf_file_util.dart
文件内容:
1 2 3 writeFile(String name, String dir_path) { print ('写入文件 $dir_path $name ' ); }
导入并调用
1 2 3 4 5 6 import 'lib/export/lxf_utils.dart' as utils;main(List <String > args) { print (utils.sum(1 , 2 )); utils.writeFile('lxf.jpg' , '/lxf/Desktop/' ); }