flutter学习之Dart基础
看完了dart基础,发现里面既有java基础的影子,也有kotlin的影子,面向对象思想,会java基础的,学习dart基础就当复习了。
前言
学习基础之前首先需要安装好环境:flutterSDK环境、AndroidSDK、JDK1.8、AndroidStudio或者VSCode或IntelliJ IDEA。
具体步骤百度或者参考flutter中文网文档Flutter中文网
一、Dart 变量 常量 命名规则
1.变量
dart是一个强大的脚本类语言,可以不预先定义变量类型 ,自动会类型推导,也可以声明的时候直接写类型。如果不声明类型,定义变量可以用var关键字,建议声明最好直接指定数据类型。
var str='this is dart';
String str='this is dart';
int str=123;
2.常量
常量有final 和 const修饰符
const值不变 一开始就得赋值
const PI = 3.14159;
PI = 123.1243; //错误的写法 常量不可以修改
print(PI);
//这样会报错,只能初始化一次,不能重复赋值
// PI = 123.1243; //错误的写法 常量不可以修改
// ^^
final 可以开始不赋值 只能赋一次 ; 而final不仅有const的编译时常量的特性,最重要的它是运行时常量,并且final是惰性初始化,即在运行时第一次使用前才初始化
final a = new DateTime.now();
print(a); //2021-07-19 14:35:42.489450
const b = new DateTime.now(); //报错了
相同点:
final、const必须初始化
final、const只能赋值一次
不同点:
final 与 const 修饰的变量取值时机不同:const 修饰的变量是在编译时已确定下来的值,而 final 修饰的变量是在运行时才确定下来的。const 修饰的变量是在编译期,程序运行前就有确定值。使用 const 修饰的常量的值,必须由可在编译时可计算出结果的。
应用范畴不同:final 只可用来修饰变量, const 关键字即可修饰变量也可用来修饰 常量构造函数,访问类中const修饰的变量需要static修饰,const修饰的List集合任意索引不可修改,final修饰的可以修改
class A{
static const a = 10;
}
3.Dart的命名规则
1、变量名称必须由数字、字母、下划线和美元符($)组成。
2、注意:标识符开头不能是数字
3、标识符不能是保留字和关键字。
4、变量的名字是区分大小写的如: age和Age是不同的变量。在实际的运用中,也建议,不要用一个单词大小写区分两个变量。
5、标识符(变量名称)一定要见名思意 :变量名称建议用名词,方法名称建议用动词
var str1='2134214';
//var 2str='xxx'; //错误
// var if='124214'; //错误
//变量的名字是区分大小写的
var age=20;
var Age=30;
print(age);
print(Age);
var price=12;
var name=124;
二、数据类型
Dart中支持以下数据类型:
常用数据类型:
Numbers(数值):
int
double
Strings(字符串)
String
Booleans(布尔)
bool
List(数组)
在Dart中,数组是列表对象,所以大多数人只是称它们为列表
Maps(字典)
通常来说,Map 是一个键值对相关的对象。 键和值可以是任何类型的对象。
每个键只出现一次, 而一个值则可以出现多次
1.字符串String
//字符串定义
var str='this is str';
String str1='this is str1';
String str1 = '''
this is str1
this is str1
this is str1
''';
var str2 = """
this is str1
this is str1
this is str1
""";
print(str1);
print(str1);
打印:
上面是单独声明字符串,下面是声明多条字符串
//2、字符串的拼接
String str1 = '你好';
String str2 = 'Dart';
print("$str1 $str2");
print(str1 + str2);
print(str1 + " " + str2);
2.整数型
//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);
3.布尔型 bool
//1、bool
bool flag1=true;
//2、条件判断语句
var flag=true;
if(flag){
print('真');
}else{
print('假');
}
if(flag) print('真');
4.List(数组/集合)
定义方式:
//不指定数据类型
var list=["张三",20,true];
//指定数据类型
var list=<String>["张三","李四"];
//增加数据
list.add("王五");
list.add("老刘");
//创建固定长度的集合,不可以修改集合的长度
var list = List<int>.filled(3, 1);
print(list);
//固定长度的集合可以修改元素
list[0] = 2;
list[1] = 3;
5.Maps(字典)
//第一种定义 Maps的方式
var 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"]);
6.Dart判断数据类型— is 关键字
在flutter中,i使用is关键字可以判断一个常量或者变量是属于什么数字类型,例如:
var str=123;
if(str is String){
print('是string类型');
}else if(str is int){
print('int');
}else{
print('其他类型');
}
三、运算符和数据类型转换
1、Dart运算符:
算术运算符
+ - * / ~/ (取整) %(取余)
关系运算符
== != > < >= <=
逻辑运算符
! && ||
赋值运算符
基础赋值运算符 =
复合赋值运算符 += -= *= /= %= ~/=
条件表达式
if else switch case
三目运算符
??运算符:
2、类型转换
1、Number与String类型之间的转换
2、其他类型转换成Booleans类型
1.算术运算符
int a = 13;
int b = 5;
print(a + b); //加 18
print(a - b); //减 8
print(a * b); //乘 65
print(a / b); //除 2.6
print(a % b); //其余 3
print(a ~/ b); //取整 2
var c = a * b; //65
print('--------');
print(c);
2.关系运算符
int a=5;
int b=3;
print(a==b); //判断是否相等 false
print(a!=b); //判断是否不等 true
print(a>b); //判断是否大于 true
print(a<b); //判断是否小于 false
print(a>=b); //判断是否大于等于true
print(a<=b); //判断是否小于等于false
if(a>b){
print('a大于b'); //打印这个
}else{
print('a小于b');
}
3.逻辑运算符
/* ! 取反 */
bool flag = false;
print(!flag); //取反 true
/* &&并且:全部为true的话值为true 否则值为false */
bool a = true;
bool b = true;
bool c = false;
print(a && b); //true
print(a && c); //false
/* ||或者:全为false的话值为false 否则值为true */
bool a = false;
bool b = false;
bool c = true;
print(a || b); //flase
print(a || c); //true
4.赋值运算符
// 1、基础赋值运算符 =
int a = 10;
int b = 3;
int c = a + b; //从右向左
print(c);
//2、 复合赋值运算符 += -= *= /= %= ~/=
var a = 12;
a = a + 10;
print(a); //22
var a = 12;
a += 10; //表示a=a+10
print(a); //22
var a = 4;
a *= 3; //a=a*3;
print(a); //12
5.条件表达式
//1、if else switch case
//判断一个人的成绩 如果大于60 显示及格 如果大于 70显示良好 如果大于90显示优秀
var score = 41;
if (score > 90) {
print('优秀');
} else if (score > 70) {
print('良好');
} else if (score >= 60) {
print('及格');
} else {
print('不及格');
}
var sex = "女";
switch (sex) {
case "男":
print('性别是男');
break;
case "女":
print('性别是女');
print('性别是女');
break;
default:
print('传入参数错误');
break;
}
//2、三目运算符
var falg = true;
var c;
if (falg) {
c = '我是true';
} else {
c = "我是false";
}
print(c); //我是true
bool flag = false;
String d = flag ? '我是true' : '我是false';
print(d); //我是false
//3 ??运算符
var a;
var b = a ?? 10;
print(b); // 10
6.赋值运算++ –
++ – 表示自增 自减 1
在赋值运算里面 如果++ – 写在前面 这时候先运算 再赋值,如果++ --写在后面 先赋值后运行运算
var a = 10;
a++; //a=a+1;
print(a); //11
var a = 10;
a--; //a=a-1;
print(a); //9
var a = 10;
var b = a++;
print(a); //11
print(b); //10
var a = 10;
var b = ++a;
print(a); //11
print(b); //11
var a = 10;
var b = --a;
print(a); //9
print(b); //9
var a = 10;
var b = a--;
print(a); //9
print(b); //10
var a = 10;
++a;
print(a); //11
7.类型转换
1、Number与String类型之间的转换
Number类型转换成String类型 toString()
String类型转成Number类型 int.parse()
String str = '123';
var myNum = int.parse(str);
print(myNum is int); //true
String str = '123.1';
var myNum = double.parse(str);
print(myNum is double); //true
var myNum = 12;
var str = myNum.toString();
print(str is String); //true
2、其他类型转换成Booleans类型
isEmpty:判断字符串是否为空
isNotEmpty:判断字符串是否不为空
var str = '';
if (str.isEmpty) {
print('str空');
} else {
print('str不为空');
}
var str1 = '';
if (str1.isNotEmpty) {
print('str空');
} else {
print('str不为空');
}
var myNum = 123;
if (myNum == 0) {
print('0');
} else {
print('非0');
}
var myNum;
if(myNum==null){
print('空');
}else{
print('非空');
}
三、循环
1.for循环
for基本语法
for (int i = 1; i<=100; i++) {
print(i);
}
//第一步,声明变量int i = 1;
//第二步,判断i <=100
//第三步,print(i);
//第四步,i++
//第五步 从第二步再来,直到判断为false
for(int i=1;i<=10;i++){
print(i);
}
// 1、打印0-50所有的偶数
for (int i = 0; i <= 50; i++) {
if (i % 2 == 0) {
print(i);
}
}
// 2、求 1+2+3+4 +...100的和
var sum = 0;
for (var i = 1; i <= 100; i++) {
sum += i;
}
print(sum);
//3、打印List ['张三','李四','王五'] 里面的内容
List list=['张三','李四','王五'];
for(var i=0;i<list.length;i++){
print(list[i]);
}
//4、定义一个二维数组 打印里面的内容
List list = [
{
"cate": '国内',
"news": [
{"title": "国内新闻1"},
{"title": "国内新闻2"},
{"title": "国内新闻3"}
]
},
{
"cate": '国际',
"news": [
{"title": "国际新闻1"},
{"title": "国际新闻2"},
{"title": "国际新闻3"}
]
}
];
/*
国内
国内新闻1
国内新闻2
国内新闻3
国际
国际新闻1
国际新闻2
*/
for (var i = 0; i < list.length; i++) {
print(list[i]["cate"]);
print('-------------');
for (var j = 0; j < list[i]["news"].length; j++) {
print(list[i]["news"][j]["title"]);
}
}
2.while do~while循环
语法格式:
while(表达式/循环条件){
}
do{
语句/循环体
}while(表达式/循环条件);
注意:1、最后的分号不要忘记
2、循环条件中使用的变量需要经过初始化
3、循环体中,应有结束循环的条件,否则会造成死循环。
while 和 do while的区别 第一次循环条件不成立的情况下
如果判断条件不满足,while 不会执行,do while会执行
int i = 1;
while (i <= 10) {
print(i);
i++;
}
//求1+2+3+4 ...+100的和
int i = 1;
var sum = 0;
while (i <= 100) {
sum += i;
i++;
}
print(sum);
int i = 1;
var sum = 0;
do {
sum += i;
i++;
} while (i <= 100);
print(sum);
//while 和 do while的区别 第一次循环条件不成立的情况下
int i = 10;
while (i < 2) {
print('执行代码'); //不执行
}
var j = 10;
do {
print('执行代码'); //执行
} while (j < 2);
3.break和continue
break语句功能:
1、在switch语句中使流程跳出switch结构。
2、在循环语句中使流程跳出当前循环,遇到break 循环终止,后面代码也不会执行
强调:
1、如果在循环中已经执行了break语句,就不会执行循环体中位于break后的语句。
2、在多层循环中,一个break语句只能向外跳出一层
break可以用在switch case中 也可以用在 for 循环和 while循环中
continue语句的功能:
【注】只能在循环语句中使用,使本次循环结束,即跳过循环体重下面尚未执行的语句,接着进行下次的是否执行循环的判断。
continue可以用在for循环以及 while循环中,但是不建议用在while循环中,不小心容易死循环
//1、如果i等于4的话跳过
for (var i = 1; i <= 10; i++) {
if (i == 4) {
continue; /*跳过当前循环体 然后循环还会继续执行*/
}
print(i); //1 2 3 5 6 7 8 9 10
}
//2、如果 i等于4的话跳出循环
for (var i = 1; i <= 10; i++) {
if (i == 4) {
break; /*跳出循环体*/
}
print(i); // 1 2 3
}
//3、break语句只能向外跳出一层
for (var i = 0; i < 5; i++) {
print('外层---$i');
for (var j = 0; j < 3; j++) {
if (j == 2) {
break;
}
print('里层$j');
}
}
//4、while循环 break跳出循环
var i = 1;
while (i <= 10) {
if (i == 4) {
break;
}
print(i); // 1 2 3
i++;
}
//5 break在switch case中
var sex = "男";
switch (sex) {
case "男":
print('男');
break;
case "女":
print('男');
break;
default:
四、集合
1.List集合
List里面常用的属性和方法:
常用属性:
length 长度
reversed 翻转
isEmpty 是否为空
isNotEmpty 是否不为空
常用方法:
add 增加
addAll 拼接数组
indexOf 查找 传入具体值
remove 删除 传入具体值
removeAt 删除 传入索引值
fillRange 修改
insert(index,value); 指定位置插入
insertAll(index,list) 指定位置插入List
toList() 其他类型转换成List
join() List转换成字符串
split() 字符串转化成List
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);
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());
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); //{name: 张三, age: 20}
print(m); //{name: 李四}
//常用属性:
Map person={
"name":"张三",
"age":20,
"sex":"男"
};
print(person.keys.toList()); //[name, age, sex]
print(person.values.toList()); //[张三, 20, 男]
print(person.isEmpty); //false
print(person.isNotEmpty); /true
//常用方法:
Map person={
"name":"张三",
"age":20,
"sex":"男"
};
person.addAll({
"work":['敲代码','送外卖'],
"height":160
});
print(person); //{name: 张三, age: 20, sex: 男, work: [敲代码, 送外卖], height: 160}
person.remove("sex");
print(person); //{name: 张三, age: 20, work: [敲代码, 送外卖], height: 160}
print(person.containsValue('张三')); //true
3.forEach map where any every用法
1、forEach 增强for循环,遍历集合
List myList = ['香蕉', '苹果', '西瓜'];
//普通for循环
for (var i = 0; i < myList.length; i++) {
print(myList[i]);
}
//for - in 循环
for (var item in myList) {
print(item);
}
//forEach循环
myList.forEach((value) {
print("$value");
});
Map person = {"name": "张三", "age": 20};
person.forEach((key, value) {
print("$key---$value");
});
var s = new Set();
s.addAll([1, 222, 333]);
s.forEach((value) => print(value));
2、map:针对集合中的所有元素做操作
List myList = [1, 3, 4];
var newList = myList.map((value) {
return value * 2;
});
print(newList.toList()); //[2, 6, 8]
3、where 针对满足条件的元素做操作
List myList = [1, 3, 4, 5, 7, 8, 9];
var newList = myList.where((value) {
return value > 5;
});
print(newList.toList()); /[7, 8, 9]
4、any 只要集合里面有满足条件的就返回true
List myList = [1, 3, 4, 5, 7, 8, 9];
var f = myList.any((value) {
//只要集合里面有满足条件的就返回true
return value > 5;
});
print(f); //true
5、where 每一个都满足条件返回true 否则返回false
List myList = [1, 3, 4, 5, 7, 8, 9];
var f = myList.every((value) {
//每一个都满足条件返回true 否则返回false
return value > 5;
});
print(f); //false
五、方法
1.定义
内置方法/函数:
print();
自定义方法:
自定义方法的基本格式:
返回类型 方法名称(参数1,参数2,...){
方法体
return 返回值;
}
void printInfo(){
print('我是一个自定义方法');
}
int getNum(){
var myNum=123;
return myNum;
}
String printUserInfo(){
return 'this is str';
}
List getList(){
return ['111','2222','333'];
}
//调用
void main(){
print('调用系统内置的方法');
printInfo();
var n=getNum();
print(n);
print(printUserInfo());
print(getList());
print(getList());
}
2.各种方式方法
使用 [ 参数1,参数2] 定义的参数,可以传,可以不传,需要指定默认值
使用 { 参数1,参数2}定义的参数,需要指定类型默认值
//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 printInfo(String username, [int age = 0]) {
//行参
if (age != 0) {
return "姓名:$username---年龄:$age";
}
return "姓名:$username---年龄保密";
}
print(printInfo('张三', 21)); //实参
print(printInfo('张三'));
//4、定义一个带默认参数的方法
String printUsersInfo(String username,[String sex='男',int age=0]){ //行参
if(age!=0){
return "姓名:$username---性别:$sex--年龄:$age";
}
return "姓名:$username---性别:$sex--年龄保密";
}
print(printUsersInfo('张三'));
print(printUsersInfo('小李','女'));
print(printUsersInfo('小李','女',30));
//5、定义一个命名参数的方法,最新的dart定义命名参数需要指定类型默认值
String printInfos(String username, {int age = 0, String sex = '男'}) {//行参
if (age != 0) {
return "姓名:$username---性别:$sex--年龄:$age";
}
return "姓名:$username---性别:$sex--年龄保密";
}
print(printInfos('张三', age: 20, sex: '未知'));
//6、实现一个 把方法当做参数的方法
var fn=(){
print('我是一个匿名方法');
};
fn();
//方法
fn1() {
print('fn1');
}
//方法
fn2(fn) {
fn();
}
//调用fn2这个方法 把fn1这个方法当做参数传入
fn2(fn1);
3.箭头函数
List list=['苹果','香蕉','西瓜'];
list.forEach((value){
print(value);
});
list.forEach((element)=>print(element));
List list = [4, 1, 2, 3, 4];
var newList = list.map((value) {
if (value > 2) {
return value * 2;
}
return value;
});
var newlists = list.map((value) => value > 2 ? value * 2 : value);
4.其他方法
//匿名方法
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);
5.闭包
1、全局变量特点: 全局变量常驻内存、全局变量污染全局
2、局部变量的特点: 不常驻内存会被垃圾机制回收、不会污染全局
想实现的功能:
1.常驻内存
2.不污染全局
产生了闭包,闭包可以解决这个问题.....
闭包: 函数嵌套函数, 内部函数会调用外部函数的变量或参数, 变量或参数不会被系统回收(不会释放内存)
闭包的写法: 函数嵌套函数,并return 里面的函数,这样就形成了闭包。
//局部变量
printInfo() {
var myNum = 123;
myNum++;
print(myNum);
}
printInfo();
printInfo();
printInfo();
//闭包
Function fn() {
var a = 123; /*不会污染全局 常驻内存*/
return () {
a++;
print(a);
};
}
var b = fn();
b();
b();
b();
六、面向对象
面向对象编程(OOP)的三个基本特征是:封装、继承、多态
封装:封装是对象和类概念的主要特性。封装,把客观事物封装成抽象的类,并且把自己的部分属性和方法提供给其他对象调用, 而一部分属性和方法则隐藏。
继承:面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
多态:允许将子类类型的指针赋值给父类类型的指针, 同一个函数调用会有不同的执行效果 。
Dart所有的东西都是对象,所有的对象都继承自Object类。
Dart是一门使用类和单继承的面向对象语言,所有的对象都是类的实例,并且所有的类都是Object的子类
一个类通常由属性和方法组成。
1.封装
Dart是一门使用类和单继承的面向对象语言,所有的对象都是类的实例,并且所有的类都是Object的子类
下面创建一个封装类。有默认的构造函数,有get set方法,toString方法,命名构造函数
class Person {
//最新版本的dart中需要初始化不可为null的实例字段,如果不初始化的话需要在属性前面加上late
//Dart和其他面向对象语言不一样,Data中没有 public private protected这些访问修饰符合
但是我们可以使用_把一个属性或者方法定义成私有。
late String _name; //前面加_表示私有
late int _age;
Person(String name, int age) {
this._name = name;
this._age = age;
}
Person.now() {
print("我是命名构造函数");
}
Person.setInfo(String name, int age) {
this._name = name;
this._age = age;
}
void printInfo() {
print("姓名:$_name---年龄:$_age");
}
int get age => _age;
set age(int value) {
_age = value;
}
String get name => _name;
set name(String value) {
_name = value;
}
@override
String toString() {
return 'Person{_name: $_name, _age: $_age}';
}
void _run(){
print('这是一个私有方法');
}
execRun(){
this._run(); //类里面方法的相互调用
}
}
调用
main() {
Person person = new Person("张三", 20);
Person person1 = new Person.setInfo("李四", 25);
person.printInfo();
person1.printInfo();
person.name = "王五";
print(person.name);
person.execRun(); //调取私有方法
}
2.继承
Dart中的静态成员:
1、使用static 关键字来实现类级别的变量和函数
2、静态方法不能访问非静态成员,非静态方法可以访问静态成员
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();
}
Dart中的对象操作符:
? 条件运算符 (了解)已被废弃 https://dart.dev/tools/diagnostic-messages#invalid_null_aware_operator
as 类型转换
is 类型判断 .//前面已经学习
.. 级联操作 (连缀) (记住)
main() {
//类型转换as
var p3;
p3 = "";
p3 = new Person("王五", 25);
(p3 as Person).printInfo();
//级联操作 ..
Person p4 = new Person("老六", 23);
p4
..name = "sdfsd"
..age = 12312
..printInfo();
}
Dart中的类的继承:
1、子类使用extends关键词来继承父类
2、子类会继承父类里面可见的属性和方法 但是不会继承构造函数
3、子类能复写父类的方法 getter和setter
class Person {
String name;
num age;
Person(this.name, this.age);
void printInfo() {
print("${this.name}---${this.age}");
}
work(){
print("${this.name}在工作...");
}
}
//super关键词的使用 实例化自类给父类构造函数传参
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}");
super.work(); //子类调用父类的方法
}
//覆写父类的方法
@override //可以写也可以不写 建议在覆写父类方法的时候加上
void printInfo(){
print("姓名:${this.name}---年龄:${this.age}");
}
@override
work(){
print("${this.name}的工作是写代码");
}
}
main() {
Web w = new Web('张三', 12, "男");
w.printInfo();
w.run();
}
实例化自类给命名构造函数传参
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() {
Web w = new Web('张三', 12, "男");
w.printInfo();
w.run();
}
3.抽象类
Dart中抽象类: Dart抽象类主要用于定义标准,子类可以继承抽象类,也可以实现抽象类接口。
1、抽象类通过abstract 关键字来定义
2、Dart中的抽象方法不能用abstract声明,Dart中没有方法体的方法我们称为抽象方法。
3、如果子类继承抽象类必须得实现里面的抽象方法
4、如果把抽象类当做接口实现的话必须得实现抽象类里面定义的所有属性和方法。
5、抽象类不能被实例化,只有继承它的子类可以
extends抽象类 和 implements的区别:
1、如果要复用抽象类里面的方法,并且要用抽象方法约束自类的话我们就用extends继承抽象类
2、如果只是把抽象类当做标准的话我们就用implements实现抽象类
3、单继承 多实现
//定义一个Animal 类要求它的子类必须包含eat方法
abstract class Animal{
eat(); //抽象方法
run(); //抽象方法
printInfo(){
print('我是一个抽象类里面的普通方法');
}
}
class Dog extends Animal{
@override
eat() {
print('小狗在吃骨头');
}
@override
run() {
print('小狗在跑');
}
}
class Cat extends Animal{
@override
eat() {
print('小猫在吃老鼠');
}
@override
run() {
print('小猫在跑');
}
}
main(){
Dog d=new Dog();
d.eat();
d.printInfo();
Cat c=new Cat();
c.eat();
c.printInfo();
// Animal a=new Animal(); //抽象类没法直接被实例化
}
4.多态
Datr中的多态:
允许将子类类型的指针赋值给父类类型的指针, 同一个函数调用会有不同的执行效果 。
子类的实例赋值给父类的引用。
多态就是父类定义一个方法不去实现,让继承他的子类去实现,每个子类有不同的表现。
父类对象指向子类引用
abstract class Animal {
eat(); //抽象方法
}
class Dog extends Animal {
@override
eat() {
print(‘小狗在吃骨头’);
}
run() {
print(‘run’);
}
}
class Cat extends Animal {
@override
eat() {
print(‘小猫在吃老鼠’);
}
run() {
print(‘run’);
}
}
main() {
Animal d = new Dog();
d.eat();
Animal c = new Cat();
c.eat();
}
5.接口
和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 {
@override
String uri;
Mysql(this.uri);
@override
add(data) {
print('这是mysql的add方法' + data);
}
@override
delete() {
return null;
}
@override
save() {
return null;
}
remove() {}
}
class MsSql implements Db {
@override
late String uri;
@override
add(String data) {
print('这是mssql的add方法' + data);
}
@override
delete() {
return null;
}
@override
save() {
return null;
}
}
main() {
Mysql mysql = new Mysql('xxxxxx');
mysql.add('1243214');
MsSql msql = new MsSql();
msql.add('1243214');
}
接口的多实现
abstract class A {
late String name;
printA();
}
abstract class B {
printB();
}
class C implements A, B {
@override
late String name;
@override
printA() {
print('printA');
}
@override
printB() {
return null;
}
}
void main() {
C c = new C();
c.printA();
}
6.mixins
mixins的中文意思是混入,就是在类中混入其他功能。
在Dart中可以使用mixins实现类似多继承的功能
因为mixins使用的条件,随着Dart版本一直在变,这里讲的是Dart2.x中使用mixins的条件:
1、作为mixins的类只能继承自Object,不能继承其他类
2、作为mixins的类不能有构造函数
3、一个类可以mixins多个mixins类
4、mixins绝不是继承,也不是接口,而是一种全新的特性
5、使用with关键字实现类型雨多继承的特性
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();
print(c is C); //true
print(c is A); //true
print(c is B); //true
}
7.泛型
泛型就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持(类型校验)
也就是写通用的类,方法,可以指定各种类型,方便复用
泛型方法:
T getData<T>(T value){
return value;
}
void main() {
print(getData(21));
print(getData('xxx'));
getData<String>('你好');
print(getData<int>(12));
print(getData<Person>(new Person()));
}
泛型类:
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());
//String类型
MyList l2 = new MyList<String>();
l2.add("张三1");
print(l2.getList());
//int类型
MyList l3 = new MyList<int>();
l3.add(11);
l3.add(12);
print(l3.getList());
}
泛型接口:
实现数据缓存的功能:有文件缓存、和内存缓存。内存缓存和文件缓存按照接口约束实现。
1、定义一个泛型接口 约束实现它的子类必须有getByKey(key) 和 setByKey(key,value)
2、要求setByKey的时候的value的类型和实例化子类的时候指定的类型一致
abstract class Cache<T> {
getByKey(String key);
void setByKey(String key, T value);
}
class FlieCache<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() {
FlieCache file = new FlieCache<double>();
file.setByKey("数据", 20.52);
MemoryCache m1 = new MemoryCache<String>();
m1.setByKey('index', '首页数据');
MemoryCache m = new MemoryCache<Map>();
m.setByKey('index', {"name": "张三", "age": 20});
}
/**
我是文件缓存 把key=数据 value=20.52的数据写入到了文件中
我是内存缓存 把key=index value=首页数据 -写入到了内存中
我是内存缓存 把key=index value={name: 张三, age: 20} -写入到了内存中
*/
七、其它
1.Dart中的库
前面介绍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; 看文档使用
2.使用系统内置库实现请求数据httpClient
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();
}
3.async和await
这两个关键字的使用只需要记住两点:
只有async方法才能使用await关键字调用方法
如果调用别的async方法必须使用await关键字
async是让方法变成异步。
await是等待异步方法执行完成。
void main() async {
var result = await testAsync();
print(result);
}
//异步方法
testAsync() async {
return 'Hello async';
}
4.Dart库的重命名 Dart冲突解决
当引入两个库中有相同名称标识符的时候,如果是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代替person2
lib.Person p2=new lib.Person('李四', 20);
p2.printInfo();
}
5.库部分导入
如果只需要导入库的一部分,有两种模式:
模式一:只导入需要的部分,使用show关键字,如下例子所示:
import 'package:lib1/lib1.dart' show foo;
模式二:隐藏不需要的部分,使用hide关键字,如下例子所示:
import 'package:lib2/lib2.dart' hide foo;
import 'lib/myMath.dart' show getAge, getName; //只需要年龄,姓名
import 'lib/myMath.dart' hide getSex; //不需要性别
void main() {
getName();
getAge();
}
6. 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 printLengths(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 b = 123; //非空的int类型
b = null; //报A value of type 'Null' can't be assigned to a variable of type 'int'
String usernames = "张三"; //非空的String类型
usernames =
null; //报A value of type 'Null' can't be assigned to a variable of type 'String'.
String? user = "张三"; // String? 表示username是一个可空类型
user = null;
print(username);
int? c = 123; // int? 表示a是一个可空类型
c = null;
print(c);
List<String> l1 = ["张三", "李四", "王五"];
l1 =
null; //报A value of type 'Null' can't be assigned to a variable of type 'List<String>'.
List<String>? l2 = ["张三", "李四", "王五"];
l2 = null;
print(l2);
//调用方法
print(getData("http://www.baidu.com"));
print(getData(null));
// ! 类型断言
String? str = "this is str";
str = null;
print(str!.length); //报异常
// 类型断言: 如果str不等于null 会打印str的长度,如果等于null会抛出异常
printLength("str");
printLength(null);
printLength("1231");
}
7.late 关键字
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());
}
8.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";
}
class Person {
String name;
int age;
Person({required this.name,required this.age}); //表示 name 和age 必须传入
String getName() {
return "${this.name}---${this.age}";
}
}
void main(args) {
print(printUserInfo('张三'));
print(printUserInfo('张三', age: 20, sex: "女"));
//age 和 sex必须传入
print(printInfo('张三', age: 22, sex: "女"));
Person p = new Person(name: "张三", age: 20);
print(p.getName());
}
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
}
总结
flutter学习之dart基础就这么多,这篇基础是看B站大地老师flutter视频记录,如果看资料学习慢,可以去看这个视频。