Dart
# 1. 安装与运行
- Dart SDK 可以用来开发 WEB、命令行程序、服务端程序等。
- 如果是开发移动端APP,直接下载 Flutter 就行,不用在下载 Dart SDK。
安装:
brew tap dart-lang/dart brew install dart
指令运行:
dart ***.dart
# 2. 变量/常量申明/变量命名规则
# 2.1 Dart变量
创建一个变量并进行初始化:
- dart是一个强大的脚本类语言,可以不预先定义变量类型 ,自动会类型推倒
- dart中定义变量可以通过
var
关键字可以通过类型来申明变量var name = 'Bob';
变量仅存储对象引用,这里的变量是 name 存储了一个 String 类型的对象引用。 “Bob” 是这个 String 类型对象的值。
name 变量的类型被推断为 String 。 但是也可以通过指定类型的方式,来改变变量类型。 如果对象不限定为单个类型,可以指定为 对象类型 或 动态类型
dynamic name = 'Bob';
显式声明可以推断出的类型:
String name = 'Bob';
提示
注意: var 后不支持写类型,写了类型 不要var
, 两者都写 报错
# 2.2 默认值
未初始化的变量默认值是 null
。即使变量是数字 类型默认值也是 null
,因为在 Dart
中一切都是对象,数字类型 也不例外。
# 2.3 Final 和 Const
常量可以使用Final
和 Const
。
Const
值不变 一开始就得赋值Final
可以开始不赋值,只能赋一次; 而Final
不仅有Const
的编译时常量的特性,最重要的它是运行时常量,并且final是惰性初始化,即在运行时第一次使用前才初始化
区别
实例变量可以是 final
类型但不能是 const
类型。 必须在构造函数体执行之前初始化 final
实例变量 —— 在变量声明中,参数构造函数中或构造函数的初始化列表中进行初始化。
const a = new DateTime.now(); // 会报错
print(a);
# 2.4 命名规则
变量名称必须由数字、字母、下划线和美元符($)组成。
注意:标识符开头不能是数字
标识符不能是保留字和关键字。
变量的名字是区分大小写的如: age和Age是不同的变量。在实际的运用中,也建议,不要用一个单词大小写区分两个变量。
标识符(变量名称)一定要见名思意 :变量名称建议用名词,方法名称建议用动词
# 3. 数据类型
Dart中支持以下数据类型:
# 3.1 常用的数据类型:
- Numbers(数值): int double
// int 必须是整型
int a = 123;
// double 既可以是整型 也可是浮点型
double b = 23.5;
- Strings(字符串): String
var str1='this is str1';
String str1='this is str1';
- Booleans(布尔): bool
- List(数组): 在Dart中,数组是列表对象,所以大多数人只是称它们为列表
// 第一种定义List的方式
var l1=["张三",20,true];
// 第二种定义List的方式 指定类型
var l2=<String>["张三","李四"];
// 第三种定义List的方式 增加数据 ,通过[]创建的集合它的容量可以变化
var l4 = [];
l4.add("张三");
l4.add("李四");
// 第四种定义List的方式 创建一个固定长度的集合
var l4 = List.filled(2, "");
- Map(字典): 通常来说,Map 是一个键值对相关的对象。 键和值可以是任何类型的对象。每个 键 只出现一次, 而一个值则- 现多次
// 第一种定义 Map的方式
var person = {
"name": "张三",
"age": 20,
"work": ["程序员", "送外卖"]
};
// 第二种定义 Map的方式
var p=new Map();
p["name"]="李四";
p["age"]=22;
p["work"]=["程序员","送外卖"];
print(p);
# 3.2 不常用的类型:
- Runes:UTF-32编码的字符串。它可以通过文字转换成符号表情或者代表特定的文字。
main() {
var clapping = '\u{1f44f}';
print(clapping);
print(clapping.codeUnits);
print(clapping.runes.toList());
Runes input = new Runes(
'\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}');
print(new String.fromCharCodes(input));
}
- Symbols
Symbol对象表示在Dart程序中声明的运算符或标识符, 在 Dart 中符号用 #
开头来表示
main() {
#radix; // Symbol("radix")
var obj = {
'a': '123',
[#radix]: 14
};
print(obj); // {a: 123, [Symbol("radix")]: 14}
}
- 使用连续三个单引号或者三个双引号实现多行字符串对象的创建:
void main(List<String> args) {
var a = 'str';
a = '''a
aa''';
print(a);
}
# 3.3 判断类型is
var str = 123;
if (str is String) {
print('是string类型');
} else if (str is int) {
print('int');
} else {
print('其他类型');
}
# 4. 运算符/条件表达式/类型转换
# 4.1 算术运算符
+ - * / ~/ (取整) %(取余)
# 4.2 关系运算符
== != > < >= <=
# 4.3 逻辑运算符
! && ||
# 4.4 赋值运算符
基础赋值运算符 = ??=: b??=23; 表示如果b为空的话把 23赋值给b 复合赋值运算符 += -= *= /= %= ~/=
# 4.5 条件表达式
if else switch case 三目运算符: bool flag=false; String c=flag?'我是true':'我是false'; ??运算符: var a; var b= a ?? 10;
# 4.6 类型转换
Number与String类型之间的转换
- Number类型转换成String类型 xxx.toString()
- String类型转成Number类型 int.parse(xxx)
其他类型转换成Booleans类型
- isEmpty:判断字符串是否为空
# 5. 循环语句
- for循环
for(int i=1;i<=10;i++){
print(i);
}
- while
while(表达式/循环条件){
}
- do ... while
do{
语句/循环体
}while(表达式/循环条件);
注意
- 最后的分号不要忘记
- 循环条件中使用的变量需要经过初始化
- 循环体中,应有结束循环的条件,否则会造成死循环。
break语句功能:
- 在switch语句中使流程跳出switch结构。
- 在循环语句中使流程跳出当前循环,遇到break 循环终止,后面代码也不会执行
强调:
- 如果在循环中已经执行了break语句,就不会执行循环体中位于break后的语句。
- 在多层循环中,一个break语句只能向外跳出一层
break可以用在switch case中 也可以用在 for 循环和 while循环中
continue语句的功能:
- 【注】只能在循环语句中使用,使本次循环结束,即跳过循环体重下面尚未执行的语句,接着进行下次的是否执行循环的判断。
- continue可以用在for循环以及 while循环中,但是不建议用在while循环中,不小心容易死循环
# 6. 集合类型
# 6.1 List
- 常用属性:
length 长度 reversed 翻转 isEmpty 是否为空 isNotEmpty 是否不为空
- 常用方法:
add 增加 addAll 拼接数组 indexOf 查找 传入具体值 remove 删除 传入具体值 removeAt 删除 传入索引值 fillRange 修改
insert(index,value); 指定位置插入
insertAll(index,list) 指定位置插入List toList() 其他类型转换成List
join() List转换成字符串 split() 字符串转化成List forEach
map where any every
# 6.2 Set
- Set是没有顺序且不能重复的集合,所以不能通过索引去获取值
var s = new Set();
s.add('香蕉');
s.add('苹果');
s.add('苹果');
# 6.3 Map
映射(Map)是无序的键值对:
常用属性:
keys 获取所有的key值 values 获取所有的value值 isEmpty 是否为空 isNotEmpty 是否不为空
常用方法:
remove(key) 删除指定key的数据 addAll({...}) 合并映射 给映射内增加属性 containsValue 查看映射内的值 返回true/false forEach map where any every
- where 用法: 筛选符合条件的data
final numbers = <int>[1, 2, 3, 5, 6, 7];
var result = numbers.where((x) => x < 5); // (1, 2, 3)
result = numbers.where((x) => x > 5); // (6, 7)
result = numbers.where((x) => x.isEven); // (2, 6)
- any: 只要集合里面有满足条件的就返回true
List myList=[1,3,4,5,7,8,9];
bool f = myList.any((value){
return value>5;
});
- every: 每一个都满足条件返回true 否则返回false
List myList=[1,3,4,5,7,8,9];
bool f = myList.every((value){
return value>5;
});
# 7. 函数
# 7.1 内置方法/函数:print();
# 7.2 自定义方法
返回类型 方法名称(参数1,参数2,...){ 方法体 return 返回值; }
int getNum(){
var myNum = 123;
return myNum;
}
String printUserInfo(){
return 'this is str';
}
# 7.3 带参数的方法
String printUserInfo(String username, int age) {
//行参
return "姓名:$username---年龄:$age";
}
# 7.4 带可选参数的方法,[]
String printUserInfo(String username,[int age=0]){ //行参
}
# 8. 类
# 8.1 类
面向对象编程(OOP)的三个基本特征是:封装、继承、多态
封装:封装是对象和类概念的主要特性。封装,把客观事物封装成抽象的类,并且把自己的部分属性和方法提供给其他对象调用, 而一部分属性和方法则隐藏。
继承:面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
多态:允许将子类类型的指针赋值给父类类型的指针, 同一个函数调用会有不同的执行效果 。
Dart所有的东西都是对象,所有的对象都继承自Object类。
Dart是一门使用类和单继承的面向对象语言,所有的对象都是类的实例,并且所有的类都是Object的子类
一个类通常由属性和方法组成。
class Person{
String name = '张三';
int age = 23;
void getInfo() {
print('${this.name}---${this.age}');
}
}
void main() {
Person p1 = new Person();
print(p1.age);
p1.getInfo();
}
- 最新版本的dart中需要初始化不可为null的实例字段,如果不初始化的话需要在属性前面加上late
class Person{
late String name;
late int age;
//默认构造函数的简写
Person(this.name,this.age);
void printInfo(){
print("${this.name}----${this.age}");
}
}
- 把类单独抽离成一个模块
- lib/Person.dart
// 注意:最新版本的dart中需要初始化不可为null的实例字段,如果不初始化的话需要在属性前面加上late
class Person{
late String name;
late int age;
//默认构造函数的简写
Person(this.name,this.age);
Person.now(){
print('我是命名构造函数');
}
Person.setInfo(String name,int age){
this.name=name;
this.age=age;
}
void printInfo(){
print("${this.name}----${this.age}");
}
}
import 'lib/Person.dart';
void main(){
Person p1=new Person.setInfo('李四1',30);
p1.printInfo();
}
- dart中的私有属性和私有方法
class Animal{
late String _name; //私有属性
late int age;
//默认构造函数的简写
Animal(this._name,this.age);
void printInfo(){
print("${this._name}----${this.age}");
}
String getName(){
return this._name;
}
void _run(){
print('这是一个私有方法');
}
execRun(){
this._run(); //类里面方法的相互调用
}
}
# 8.2 getter/setter
- getter 和 setter修饰符的用法
class Rect{
late num height;
late num width;
Rect(this.height, this.width);
// 可以在构造函数体运行之前初始化实例变量
// Rect():height=2,width=10
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);
}
# 8.3 静态成员
- 使用
static
关键字实现类级别的变量和函数 - 静态方法不能访问非静态成员,非静态成员可以访问静态成员
class Person {
static String name = '张三';
int age = 20;
static void show() {
print(name);
}
void printInfo() {
/*非静态方法可以访问静态成员以及非静态成员*/
// print(name); //访问静态属性
// print(this.age); //访问非静态属性
show(); //调用静态方法
}
static void printUserInfo() {
//静态方法
print(name); //静态属性
show(); //静态方法
//print(this.age); //静态方法没法访问非静态的属性
// this.printInfo(); //静态方法没法访问非静态的方法
// printInfo();
}
}
main() {
// print(Person.name);
// Person.show();
// Person p=new Person();
// p.printInfo();
Person.printUserInfo();
}
# 8.4 对象操作符
- [?] 条件运算符
- [as] 类型转换
- [is] 类型判断
- [..] 级联操作
class Person {
String name;
num age;
Person(this.name, this.age);
void printInfo() {
print("${this.name}---${this.age}");
}
}
main() {
Person p=new Person('张三', 20);
if(p is Person){
p.name="李四";
}
p.printInfo();
print(p is Object);
}
# 8.5 类的继承:
- 子类使用extends关键词来继承父类
- 子类会继承父类里面可见的属性和方法 但是不会继承构造函数
- 子类能复写父类的方法 getter和setter
class Person {
String name='张三';
num age=20;
void printInfo() {
print("${this.name}---${this.age}");
}
}
class Web extends Person{
late String sex;
Web(String name, num age,String sex) : super(name, age){
this.sex=sex;
}
run(){
print("${this.name}---${this.age}--${this.sex}");
}
work(){
print("${this.name}在工作...");
}
}
# 8.6 类的继承 覆写父类的方法
class Web extends Person{
Web(String name, num age) : super(name, age);
run(){
print('run');
super.work(); //子类调用父类的方法
}
//覆写父类的方法
//可以写也可以不写 建议在覆写父类方法的时候加上 @override
void printInfo(){
print("姓名:${this.name}---年龄:${this.age}");
}
work(){
print("${this.name}的工作是写代码");
}
}
# 8.7 抽象类
- Dart抽象类主要用于定义标准,子类可以继承抽象类,也可以实现抽象类接口。
- 抽象类通过
abstract
关键字来定义 - Dart中的抽象方法不能用abstract声明,Dart中没有方法体的方法我们称为抽象方法。
- 如果子类继承抽象类必须得实现里面的抽象方法
- 如果把抽象类当做接口实现的话必须得实现抽象类里面定义的所有属性和方法。
- 抽象类不能被实例化,只有继承它的子类可以
abstract class Animal {
eat();
run();
printInfo() {
print('abstract 方法');
}
}
class Dog extends Animal{
eat() {
print('小狗在吃骨头');
}
run() {
print('小狗在跑');
}
}
main() {
Dog d = new Dog();
d.eat();
d.printInfo();
}
# 8.8 多态
- 允许将子类类型的指针赋值给父类类型的指针, 同一个函数调用会有不同的执行效果 。
- 子类的实例赋值给父类的引用。
- 多态就是父类定义一个方法不去实现,让继承他的子类去实现,每个子类有不同的表现。
abstract class Animal {
eat();
}
class Dog extends Animal{
eat() {
print('小狗狗在吃骨头');
}
}
class Cat extends Animal{
eat() {
print('小猫猫在吃骨头');
}
}
main() {
Dog d = new Dog();
d.eat();
Cat c = new Cat();
c.eat();
}
# 8.9 接口
- dart的接口没有
interface
关键字定义接口,而是普通类或抽象类都可以作为接口被实现。 - 同样使用
implements
关键字进行实现。 - 但是dart的接口有点奇怪,如果实现的类是普通类,会将普通类和抽象中的属性的方法全部需要覆写一遍。
- 而因为抽象类可以定义抽象方法,普通类不可以,所以一般如果要实现像Java接口那样的方式,一般会使用抽象类。
- 建议使用抽象类定义接口
abstract class Db{
late String uri;
add(String data);
save();
delete();
}
class Mysql implements Db{
String uri;
Mysql(this.uri);
add(String data) {
print('mysql add' + data);
}
save() {
print('save----');
}
delete() {
print('delete--');
}
}
class MsSql implements Db{
late String uri;
add(String data) {
print('mssql add' + data);
}
save() {
print('save----');
}
delete() {
print('delete--');
}
}
void main(List<String> args) {
Mysql sql = new Mysql('sss');
sql.add('12345');
MsSql ms = new MsSql();
ms.add('MsSql');
}
# 8.10 mixins
- 作为mixins的类只能继承自Object,不能继承其他类
- 作为mixins的类不能有构造函数
- 一个类可以mixins多个mixins类
- mixins绝不是继承,也不是接口,而是一种全新的特性
class A {
String info = "this is A";
void printA() {
print('A');
}
}
class B {
void printB() {
print('B');
}
}
class C with A, B {
}
void main() {
var c = new C();
c.printA();
c.printB();
print(c.info);
}
# 8.11 泛型
- 泛型就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持(类型校验)
abstract class Cache<T>{
getByKey(String key);
void setByKey(String key, T value);
}
class FlieCache<T> implements Cache<T>{
getByKey(String key) {
return null;
}
void setByKey(String key, T value) {
print("我是文件缓存 把key=${key} value=${value}的数据写入到了文件中");
}
}
class MemoryCache<T> implements Cache<T>{
getByKey(String key) {
return null;
}
void setByKey(String key, T value) {
print("我是内存缓存 把key=${key} value=${value} -写入到了内存中");
}
}
void main(){
// MemoryCache m=new MemoryCache<String>();
// m.setByKey('index', '首页数据');
MemoryCache m=new MemoryCache<Map>();
m.setByKey('index', {"name":"张三","age":20});
}
# 9. 库
自定义的库
import 'lib/xx.dart'
系统内置库
import 'dart:math'; import 'dart:io';
- Pub包管理系统中的库
https://pub.dev/packages https://pub.flutter-io.cn/packages https://pub.dartlang.org/flutt 1、需要在项目根目录新建一个pubspec.yaml 2、在pubspec.yaml文件 然后配置名称 、描述、依赖等信息 3、然后运行 pub get 获取包下载到本地
4、项目中引入库 import 'package:http/http.dart' as http; 看文档使用
- 导入系统内置库实现数据请求
import 'dart:convert';
import 'dart:io';
void main() async {
var result = await getData();
print(result);
}
getData() async{
var httpClient = new HttpClient();
var uri = new Uri.http('news-at.zhihu.com','/api/3/stories/latest');
var request = await httpClient.getUrl(uri);
var response = await request.close();
return await response.transform(utf8.decoder).join();
}
- pub包管理系统
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
void main(List<String> arguments) async {
// This example uses the Google Books API to search for books about http.
// https://developers.google.com/books/docs/overview
var url =
Uri.https('www.googleapis.com', '/books/v1/volumes', {'q': '{http}'});
// Await the http get response, then decode the json-formatted response.
var response = await http.get(url);
if (response.statusCode == 200) {
var jsonResponse =
convert.jsonDecode(response.body) as Map<String, dynamic>;
var itemCount = jsonResponse['totalItems'];
print('Number of books about http: $itemCount.');
} else {
print('Request failed with status: ${response.statusCode}.');
}
}
部分导入: 如果只需要导入库的一部分,有两种模式:
- 模式一:只导入需要的部分,使用show关键字,如下例子所示:
import 'package:lib1/lib1.dart' show foo;
- 模式二:隐藏不需要的部分,使用hide关键字,如下例子所示:
import 'package:lib2/lib2.dart' hide foo;
- 模式一:只导入需要的部分,使用show关键字,如下例子所示:
延迟加载 也称为懒加载,可以在需要的时候再进行加载。 懒加载的最大好处是可以减少APP的启动时间。
懒加载使用deferred as关键字来指定,如下例子所示:
import 'package:deferred/hello.dart' deferred as hello;当需要使用的时候,需要使用loadLibrary()方法来加载:
greet() async { await hello.loadLibrary(); hello.printGreeting(); }
# 10. 新特性
Dart 2.13之后的一些新特性Null safety 、late 关键字、空类型声明符?、非空断言!、required 关键字
null safety:可以帮助开发者避免一些日常开发中很难被发现的错误,并且额外的好处是可以改善性能。 Flutter2.2.0(2021年5月19日发布) 之后的版本都要求使用null safety。
- ? 可空类型
- ! 类型断言
late 关键字主要用于延迟初始化
required关键词
- 最开始 @required 是注解
- 现在作为内置修饰符
- 主要用于允许根据需要标记任何命名参数(函数或类),使得它们不为空。因为可选参数中必须有个 required 参数或者该参数有个默认值。