Dart语法
1.Dart介绍
Dart是由谷歌开发的计算机编程语言,它可以被用于web、服务器、移动应用 和物联网等领域的开发。Dart诞生于2011年,号称要取代JavaScript。但是过去的几年中一直不温不火。直到Flutter的出现现在被人们重新重视。要学Flutter的话我们必须首先得会Dart。
官网:https://dart.dev/
2.Dart环境搭建
要在我们本地开发Dart程序的话首先需要安装Dart Sdk. 官方文档:https://dart.dev/get-dart
windows(推荐):http://www.gekorm.com/dart-windows/
mac:如果mac电脑没有安装brew这个工具首先第一步需要安装它: https://brew.sh/
brew tap dart-lang/dart
brew install dart
3.Dart 开发工具
Dart的开发工具有很多: IntelliJ IDEA 、 WebStorm、 Atom、Vscode等。 这里我们主要给大家讲解的是如果在Vscode中配置Dart。
1、找到vscode插件安装dart
2、找到vscode插件安装code runner Code Runner 可以运行我们的文件
4.Dart 变量、常量、 命名规则
4.1 Dart 变量
dart是一个强大的脚本类语言,可以不预先定义变量类型 ,自动会类型推倒。dart中定义变量可以通过var关键字可以通过类型来申明变量。如:
var str='this is var';
String str='this is var';
int str=123;
注意: var 后就不要写类型 , 写了类型 不要var 两者都写 var a int = 5; 报错
4.2 Dart 常量
final 和 const修饰符。
const值不变 一开始就得赋值。
final 可以开始不赋值 只能赋一次 ; 而final不仅有const的编译时常量的特性,最重要的它是运行时常量,并且final是惰性初始化,即在运行时第一次使用前才初始化。
永远不改量的量,请使用final或const修饰它,而不是使用var或其他变量类型。
final name = 'Bob'; // Without a type annotation
final String nickname = 'Bobby';
const bar = 1000000; // Unit of pressure (dynes/cm2)
const double atm = 1.01325 * bar; // Standard atmosphere
4.3 Dart的命名规则
1、变量名称必须由数字、字母、下划线和美元符($)组成。
2.注意:标识符开头不能是数字。
3.标识符不能是保留字和关键字。
4.变量的名字是区分大小写的如: age和Age是不同的变量。在实际的运用中,也建议,不要用一个单词大小写区分两个变量。
5、标识符(变量名称)一定要见名思意 :变量名称建议用名词,方法名称建议用动词
5.Dart 数据类型
5.1 Dart中支持以下数据类型
5.1.1 常用数据类型
Numbers(数值):num( int、double)
num a = 123;
num b = 1.23;
//1、int 必须是整型
int a=123;
a=45;
print(a);
//2、double 既可以是整型 也可是浮点型
double b=23.5;
b=24;
print(b);
//3、运算符
// + - * / %
var c=a+b;
print(c);
Strings(字符串):String
String str1='你好';
String str2='Dart';
// print("$str1 $str2");
print(str1 + str2);
print(str1 +" "+ str2);
String str3 = str1 *3;
Booleans(布尔):bool
//1、bool
bool flag1=true;
print(flag1);
bool flag2=false;
print(flag2);
//2、条件判断语句
var flag=true;
if(flag){
print('真');
}else{
print('假');
}
var a=123;
var b='123';
if(a==b){
print('a=b');
}else{
print('a!=b');
}
List(数组): 在Dart中,数组是列表对象,所以大多数人只是称它们为列表
//1、第一种定义List的方式
var l1=["张三",20,true];
print(l1); //[张三, 20, true]
print(l1.length); //3
print(l1[0]); //张三
print(l1[1]); //20
//2、第二种定义List的方式 指定类型
var l2=<String>["张三","李四"];
print(l2);
var l3 = <int>[12, 30];
print(l3);
//3、第三种定义List的方式 增加数据 ,通过[]创建的集合它的容量可以变化
var l4 = [];
print(l4);
print(l4.length);
l4.add("张三");
l4.add("李四");
l4.add(20);
print(l4);
print(l4.length);
var l5 = ["张三", 20, true];
l5.add("李四");
l5.add("zhaosi");
print(l5);
//4、第四种定义List的方式
var l6=new List(); //在新版本的dart里面没法使用这个方法了
var l6=List.filled(2, ""); //创建一个固定长度的集合
print(l6);
print(l6[0]);
l6[0]="张三"; //修改集合的内容
l6[1]="李四";
print(l6); //[张三, 李四]
l6.add("王五"); //错误写法 通过List.filled创建的集合长度是固定 没法增加数据
//通过List.filled创建的集合长度是固定
var l6=List.filled(2, "");
print(l6.length);
l6.length=0; //修改集合的长度 报错
var l7=<String>["张三","李四"];
print(l7.length); //2
l7.length=0; //可以改变的
print(l7); //[]
var l3 = l2.map((item) => item).toList()
String str = '123a';
var list = [];
list.add(str);
str = 'ccc';
list.add(str);
print(list)
Maps(字典):通常来说,Map 是一个键值对相关的对象。 键和值可以是任何类型的对象。每个 键 只出现一次, 而一个值则可以出现多次
//第一种定义 Maps的方式
Map<String, dynamic> person={
"name":"张三",
"age":20,
"work":["程序员","送外卖"]
};
print(person);
print(person["name"]);
print(person["age"]);
print(person["work"]);
//第二种定义 Maps的方式
var p=new Map();
p["name"]="李四";
p["age"]=22;
p["work"]=["程序员","送外卖"];
print(p);
print(p["age"]);
5.1.2 项目中用不到的数据类型 (用不到)
Runes :Rune是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程序中声明的运算符或标识符。您可能永远不需要使用符号,但它们对于按名称引用标识符的API非常有用,因为缩小会更改标识符名称而不会更改标识符符号。要获取标识符的符号,请使用符号文字,它只是#后跟标识符:在 Dart 中符号用 # 开头来表示,入门阶段不需要了解这东西,可能永远也用不上。 http://dart.goodev.org/guides/libraries/library-tour#dartmirrors—reflection
5.2 Dart判断数据类型
is 关键词来判断类型。
var str=123;
if(str is String){
print('是string类型');
}else if(str is int){
print('int');
}else{
print('其他类型');
6.Dart 运算符、条件判断、类型转换
6.1 Dart运算符
6.1.1 算术运算符
+ - * / ~/ (取整) %(取余)
6.1.2 关系运算符
== != > < >= <=
6.1.3 逻辑运算符
! && ||
6.1.4 赋值运算符
基础赋值运算符 = ??=
复合赋值运算符 += -= *= /= %= ~/=
6.1.5 三目运算符
?:
6.2 条件表达式
if else switch case
6.3 类型转换
1、Number与String类型之间的转换
//Number类型转换成String类型 toString()
//String类型转成Number类型 int.parse()
String str='123';
var myNum=int.parse(str);
print(myNum is int);
String str='123.1';
var myNum=double.parse(str);
print(myNum is double);
String price='12';
var myNum=double.parse(price);
print(myNum);
print(myNum is double);
//报错
String price='';
var myNum=double.parse(price);
print(myNum);
print(myNum is double);
try ... catch
String price='';
try{
var myNum=double.parse(price);
print(myNum);
}catch(err){
print(0);
}
var myNum=12;
var str=myNum.toString();
print(str is String);
2、其他类型转换成Booleans类型
//isEmpty:判断字符串是否为空
var str='';
if(str.isEmpty){
print('str空');
}else{
print('str不为空');
}
var myNum=123;
if(myNum==0){
print('0');
}else{
print('非0');
}
var myNum;
if(myNum==0){
print('0');
}else{
print('非0');
}
var myNum;
if(myNum==null){
print('空');
}else{
print('非空');
}
var myNum=0/0;
print(myNum);
if(myNum.isNaN){
print('NaN');
}
7.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 |
List myList=['香蕉','苹果','西瓜'];
print(myList[1]);
var list=new List(); //新版本没法使用
list.add('111');
list.add('222');
print(list);
//List里面的属性:
List myList=['香蕉','苹果','西瓜'];
print(myList.length);
print(myList.isEmpty);
print(myList.isNotEmpty);
print(myList.reversed); //对列表倒序排序
var newMyList=myList.reversed.toList();
print(newMyList);
//List里面的方法:
List myList=['香蕉','苹果','西瓜'];
myList.add('桃子'); //增加数据 增加一个
myList.addAll(['桃子','葡萄']); //拼接数组
print(myList);
print(myList.indexOf('苹x果')); //indexOf查找数据 查找不到返回-1 查找到返回索引值
myList.remove('西瓜');
myList.removeAt(1);
print(myList);
List myList=['香蕉','苹果','西瓜'];
myList.fillRange(1, 2,'aaa'); //修改
myList.fillRange(1, 3,'aaa');
myList.insert(1,'aaa'); //插入 一个
myList.insertAll(1, ['aaa','bbb']); //插入 多个
print(myList);
List myList=['香蕉','苹果','西瓜'];
var str=myList.join('-'); //list转换成字符串
print(str);
print(str is String); //true
var str='香蕉-苹果-西瓜';
var list=str.split('-');
print(list);
print(list is List);
7.2 set
用它最主要的功能就是去除数组重复内容。Set是没有顺序且不能重复的集合,所以不能通过索引去获取值
var s=new Set();
s.add('香蕉');
s.add('苹果');
s.add('苹果');
print(s); //{香蕉, 苹果}
print(s.toList());
List myList=['香蕉','苹果','西瓜','香蕉','苹果','香蕉','苹果'];
var s=new Set();
s.addAll(myList);
print(s);
print(s.toList());
7.3 map
映射(Maps)是无序的键值对
常用属性:
属性名称 | 说明 |
---|---|
keys | 获取所有的key值 |
values | 获取所有的value值 |
isEmpty | 是否为空 |
isNotEmpty | 是否不为空 |
常用方法:
方法名称 | 说明 |
---|---|
remove(key) | 删除指定key的数据 |
addAll({…}) | 合并映射 给映射内增加属性 |
containsValue | 查看映射内的值 返回true/false |
Map person={
"name":"张三",
"age":20
};
var m=new Map();
m["name"]="李四";
print(person);
print(m);
//常用属性:
Map person={
"name":"张三",
"age":20,
"sex":"男"
};
print(person.keys.toList());
print(person.values.toList());
print(person.isEmpty);
print(person.isNotEmpty);
//常用方法:
Map person={
"name":"张三",
"age":20,
"sex":"男"
};
person.addAll({
"work":['敲代码','送外卖'],
"height":160
});
print(person);
person.remove("sex");
print(person);
print(person.containsValue('张三'));
7.4 forEach、map、where、any、every
List myList=['香蕉','苹果','西瓜'];
for(var i=0;i<myList.length;i++){
print(myList[i]);
}
for(var item in myList){
print(item);
}
myList.forEach((value){
print("$value");
});
List myList=[1,3,4];
List newList=new List();
for(var i=0;i<myList.length;i++){
newList.add(myList[i]*2);
}
print(newList);
List myList=[1,3,4];
var newList=myList.map((value){
return value*2;
});
print(newList.toList());
List myList=[1,3,4,5,7,8,9];
var newList=myList.where((value){
return value>5;
});
print(newList.toList());
List myList=[1,3,4,5,7,8,9];
var f=myList.any((value){ //只要集合里面有满足条件的就返回true
return value>5;
});
print(f);
List myList=[1,3,4,5,7,8,9];
var f=myList.every((value){ //每一个都满足条件返回true 否则返回false
return value>5;
});
print(f);
//set
var s=new Set();
s.addAll([1,222,333]);
s.forEach((value)=>print(value));
//map
Map person={
"name":"张三",
"age":20
};
person.forEach((key,value){
print("$key---$value");
});
8.方法传参 、默认参数、可选参数、命名参数 、方法作为参数
//1、定义一个方法 求1到这个数的所有数的和 60 1+2+3+。。。+60
int sumNum(int n){
var sum=0;
for(var i=1;i<=n;i++)
{
sum+=i;
}
return sum;
}
var n1=sumNum(5);
print(n1);
var n2=sumNum(100);
print(n2);
//2、定义一个方法然后打印用户信息
String printUserInfo(String username, int age) {
//行参
return "姓名:$username---年龄:$age";
}
print(printUserInfo('张三', 20)); //实参
//3、定义一个带可选参数的方法 ,最新的dart定义可选参数需要指定类型默认值
String printUserInfo(String username,[int age=0]){ //行参
if(age!=0){
return "姓名:$username---年龄:$age";
}
return "姓名:$username---年龄保密";
}
print(printUserInfo('张三',21)); //实参
print(printUserInfo('张三'));
//4、定义一个带默认参数的方法
String printUserInfo(String username,[String sex='男',int age=0]){ //行参
if(age!=0){
return "姓名:$username---性别:$sex--年龄:$age";
}
return "姓名:$username---性别:$sex--年龄保密";
}
print(printUserInfo('张三'));
print(printUserInfo('小李','女'));
print(printUserInfo('小李','女',30));
//5、定义一个命名参数的方法,最新的dart定义命名参数需要指定类型默认值
String printUserInfo(String username, {int age = 0, String sex = '男'}) {//行参
if (age != 0) {
return "姓名:$username---性别:$sex--年龄:$age";
}
return "姓名:$username---性别:$sex--年龄保密";
}
print(printUserInfo('张三', age: 20, sex: '未知'));
//6、实现一个 把方法当做参数的方法
var fn=(){
print('我是一个匿名方法');
};
fn();
fn1方法
fn1() {
print('fn1');
}
fn2方法
fn2(fn) {
fn();
}
//调用fn2这个方法 把fn1这个方法当做参数传入
fn2(fn1);
String printUserInfo(String username, {int age = 0, String sex = '男'}) {//行参
if (age != 0) {
return "姓名:$username---性别:$sex--年龄:$age";
}
return "姓名:$username---性别:$sex--年龄保密";
}
printUserInfo('张三', age: 20, sex: '未知')
9.箭头函数、函数的相互调用
/*需求:使用forEach打印下面List里面的数据*/
List list=['苹果','香蕉','西瓜'];
list.forEach((value){
print(value);
});
list.forEach((value)=>print(value));
//注意和方法的区别: 箭头函数内只能写一条语句,并且语句后面没有分号(;)
list.forEach((value)=>{
print(value)
});
/*需求:修改下面List里面的数据,让数组中大于2的值乘以2*/
List list=[4,1,2,3,4];
var newList=list.map((value){
if(value>2){
return value*2;
}
return value;
});
print(newList.toList());
var newList=list.map((value)=>value>2?value*2:value);
print(newList.toList());
/*
需求: 1、定义一个方法isEvenNumber来判断一个数是否是偶数
2、定义一个方法打印1-n以内的所有偶数
*/
//1、定义一个方法isEvenNumber来判断一个数是否是偶数
bool isEvenNumber(int n) {
if (n % 2 == 0) {
return true;
}
return false;
}
//2、定义一个方法打印1-n以内的所有偶数
printNum(int n) {
for (var i = 1; i <= n; i++) {
if (isEvenNumber(i)) {
print(i);
}
}
}
printNum(10);
10.匿名方法 自执行方法 方法的递归
//匿名方法
var printNum=(){
print(123);
};
printNum();
var printNum=(int n){
print(n+2);
};
printNum(12);
//自执行方法
((int n){
print(n);
print('我是自执行方法');
})(12);
//方法的递归
var sum = 1;
fn(int n) {
sum *= n;
if (n == 1) {
return;
}
fn(n - 1);
}
fn(5);
print(sum);
//通过方法的递归 求1-100的和
var sum=0;
fn(int n){
sum+=n;
if(n==0){
return;
}
fn(n-1);
}
fn(100);
print(sum);
11.闭包
1、全局变量特点: 全局变量常驻内存、全局变量污染全局
2、局部变量的特点: 不常驻内存会被垃圾机制回收、不会污染全局
想实现的功能: 1.常驻内存 ;2.不污染全局。产生了闭包,闭包可以解决这个问题…
闭包: 函数嵌套函数, 内部函数会调用外部函数的变量或参数, 变量或参数不会被系统回收(不会释放内存)
闭包的写法: 函数嵌套函数,并return 里面的函数,这样就形成了闭包。
/*全局变量*/
var a = 123;
void main() {
print(a);
fn(){
a++;
print(a);
}
fn();
fn();
fn();
//局部变量
printInfo() {
var myNum = 123;
myNum++;
print(myNum);
}
printInfo();
printInfo();
printInfo();
//闭包
fn() {
var a = 123; /*不会污染全局 常驻内存*/
return () {
a++;
print(a);
};
}
var b = fn();
b();
b();
b();
11.Dart面向对象的介绍以及Data内置对象
面向对象编程(OOP)的三个基本特征是:封装、继承、多态
封装:封装是对象和类概念的主要特性。封装,把客观事物封装成抽象的类,并且把自己的部分属性和方法提供给其他对象调用, 而一部分属性和方法则隐藏。
继承:面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
多态:允许将子类类型的指针赋值给父类类型的指针, 同一个函数调用会有不同的执行效果
Dart所有的东西都是对象,所有的对象都继承自Object类。
Dart是一门使用类和单继承的面向对象语言,所有的对象都是类的实例,并且所有的类都是Object的子类
一个类通常由属性和方法组成。
11.1创建义类使用类
Dart是一门使用类和单继承的面向对象语言,所有的对象都是类的实例,并且所有的类都是Object的子类
class Person{
String name="张三";
int age=23;
void getInfo(){
// print("$name----$age");
print("${this.name}----${this.age}");
}
void setInfo(int age){
this.age=age;
}
}
void main(){
//实例化
// var p1=new Person();
// print(p1.name);
// p1.getInfo();
Person p1=new Person();
// print(p1.name);
p1.setInfo(28);
p1.getInfo();
}
11.2自定义类的默认构造函数
// class Person{
// String name='张三';
// int age=20;
// //默认构造函数
// Person(){
// print('这是构造函数里面的内容 这个方法在实例化的时候触发');
// }
// void printInfo(){
// print("${this.name}----${this.age}");
// }
// }
//最新版本的dart中需要初始化不可为null的实例字段,如果不初始化的话需要在属性前面加上late
// class Person{
// late String name;
// late int age;
// //默认构造函数
// Person(String name,int age){
// this.name=name;
// this.age=age;
// }
// void printInfo(){
// print("${this.name}----${this.age}");
// }
// }
//最新版本的dart中需要初始化不可为null的实例字段,如果不初始化的话需要在属性前面加上late
class Person{
late String name;
late int age;
//默认构造函数的简写
Person(this.name,this.age);
void printInfo(){
print("${this.name}----${this.age}");
}
}
void main(){
Person p1=new Person('张三',20);
p1.printInfo();
Person p2=new Person('李四',25);
p2.printInfo();
}
11.3自定义类的命名构造函数
/*
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}");
}
}
void main() {
// var d=new DateTime.now(); //实例化DateTime调用它的命名构造函数
// print(d);
//Person p1=new Person('张三', 20); //默认实例化类的时候调用的是 默认构造函数
//Person p1=new Person.now(); //命名构造函数
Person p1 = new Person.setInfo('李四', 30);
p1.printInfo();
}
11.4私有方法和私有属性
/*
Dart和其他面向对象语言不一样,Data中没有 public private protected这些访问修饰符合
但是我们可以使用_把一个属性或者方法定义成私有。
*/
import 'lib/Animal.dart';
void main(){
Animal a=new Animal('小狗', 3);
print(a.getName());
a.execRun(); //间接的调用私有方法
}
11.5getter和setter修饰符的用法
// class Rect{
// int height;
// int width;
// getArea(){
// return this.height*this.width;
// }
// }
// class Rect{
// num height;
// num width;
// Rect(this.height,this.width);
// area(){
// return this.height*this.width;
// }
// }
// void main(){
// Rect r=new Rect(10,4);
// print("面积:${r.area()}");
// }
// class Rect{
// num height;
// num width;
// Rect(this.height,this.width);
// get area{
// return this.height*this.width;
// }
// }
// void main(){
// Rect r=new Rect(10,2);
// print("面积:${r.area}"); //注意调用直接通过访问属性的方式访问area
// }
class Rect{
late num height;
late num width;
Rect(this.height,this.width);
get area{
return this.height*this.width;
}
set areaHeight(value){
this.height=value;
}
}
void main(){
Rect r=new Rect(10,4);
// print("面积:${r.area()}");
r.areaHeight=6;
print(r.area);
}
11.6初始化列表
// Dart中我们也可以在构造函数体运行之前初始化实例变量
class Rect{
int height;
int width;
Rect():height=2,width=10{
print("${this.height}---${this.width}");
}
getArea(){
return this.height*this.width;
}
}
void main(){
Rect r=new Rect();
print(r.getArea());
}
11.7静态成员 静态方法
/*
Dart中的静态成员:
1、使用static 关键字来实现类级别的变量和函数
2、静态方法不能访问非静态成员,非静态方法可以访问静态成员
*/
// class Person {
// static String name = '张三';
// static void show() {
// print(name);
// }
// }
// main(){
// print(Person.name);
// Person.show();
// }
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();
}
11.8 对象操作符
/*
Dart中的对象操作符:
? 条件运算符 (了解) https://dart.dev/tools/diagnostic-messages#invalid_null_aware_operator
as 类型转换
is 类型判断
.. 级联操作 (连缀) (记住)
*/
class Person {
String name;
num age;
Person(this.name, this.age);
void printInfo() {
print("${this.name}---${this.age}");
}
}
main() {
// Person p;
// p?.printInfo(); //已被最新的dart废弃 了解
// Person p=new Person('张三', 20);
// p?.printInfo(); //已被最新的dart废弃 了解
Person p=new Person('张三', 20);
if(p is Person){
p.name="李四";
}
p.printInfo();
print(p is Object);
// var p1;
// p1='';
// p1=new Person('张三1', 20);
// p1.printInfo();
// (p1 as Person).printInfo();
// Person p1=new Person('张三1', 20);
// p1.printInfo();
// p1.name='张三222';
// p1.age=40;
// p1.printInfo();
Person p1 = new Person('张三1', 20);
p1.printInfo();
p1
..name = "李四"
..age = 30
..printInfo();
}
11.9 继承
/*
面向对象的三大特性:封装 、继承、多态
Dart中的类的继承:
1、子类使用extends关键词来继承父类
2、子类会继承父类里面可见的属性和方法 但是不会继承构造函数
3、子类能复写父类的方法 getter和setter
注意:最新版本的dart中需要初始化不可为null的实例字段,如果不初始化的话需要在属性前面加上late
*/
class Person {
String name;
num age;
Person(this.name, this.age);
Person.xxx(this.name, this.age);
void printInfo() {
print("${this.name}---${this.age}");
}
}
class Web extends Person {
late String sex;
Web(String name, num age, String sex) : super.xxx(name, age) {
this.sex = sex;
}
run() {
print("${this.name}---${this.age}--${this.sex}");
}
}
main() {
// Person p=new Person('李四',20);
// p.printInfo();
// Person p1=new Person('张三',20);
// p1.printInfo();
Web w = new Web('张三', 12, "男");
w.printInfo();
w.run();
}
11.10 覆写父类的方法和自类里面调用父类的方法
class Person {
String name;
num age;
Person(this.name,this.age);
void printInfo() {
print("${this.name}---${this.age}");
}
work(){
print("${this.name}在工作...");
}
}
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}");
}
}
main(){
Web w=new Web('李四',20);
// w.printInfo();
w.run();
}
11.11 抽象类
Dart中抽象类: Dart抽象类主要用于定义标准,子类可以继承抽象类,也可以实现抽象类接口。
1、抽象类通过abstract 关键字来定义
2、Dart中的抽象方法不能用abstract声明,Dart中没有方法体的方法我们称为抽象方法。
3、如果子类继承抽象类必须得实现里面的抽象方法
4、如果把抽象类当做接口实现的话必须得实现抽象类里面定义的所有属性和方法。
5、抽象类不能被实例化,只有继承它的子类可以
extends抽象类 和 implements的区别:
1、如果要复用抽象类里面的方法,并且要用抽象方法约束自类的话我们就用extends继承抽象类
2、如果只是把抽象类当做标准的话我们就用implements实现抽象类
/*案例:定义一个Animal 类要求它的子类必须包含eat方法
*/
abstract class Animal{
eat(); //抽象方法
run(); //抽象方法
printInfo(){
print('我是一个抽象类里面的普通方法');
}
}
class Dog extends Animal{
eat() {
print('小狗在吃骨头');
}
run() {
// TODO: implement run
print('小狗在跑');
}
}
class Cat extends Animal{
eat() {
// TODO: implement eat
print('小猫在吃老鼠');
}
run() {
// TODO: implement run
print('小猫在跑');
}
}
main(){
Dog d=new Dog();
d.eat();
d.printInfo();
Cat c=new Cat();
c.eat();
c.printInfo();
// Animal a=new Animal(); //抽象类没法直接被实例化
}
11.12 多态
Datr中的多态:
允许将子类类型的指针赋值给父类类型的指针, 同一个函数调用会有不同的执行效果 。
子类的实例赋值给父类的引用。
多态就是父类定义一个方法不去实现,让继承他的子类去实现,每个子类有不同的表现。
abstract class Animal{
eat(); //抽象方法
}
class Dog extends Animal{
eat() {
print('小狗在吃骨头');
}
run(){
print('run');
}
}
class Cat extends Animal{
eat() {
print('小猫在吃老鼠');
}
run(){
print('run');
}
}
main(){
// Dog d=new Dog();
// d.eat();
// d.run();
// Cat c=new Cat();
// c.eat();
Animal d=new Dog();
d.eat();
Animal c=new Cat();
c.eat();
}
11.13 接口
和Java一样,dart也有接口,但是和Java还是有区别的。
首先,dart的接口没有interface关键字定义接口,而是普通类或抽象类都可以作为接口被实现。
同样使用implements关键字进行实现。
但是dart的接口有点奇怪,如果实现的类是普通类,会将普通类和抽象中的属性的方法全部需要覆写一遍。
而因为抽象类可以定义抽象方法,普通类不可以,所以一般如果要实现像Java接口那样的方式,一般会使用抽象类。
建议使用抽象类定义接口。
/*
定义一个DB库 支持 mysql mssql mongodb
mysql mssql mongodb三个类里面都有同样的方法
*/
abstract class Db{ //当做接口 接口:就是约定 、规范
late String uri; //数据库的链接地址
add(String data);
save();
delete();
}
class Mysql implements Db{
String uri;
Mysql(this.uri);
add(data) {
// TODO: implement add
print('这是mysql的add方法'+data);
}
delete() {
// TODO: implement delete
return null;
}
save() {
// TODO: implement save
return null;
}
remove(){
}
}
class MsSql implements Db{
late String uri;
add(String data) {
print('这是mssql的add方法'+data);
}
delete() {
// TODO: implement delete
return null;
}
save() {
// TODO: implement save
return null;
}
}
main() {
Mysql mysql=new Mysql('xxxxxx');
mysql.add('1243214');
}
11.14 implements实现多个接口
/*
Dart中一个类实现多个接口:
*/
abstract class A{
late String name;
printA();
}
abstract class B{
printB();
}
class C implements A,B{
late String name;
printA() {
print('printA');
}
printB() {
// TODO: implement printB
return null;
}
}
void main(){
C c=new C();
c.printA();
}
11.15 mixins
mixins的中文意思是混入,就是在类中混入其他功能。在Dart中可以使用mixins实现类似多继承的功能.因为mixis使用的条件,随着Dart版本一直在变,这里讲的是Dart2.x中使用mixins的条件:
1、作为mixins的类只能继承自Object,不能继承其他类
2、作为mixins的类不能有构造函数
3、一个类可以mixins多个mixins类
4、mixins绝不是继承,也不是接口,而是一种全新的特性
class Person{
String name;
num age;
Person(this.name,this.age);
printInfo(){
print('${this.name}----${this.age}');
}
void run(){
print("Person Run");
}
}
class A {
String info="this is A";
void printA(){
print("A");
}
void run(){
print("A Run");
}
}
class B {
void printB(){
print("B");
}
void run(){
print("B Run");
}
}
class C extends Person with B,A{
C(String name, num age) : super(name, age);
}
void main(){
var c=new C('张三',20);
c.printInfo();
// c.printB();
// print(c.info);
c.run();
}
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();
print(c is C); //true
print(c is A); //true
print(c is B); //true
// var a=new A();
// print(a is Object);
}
11.16 泛型
11.16.1 泛型方法
/*
通俗理解:泛型就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持(类型校验)
*/
//只能返回string类型的数据
// String getData(String value){
// return value;
// }
//同时支持返回 string类型 和int类型 (代码冗余)
// String getData1(String value){
// return value;
// }
// int getData2(int value){
// return value;
// }
//同时返回 string类型 和number类型 不指定类型可以解决这个问题
// getData(value){
// return value;
// }
//不指定类型放弃了类型检查。我们现在想实现的是传入什么 返回什么。比如:传入number 类型必须返回number类型 传入 string类型必须返回string类型
// T getData<T>(T value){
// return value;
// }
getData<T>(T value){
return value;
}
void main(){
// print(getData(21));
// print(getData('xxx'));
// getData<String>('你好');
print(getData<int>(12));
}
11.16.2 泛型类
//集合List 泛型类的用法
//案例:把下面类转换成泛型类,要求MyList里面可以增加int类型的数据,也可以增加String类型的数据。但是每次调用增加的类型要统一
/*
class MyList {
List list = <int>[];
void add(int value) {
this.list.add(value);
}
List getList() {
return list;
}
}
MyList l = new MyList();
l.add(1);
l.add(12);
l.add(5);
print(l.getList());
*/
class MyList<T> {
List list = <T>[];
void add(T value) {
this.list.add(value);
}
List getList() {
return list;
}
}
main() {
// MyList l1=new MyList();
// l1.add("张三");
// l1.add(12);
// l1.add(true);
// print(l1.getList());
// MyList l2 = new MyList<String>();
// l2.add("张三1");
// // l2.add(11); //错误的写法
// print(l2.getList());
// MyList l3 = new MyList<int>();
// l3.add(11);
// l3.add(12);
// l3.add("aaaa");
// print(l3.getList());
// List list = List.filled(2, "");
// list[0] = "张三";
// list[1] = "李四";
// print(list);
// List list = new List.filled(2, "");
// list[0] = "张三1";
// list[1] = "李四";
// print(list);
// List list = new List<String>.filled(2, "");
// list[0] = "张三1";
// list[1] = "李四";
// print(list);
List list2 = new List<int>.filled(2, 0);
list2[0] = 12;
list2[1] = 13;
print(list2);
}
11.16.3 泛型接口
/*
Dart中的泛型接口:
实现数据缓存的功能:有文件缓存、和内存缓存。内存缓存和文件缓存按照接口约束实现。
1、定义一个泛型接口 约束实现它的子类必须有getByKey(key) 和 setByKey(key,value)
2、要求setByKey的时候的value的类型和实例化子类的时候指定的类型一致
*/
// abstract class ObjectCache {
// getByKey(String key);
// void setByKey(String key, Object value);
// }
// abstract class StringCache {
// getByKey(String key);
// void setByKey(String key, String value);
// }
// abstract class Cache<T> {
// getByKey(String key);
// void setByKey(String key, T value);
// }
abstract class Cache<T> {
getByKey(String key);
void setByKey(String key, T value);
}
class FileCache<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});
}
12 其他
12.1 库
前面介绍Dart基础知识的时候基本上都是在一个文件里面编写Dart代码的,但实际开发中不可能这么写,模块化很重要,所以这就需要使用到库的概念。
在Dart中,库的使用时通过import关键字引入的。
library指令可以创建一个库,每个Dart文件都是一个库,即使没有使用library指令来指定。
Dart中的库主要有三种:
1、我们自定义的库
import ‘lib/xxx.dart’;
2、系统内置库
import ‘dart:math’;
import ‘dart:io’;
import ‘dart:convert’;
3、Pub包管理系统中的库
https://pub.dev/packages
https://pub.flutter-io.cn/packages
https://pub.dartlang.org/flutter/
1、需要在自己想项目根目录新建一个pubspec.yaml
2、在pubspec.yaml文件 然后配置名称 、描述、依赖等信息
3、然后运行 pub get 获取包下载到本地
4、项目中引入库 import ‘package:http/http.dart’ as http; 看文档使用
import 'dart:io';
import 'dart:convert';
void main() async{
var result = await getDataFromZhihuAPI();
print(result);
}
//api接口: http://news-at.zhihu.com/api/3/stories/latest
getDataFromZhihuAPI() async{
//1、创建HttpClient对象
var httpClient = new HttpClient();
//2、创建Uri对象
var uri = new Uri.http('news-at.zhihu.com','/api/3/stories/latest');
//3、发起请求,等待请求
var request = await httpClient.getUrl(uri);
//4、关闭请求,等待响应
var response = await request.close();
//5、解码响应的内容
return await response.transform(utf8.decoder).join();
}
pubspec.yaml
name: xxx
description: A new flutter module project.
dependencies:
http: ^0.12.0+2
date_format: ^1.0.6
environment:
sdk: '>=2.10.0 <3.0.0'
12.2 Async、Await
async和await
这两个关键字的使用只需要记住两点:
只有async方法才能使用await关键字调用方法
如果调用别的async方法必须使用await关键字
async是让方法变成异步。
await是等待异步方法执行完成。
void main() async{
var result = await testAsync();
print(result);
}
//异步方法
testAsync() async{
return 'Hello async';
}
12.3 导入Pub包管理系统中的库
pub包管理系统:
1、从下面网址找到要用的库
https://pub.dev/packages
https://pub.flutter-io.cn/packages
https://pub.dartlang.org/flutter/
2、创建一个pubspec.yaml文件,内容如下
name: xxx
description: A new flutter module project.
dependencies:
http: ^0.12.0+2
date_format: ^1.0.6
3、配置dependencies
4、运行pub get 获取远程库
5、看文档引入库使用
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
import 'package:date_format/date_format.dart';
main() async {
// var url = "http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1";
// // Await the http get response, then decode the json-formatted responce.
// var response = await http.get(url);
// if (response.statusCode == 200) {
// var jsonResponse = convert.jsonDecode(response.body);
// print(jsonResponse);
// } else {
// print("Request failed with status: ${response.statusCode}.");
// }
print(formatDate(DateTime(1989, 2, 21), [yyyy, '*', mm, '*', dd]));
}
12.4 库的重命名 Dart冲突解决
/*
1、冲突解决
当引入两个库中有相同名称标识符的时候,如果是java通常我们通过写上完整的包名路径来指定使用的具体标识符,甚至不用import都可以,但是Dart里面是必须import的。当冲突的时候,可以使用as关键字来指定库的前缀。如下例子所示:
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
Element element1 = new Element(); // Uses Element from lib1.
lib2.Element element2 = new lib2.Element(); // Uses Element from lib2.
*/
import 'lib/Person1.dart';
import 'lib/Person2.dart' as lib;
main(List<String> args) {
Person p1=new Person('张三', 20);
p1.printInfo();
lib.Person p2=new lib.Person('李四', 20);
p2.printInfo();
}
12.5 部分导入
部分导入:如果只需要导入库的一部分,有两种模式:
模式一:只导入需要的部分,使用show关键字,如下例子所示:
import ‘package:lib1/lib1.dart’ show foo;
模式二:隐藏不需要的部分,使用hide关键字,如下例子所示:
import ‘package:lib2/lib2.dart’ hide foo;
// import 'lib/myMath.dart' show getAge;
import 'lib/myMath.dart' hide getName;
void main(){
// getName();
getAge();
}
12.6 延迟加载
延迟加载:也称为懒加载,可以在需要的时候再进行加载。 懒加载的最大好处是可以减少APP的启动时间。
懒加载使用deferred as 关键字来指定,如下例子所示:
import 'package:deferred/hello.dart' deferred as hello;
//当需要使用的时候,需要使用loadLibrary()方法来加载:
greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
12.7 Null safety 以及可空类型 非空断言
Null safety翻译成中文的意思是空安全。
null safety 可以帮助开发者避免一些日常开发中很难被发现的错误,并且额外的好处是可以改善性能。
Flutter2.2.0(2021年5月19日发布) 之后的版本都要求使用null safety。
? 可空类型
! 类型断言
String? getData(apiUrl){
if(apiUrl!=null){
return "this is server data";
}
return null;
}
// void printLength(String? str){
// // print(str!.length);
// if (str!=null){
// print(str.length);
// }
// }
void printLength(String? str){
try {
print(str!.length);
} catch (e) {
print("str is null");
}
}
void main(args) {
//1、 ? 可空类型
// int a=123;
// print(a);
// String username="张三";
// print(username);
// List<String> l1=["张三","李四","王五"];
// print(l1);
// int a=123; //非空的int类型
// a=null; //A value of type 'Null' can't be assigned to a variable of type 'int'
// String username="张三"; //非空的String类型
// username=null; //A value of type 'Null' can't be assigned to a variable of type 'String'.
// String? username="张三"; // String? 表示username是一个可空类型
// username=null;
// print(username);
// int? a=123; // int? 表示a是一个可空类型
// a=null;
// print(a);
// List<String> l1=["张三","李四","王五"];
// l1=null; //A value of type 'Null' can't be assigned to a variable of type 'List<String>'.
// List<String>? l1=["张三","李四","王五"];
// l1=null;
// print(l1);
//调用方法
// print(getData("http://www.itying.com"));
// print(getData(null));
// ! 类型断言
// String? str="this is str";
// str=null;
// print(str!.length);
//类型断言: 如果str不等于null 会打印str的长度,如果等于null会抛出异常
// printLength("str");
printLength(null);
}
12.8 late
/*
Null safety翻译成中文的意思是空安全。
late 关键字主要用于延迟初始化。
*/
class Person {
late String name;
late int age;
void setName(String name, int age) {
this.name = name;
this.age = age;
}
String getName() {
return "${this.name}---${this.age}";
}
}
void main(args) {
Person p = new Person();
p.setName("张三", 20);
print(p.getName());
}
12.9 required
required翻译成中文的意思是需要、依赖。required关键词:最开始 @required 是注解现在它已经作为内置修饰符。主要用于允许根据需要标记任何命名参数(函数或类),使得它们不为空。因为可选参数中必须有个 required 参数或者该参数有个默认值。
String printUserInfo(String username, {int age=10, String sex="男"}) {//行参
return "姓名:$username---性别:$sex--年龄:$age";
}
String printInfo(String username, {required int age, required String sex}) {//行参
return "姓名:$username---性别:$sex--年龄:$age";
}
void main(args) {
print(printUserInfo('张三'));
print(printUserInfo('张三',age: 20,sex: "女"));
//age 和 sex必须传入
print(printInfo('张三',age: 22,sex: "女"));
}
required命名参数 命名可选参数
// name 可以传入也可以不传入 age必须传入
class Person {
String? name; //可空属性
int age;
Person({this.name,required this.age}); //表示 name 和age 必须传入
String getName() {
return "${this.name}---${this.age}";
}
}
void main(args) {
Person p=new Person(
name: "张三",
age: 20
);
print(p.getName()); //张三---20
Person p1=new Person(
age: 20
);
print(p1.getName()); //null---20
}
12.10 const identical 函数
/*
dart:core 库中identical 函数的用法介绍如下。
用法:
bool identical(
Object? a,
Object? b
)
//检查两个引用是否指向同一个对象。
var o = new Object();
var isIdentical = identical(o, new Object()); // false, different objects.
print(isIdentical);
isIdentical = identical(o, o); // true, same object
print(isIdentical);
isIdentical = identical(const Object(), const Object()); // true, const canonicalizes
print(isIdentical);
isIdentical = identical([1], [1]); // false
print(isIdentical);
isIdentical = identical(const [1], const [1]); // true
print(isIdentical);
isIdentical = identical(const [1], const [2]); // false
print(isIdentical);
isIdentical = identical(2, 1 + 1); // true, integers canonicalizes
print(isIdentical);
*/
void main(){
// var o1 = new Object();
// var o2 = new Object();
// print(identical(o1,o2)); //false 不共享存储空间
// print(identical(o1,o1)); //true 共享存储空间
// var o1 = Object();
// var o2 = Object();
// print(identical(o1,o2)); //false
// print(identical(o1,o1)); //true
//表示实例化常量构造函数
//o1 和 o2共享了存储空间
// var o1 = const Object();
// var o2 = const Object();
// print(identical(o1,o2)); //true 共享存储空间
// print(identical(o1,o1)); //true 共享存储空间
// print(identical([2],[2])); //false
// var a=[2];
// var b=[2];
// print(identical(a,b)); //false 不共享存储空间
// print(identical(const [2],const [2])); //true
const a=[2];
const b=[2];
print(identical(a,b)); //true 共享存储空间
const c=[2];
const d=[3];
print(identical(c,d)); //false 不共享存储空间
}
// 发现:const关键词在多个地方创建相同的对象的时候,内存中只保留了一个对象
// 共享存储空间条件:1、常量 2、值相等
12.11 常量构造函数
常量构造函数总结如下几点:
1、常量构造函数需以const关键字修饰
2、const构造函数必须用于成员变量都是final的类
3、如果实例化时不加const修饰符,即使调用的是常量构造函数,实例化的对象也不是常量实例
4、实例化常量构造函数的时候,多个地方创建这个对象,如果传入的值相同,只会保留一个对象。
5、Flutter中const 修饰不仅仅是节省组件构建时的内存开销,Flutter 在需要重新构建组件的时候,由于这个组件是不应该改变的,重新构建没有任何意义,因此 Flutter 不会重建构建 const 组件
//常量构造函数
class Container{
final int width;
final int height;
const Container({required this.width,required this.height});
}
void main(){
var c1=Container(width: 100,height: 100);
var c2=Container(width: 100,height: 100);
print(identical(c1, c2)); //false
var c3=const Container(width: 100,height: 100);
var c4=const Container(width: 100,height: 100);
print(identical(c3, c4)); //true
var c5=const Container(width: 100,height: 110);
var c6=const Container(width: 120,height: 100);
print(identical(c5, c6)); //false
}
// 实例化常量构造函数的时候,多个地方创建这个对象,如果传入的值相同,只会保留一个对象。