SDK:指一些被软件工程师用于为特定的软件包、软件框架、硬性平台及作业系统等建立应用软件的开发工具之集合。
dart
由于前边的教程使用了老版本Dart,所以部分代码可能存在空安全问题,可以参考”新特性”:
入口方法
Hello world程序的两种写法:
1 2 3 4 5 6 7 8 9
| main() { print('hello dart'); }
void main() { print('hello dart'); }
|
变量
- 可以不预先定义变量类型,用
var
定义变量,类型会自动推导
- 也可以定义变量类型
- 不给变量赋值时,值为null(空)
1 2 3 4 5 6 7 8 9 10
| void main() { var str1 = 'hello dart1'; var num1 = 123; String str2 = 'hello dart2'; int num2 = 456; print(num1); printf("$num2"); }
|
- dart存在类型校验,类型确定后,再给变量赋值为另一个类型,会报错
final和const的区别
- final可以开始不赋值,之后只能赋值一次
- final不仅有const的编译时常量的特性,最重要的是它是
运行时常量
,并且final是惰性初始化,即在运行时第一次使用前才初始化
1 2
| final a = new DateTime.now(); const a = new DateTime.now();
|
运算符
a~/b
:取整。如:5~/4 = 1
??=
:如果左边值不为空,把右值赋给左边
??
:和上方类似
类型转换
xxx is int/double/String...
:判断是否为某类型
int/double.parse()
:转化为整数/浮点数
xxx.toString()
:转化为字符串
xxx.isEmpty
:判断当前字符串是否为空(无括号)
数组与集合
声明数组与扩容
数组可以直接声明,可以加类型:var a = <int>[1, 2, 3]; print(a);
也可以使用List:List b = <String>['4', '5', '6']; print(b);
1 2
| var a = [{"b": 5}]; print(a[0]["b"]);
|
使用add添加元素:
1 2
| List l = <String>['香蕉', '苹果']; l.add('草莓');
|
如果要增加多个元素,使用addAll([元素1, 元素2])
,即拼接数组
声明不可扩容的数组,使用filled:
1 2
| List l = List.filled(2, '');
|
数组方法
isEmpty
:判断数组为空
isNotEmpty
:判断数组不为空
reversed
:数组翻转
1 2 3
| List l = ['西瓜', '苹果', '桃子']; print(l.reversed); print(l.reversed.toList());
|
indexOf(value)
:查找元素,找到则返回序号,找不到返回-1
remove(value)
:根据值删除
removeAt(index)
:根据索引删除
fillRange(index1,index2,value)
:将一段区间内的元素的值修改,不包括右边界
1 2 3
| List l = ['西瓜', '苹果', '桃子', '李子']; l.fillRange(1, 3, '水果'); print(l);
|
insert(index, value)
:指定位置插入
insertAll(index, list)
:指定位置插入数组
join(分隔符)
:数组转换成字符串
split(分隔符)
:字符串转换成数组
Set和Map
集合的创建和添加元素:
1 2 3 4 5 6
| var s = new Set(); s.add('苹果'); s.add('香蕉'); s.add('香蕉');
print(s);
|
数组去重:
1 2 3 4 5
| List myList = ['香蕉', '苹果', '西瓜', '香蕉', '苹果', '香蕉', '苹果']; var s = new Set(); s.addAll(myList); print(s); print(s.toList());
|
映射(Maps)是无序的键值对
创建方式:
1 2 3 4 5 6 7
| Map person = { "name": "张三", "age": 20 };
var m = new Map(); m["name"] = "李四";
|
常用属性:
- keys:获取所有的key值
- values:获取所有的value值
- isEmpty/isNotEmpty
常用方法:
List、set、Map的通用方法:
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
| List l = [1, 3, 5]; var newl = l.map((value) { return value*2; });
l.forEach((value) { print(value); })
Map person = { "name": "张三", "age": 20 }; person.forEach((key, value) { print("$key---$value"); })
var newl = l.where((value) { return value > 3; })
var boolean = l.every((value) { return value > 3; });
var boolean = l.any((value) { return value > 3; });
|
函数
参数
和js的区别:
- 返回值可以限定类型
- 传入的参数可以限定类型
- 函数可以定义在入口函数外部(全局函数),也可以在内部,甚至定义在某个函数的内部,只是作用域不同
可选参数 + 默认参数:
1 2 3 4 5 6 7 8 9 10 11
|
String fn(String username, [String sex = '男', int age]) { if (age != null) { return "姓名:$username ---性别:$sex --- 年龄:$age"; } return "姓名:$username --- 性别:$sex --- 年龄:保密"; } print(fn('张三', 20)); print(fn('小李', '女'));
|
命名参数:
1 2 3 4 5 6 7 8 9
| String fn(String username, {String sex = '男', int age}) { if (age != null) { return "姓名:$username ---性别:$sex --- 年龄:$age"; } return "姓名:$username --- 性别:$sex --- 年龄:保密"; }
print(fn('张三', sex:'男', age:20,));
|
新版本dart应该这样写:
1 2 3 4 5 6
| String fn(String username, {String sex = '男', required int age}) { return "姓名:$username --- 性别:$sex --- 年龄: ${age}"; } void main() { print(fn('张三', sex:'男', age: 20)); }
|
箭头函数
dart的箭头函数内只能写一行,且不能带分号,所以可以在箭头函数中使用三目运算符
1 2 3 4 5 6 7
| list.forEach((value) => print(value));
list.forEach((value) => { print(value) });
|
匿名方法
1 2 3 4 5
| var fn = () { print(123); } fn();
|
IIFE
1 2 3
| (() { print('我是自执行方法'); })();
|
递归
1 2 3 4 5 6 7 8 9 10 11
| var sum = 1; fn(n) { sum *= n; if (n == 1) { return; } fn(n - 1); }
fn(5); print(sum);
|
闭包
目的:使局部变量常驻内存,不污染全局变量
1 2 3 4 5 6 7 8 9 10
| fn() { var a = 1; return() { a++; print(a); }; } var b = fn(); b(); b();
|
面向对象
类与构造函数
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 Person{ String name; int age; Person(String name, int age) { this.name = name; this.age = age; } Person.now() { print('我是命名构造函数'); } void fn() { print("${this.name} --- ${this.age}"); } }
void main() { var d = new DateTime.now(); Person p1 = new Person('张三', 20); p1.fn(); Person.now(); }
|
权限
Dart中没有public、private、protected这些访问修饰符号,定义私有成员,使用_
(只有将类抽成单独的文件再引入时才有效)
将类写入.dart文件,放在/lib目录下,再引入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Animal { String _name; int age; Animal(this._name, this.age); void fn() { print("${this.name} --- ${this.age}"); } String getName() { return this._name; } void _run() { print('这是一个私有方法'); } execRun() { this._run(); } }
|
主文件:
1 2 3 4 5 6 7
| import 'lib/Animal.dart' void main() { Animal a = new Animal('小狗', 3); print(a.getName()); a.execRun(); }
|
getter和setter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class Rect { int height; int width; Rect(int height, int width):height = 2, width = 10 { print("${this.height} --- ${this.width}"); this.height = height; this.width = width; } get area { return this.height * this.width; } set areaHeight(value) { this.height = value; } }
void main() { Rect r = new Rect(10, 4); r.areaHeight = 6; print(r.area); }
|
静态成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Person { static String name = '张三'; int age = 20; Person(this.name, this.age); static void show() { print(name); } void printInfo() { print(name); print(this.age); } }
void main() { print(Person.name); Person.show(); }
|
静态操作符
?
:条件运算符,当左侧不为null时就执行右侧。比如p?.fn(),只有当p不为null时才会执行其fn()方法
as
:类型转换
is
:类型判断
..
:级联操作(连缀)
连缀:
1 2 3 4 5 6 7 8 9
| Person p1 = new Person('张三', 20); p1..name = '李四' ..age = 30 ..printInfo();
|
继承
- 子类使用extends关键字来继承父类
- 子类会继承父类里可见的属性和方法,不会继承构造函数
- 子类能复写父类的getter和setter
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; num age; Person(this.name, this.age); void printInfo() { print("${this.name} --- ${this.age}"); } }
class Web extends Person { String? sex; Web(String name, num age, String sex) : super(name, age) { this.sex = sex; } @override void printInfo() { print("${this.name} --- ${this.age} --- ${this.sex}"); } }
void main() { Web w = new Web('张三', 20, '女'); w.printInfo(); }
|
抽象类、多态与接口
没有方法体的方法,称作抽象方法
如果子类继承抽象类,必须实现里面的抽象方法
如果把抽象类当作接口实现,必须得实现抽象类里面定义的所有属性和方法
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
| abstract class Animal{ eat(); }
class Dog extends Animal { @override eat() { print('小狗在吃骨头'); } run() { print('小狗在跑'); } } class Cat extends Animal { @override eat() { print('小猫在吃鱼'); } speak() { print('小猫在叫'); } }
void main() { Animal a = new Dog(); Animal b = new Cat(); }
|
Dart用抽象类实现接口:
接口就是标准,抽象类里面定义了一些属性和方法,要求子类必须对它们进行实现
extends 抽象类和implements的区别:
- 如果要复用抽象类里面的方法,并且要用抽象方法约束子类,用extends继承抽象类
- 如果只是把抽象类当作标准,就用implments实现抽象类
在Flutter中:
- class 就是 interface
- 当class被当做interface用时,class中的方法就是接口的方法,需要在子类里重新实现,在子类实现的时候要加
@override
- 当class被当做interface用时,class中的成员变量也需要在子类里重新实现。在成员变量前加
@override
- 实现接口可以有多个
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
| abstract class Db { String? address; write(); save(); }
class Mysql implements Db { @override write() { print('Mysql write'); } @override save() { print('Mysql save'); } @override String? address;
Mysql(String address){ this.address = address; } }
class MongoDb implements Db { @override write() { print('MongoDb write'); } @override save() { print('MongoDb save'); }
@override String? address;
MongoDb(String address){ this.address = address; } } void main() { Mysql sq = new Mysql('localhost:127.0.0.1:3000'); sq.write(); MongoDb mg = new MongoDb('localhost:127.0.0.1:3001'); mg.save(); }
|
mixins
mixins意为混入,即在类中混入其他功能,使用with关键字来实现mixins
在Dart中可以使用mixins实现类似多继承的功能
使用mixins的条件:
- 作为mixins的类只能继承自Object,不能继承其他类
- 作为mixins的类不能有构造函数
- 一个类可以mixins多个mixins类
- mixins不是继承,也不是接口,而是一种全新的特性
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
| class Person { String name; num age; Person(this.name, this.age); printInfo() { print('${this.name} --- ${this.age}'); } }
class A { String info = 'this is A'; void printA() { print('A'); } }
class B { void printB() { print('B'); } }
class C extends Person with A, B { C(String name, num age) : super(name, age); }
void main() { var c = new C('张三', 20); c.printInfo(); print(c.info); c.printB; print(c is Object); print(c is A); print(c is B); print(c is C); }
|
泛型
解决代码复用问题,并对特定数据类型进行校验
泛型方法
1 2 3 4 5 6 7 8
| T getData<T>(T value) { return value; }
void main() { getData<String>('你好'); }
|
泛型类
1 2 3 4 5 6 7 8 9 10
| List list = new List<String>.filled(2, ''); list[0] = "张三"; list[1] = "李四"; print(list);
List list2 = new List<int>.filled(2, 0); list2[0] = 1; list2[1] = 2; print(list2);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class MyList<T> { List list = <T>[]; void add(T value) { this.list.add(value); } List getList() { return list; } }
void main() { MyList l = new MyList<String>(); l.add('张三'); print(l.getList()); }
|
泛型接口
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
| abstract class Cache<T> { getByKey(String key); void setByKey(String key, T value); }
class FileCache<T> implements Cache<T> { @override getByKey(String key) { return null; } @override void setByKey(String key, T value) { print("我是文件缓存,把key=${key} value=${value}的数据写入了文件"); } }
class MemoryCache<T> implements Cache<T> { @override getByKey(String key) { return null; } @override void setByKey(String key, T value) { print("我是内存缓存,把key=${key} value=${value}的数据写入了内存"); } }
void main() { FileCache f = new FileCache<Map>(); m.setByKey('index', {"name": "张三", "age": 20}); MemoryCache m = new MemoryCache<String>(); m.setByKey('index', '首页数据'); }
|
库
Dart的库分为三种:
自定义的库:import 'lib/xxx.dart';
系统内置库:
import 'dart:math';
import 'dart:io';
import 'dart:convert';
Pub包管理系统中的库
系统内置库
示例1:数学库
1 2 3 4 5 6
| import 'dart:math';
main() { print(min(1, 2)); print(max(1, 2)); }
|
示例2:ajax
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import 'dart:io'; import 'dart:convert';
void main() async { var res = await getDataFromZhihuAPI(); print(res); }
getDataFromZhihuAPI() async { var httpClient = new HttpClient(); var uri = new Uri.http('news-at.zhihu.com', '/api/3/stories/latest'); var req = await httpClient.getUrl(uri); var res = await req.close(); return await res.transform(utf8.decoder).join(); }
|
Pub库
pub包管理系统中的库:
从下面网址找到要用的库:
https://pub.dev/packages
https://pub.flutter-io.cn/packages
https://pub.dartlang.org/flutter/
创建一个pubspec.yaml文件,内容如下:
1 2 3 4 5 6
| name: xxx description: A new flutter module project. dependencies:
|
配置dependencies
在当前目录运行put get
,获取远程库
参考文档使用库
库的特性
库的重命名:
as可以实现库的重命名,避免名称冲突
部分引入:使用 show/hide 关键字
库里面定义了多个函数,我只引入一个:import 'lib/myMath.dart' show getAge
如果只是某个函数不想引入,则使用hide
延迟加载
即懒加载,作用是可以减少APP的启动时间
它使用deferred as
关键字指定
1 2 3 4 5 6 7 8
| import 'package:deferred/hello.dart deferred as hello';
greet() async { await hello.loadLibrary(); hello.printGreeting(); }
|
包分片
有时一个库太大了,使用part
关键字把大库分成小的库
在一个大库中:
1 2 3 4 5 6 7
|
part 'part1.dart'; part 'part2.dart'; part 'part3.dart';
|
dart新特性
可空类型?
文档:https://dart.cn/null-safety
简而言之,为了避免访问到空对象的值或方法而引起报错,dart在编译时就进行检查,避免出现空值
若你想让变量可以为 null
,只需要在类型声明后加上 ?
,类型?
表示可空类型
1
| >int? aNullableInt = null;
|
类型断言!
一般配合try-catch使用
1 2 3 4 5 6 7
| String? str = 'This is str'; str = null; try { print(str!.length); } catch (e) { print('字符串为空'); }
|
late
late
关键字的作用是延迟初始化,告诉编译器:这个值不为空,我一会儿再初始化
required
指定必须传入的命名参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Person { String name; int age; Person({required this.name, required this.age}); String getName() { return '${this.name} --- ${this.age}'; } }
void main(args) { Person p = new Person( name: '张三', age: 20 ); print(p.getName()); }
|
flutter
以后学~