Flutter(二) Dart语法学习
一. Dart入口方法
每一个flutter项目的lib目录里面都有一个main.dart。这个文件就是flutter的入口文件,其中的main方法是dart的入口方法。
runApp返回的是组件。MyApp是自定义的一个组件。在flutter里面万物皆组件。
# void是当前函数的返回值 可以省略
void main() {//程序执行的入口函数 有且仅有一个 必须的
runApp(MyApp)
print('Hello World!')//dart通过print方法打印数据
}
//如果runApp后面只有一行代码也可以这样写
void main() => runApp(MyApp());
二. Dart变量,常量和命名规则
- 变量
dart是一个强大的脚本类语言,可以不预先定义变量类型,dart会通过类型推断 type inference(swift也是)自动判断当前变量的类型。dart中定义变量是通过var关键字或者通过类型来声明变量。
var str = '这是var变量';
String str1 = '这是类型声明的变量';
int number = 123;
注意: var 后不要写类型,写了类型不要写var 写了会报错 var a int = 5
-
常量
常量通过
final
或者const
修饰。差异之处:
const
修饰的常量在创建时就需要赋值(编译时的常量)赋值后不可改变。final
不仅有const
的编译时的常量特性,最重要的是它的运行时常量。并且final
是惰性初始化,即在运行时第一次使用前才初始化。如果是不改量的量,请使用final
或者cont
修饰它,而不是使用var
或者其他变量类型。
final name = ‘huang’;
final String rename = 'hai';
const bar = 100000;
cont double atm = 1.01325 * bar
- dart的命名规则
- 变量等名称必须由数字、字母、下划线和美元符号($)组成。
- 注意:标识符开头不能是数字。
- 标识符不能使保留字或者关键字。
- 变量的名称是区分大小写的如:age和Age是不同的变量。在实际的运用中,也建议不要用一个
- 标识符(变量名称)一定要见名思意 :变量名称建议用名称,方法名称建议用动词
三. Dart基础数据类型
-
字符串(String)
① 字符串创建方式
通过var
关键字或者String
单引号、双引号、三个单引号或者三个双引号来包裹字符组成字符串变量或者常量。
②字符串拼接,通过+
号或者通过$加变量 $str或者${表达式}
。
对应的①
/单引号 '' 或者 双引号 "" 来包裹字符组成字符串
String str1 = '这是字符串';
String str2 = "这是字符串";
//使用 + 链接两个字符串
String str3 = str1 + str2;
print(str3);
//多行字符串使用三重单引号 '''字符串'''
//三重双引号 """字符串"""
String str4 = """ 1111
22222
333333""";
print(str4);
//使用r前缀 可以使字符串里面的特殊字符作为普通字符串
// \n 换行符 特殊字符
String str5 = r'这是特殊字符\n现在不会换行';
String str6 = '这是特殊字符\n现在会换行';
print(str5);
print(str6);
对应的②
var str4 = str + str1;
print('$str4 ${str4.length}');
// dart判断数据类型 is关键词用来判断数据类型
if(str4 is String){
// print()
}
-
num(数字)
num
是数字类型的父类,有两个子类int
和double
。- int 整型 数值范围在-2的53次方到2的53次方减1
- double双精度浮点型
- int 必须是整型 double 既可以接收整型也可以接收浮点型
//print函数是控制套输出函数
print('a 是 $a');
print('你好dart');
double c = 10;//double可以包含整型 整型 dart sdk 2.1 之前不能使用,会报错
double d = 10.1;
print('d = $d');
num aa = -3; //num 是number类型的简写 abs()取绝对值
print('asdasdas' + aa.abs().toString());
//ceil() 带小数就向前进一位取大于或者等于表达式的最小整数
num h = 8.3;
num i = h.ceil();
print(i);
//floor()舍掉小数位 不管他多大
num j = 10.9;
num k = j.floor();
//round()四舍五入
- boolean
- 布尔 关键字 bool 值 true 或者 false
- 布尔类型的合法值只有两个 true 和 false 在条件判断语句中dart不会对条件语句进行类型转换
- 与其他语言不同 例如 JavaScript 中 布尔是非空都为true dart中只有值为true才可以 比如就算一个字符串不是空 但是他的返回值不是bool值 所以是false
bool value = true;
// bool value2 = 'asda';//不合法
bool value2 = false;
if(value){
print(value);
}
四. Dart集合
- List集合(数组)
//第一种定义List的方式
var list222 = ["asdsa","asdasd","asdsadff"];
list222.length; 获取长度
list222[0];获取第一个值
//第二种定义List的方式
var list111 = List();
list111.add('asdsdddda');//增加新值
list111.addAll(['hahah','asdasd']); 可以添加新的数组
//在定义List时 直接指定其类型
var List2223 = List<String>();//定义了一个只能放String类型的数组 调用其构造函数创建数组
//数组 里面的元素(值)可以重复 值的类型可以不同
List list1 = [1,2,3,4,5,6,7,8];
List list2 = [1,true,'qwe',1];
- List里面常用的属性和方法 Map Set差不多通用
//常用属性
length 长度
reversed 数组翻转 对列表倒序排序
isEmpty 是否为空
isNoTEmpty 是否不为空
//常用方法
add 增加元素 增加一个
addAll 拼接数组
indexOf 查找数据 传入具体值 查到了返回元素所在位置索引 查找不到返回-1
remove 删除 传入具体值
removeAt 删除 传入索引值
fillRange 修改数据 传入开始索引和结束索引 然后传入要添加的值(1,10 ,'修改的值');
insert(index,value); 指定位置插值 (1 要添加的位置在索引为1的元素前添加 ,’要添加的值‘)
insertAll(index,List); 指定位置插入List 和上面的一样 只是传入的是数组
toList() 其他类型转换为List
join() List转换为字符串 传入变成字符串后的分割元素之间的符号
split() 字符串转换为List 传入通过哪些符号来分割字符串 变成List
forEach
map
where
any
every
-
Maps(字典) 也可以叫对象是无序的键值对
- 常用属性:
- keys 获取所以key值
- values 获取所有Value值
- isEmpty 是否为空
- isNotEmpty是否不为空
- 常用方法
- remove(key) 删除指定key的数据
- addAll({....}) 合并映射字典 给映射增加属性
- containsValue 查看字典内是否有某个值 返回true false
- 常用属性:
//将key和Value相关联的对象
//key和Value都可以是任何对象
//定义方式 map字面量来定义
var person = {
"name" : "zhangsna",
"age" : 20,
"work" : ['haha','hehe']
};
//可以直接添加或者修改值
person["name"]; //取值
var per = Map();
// Map类型
Map dic = {'name': 'zhangsna', 'age': 20};
Map dic2 = new Map();//dart2.0版本后可以不用写new关键字
var dic3 = new Map();
//从map中取值 如果map对象中没有对应的键 返回null
print(tempMap2['sencond'].toString());
// .length 取得map对象的长度
-
Set
集合里面的值不可以重复,值的类型必须统一。最主要的功能是去除数组中重复的内容。Set是没有顺序且不能重复的集合,所以不能通过索引去获取值。
定义有两种形式 Set字面量 Set类型
var s = Set();
var set1 = {'value1','value2'};//Set字面量
//Set类型
//变量类型 决定
Set set2 = {};
//类型参数
var set3 = <String>{};//指定元素为String类型的Set集合
Set<String> set4 = {};
//.add()添加至到集合
set4.add('value1');
print(set4);
// .addAll 将元素为同类型的集合添加到现有集合
set4.addAll(set1);
print(set4);
// .length 得到集合的长度 里面多少个元素或者值
print(set4.length.toString());
-
常用方法
①. forEach() 传入函数表达式 遍历
list.forEach((value){ 遍历数组 会把数组的每一个元素赋值给Value
print("$value");
});
②. map方法和JS里map方法很像,遍历一个数组同时返回一个数组,遍历同时会给出每个元素value。
var newList = lsit.map((value){
return value*2;
})
③. where方法遍历数组得到元素同时可以加判断语句
var newList = list.where((value){
return value>5;
})
④. any方法遍历一个数组是否存在某个元素或者符合某些条件 返回 true false 只要集合里面有满足条件就返回true
var newList = list.any((value){
return value>5;
})
⑤. every方法遍历一个数组是否存在某个元素 或者 符合某些条件 每一个都满足条件返回true否则返回false
var newList = list.every((value){
return value>5;
})
五. 运算符
- 算术运算符 + 、- 、* 、/ 、~/取整 、 %取余、 ++ 、 -- 、 += 、+-
a = b++; 会先把b赋值给a 然后在加 ++ -- 标识自增 自减 1
a = ++b; 把b加上1 在赋值给a
//在赋值运算里面如果++ -- 写在前面 这个时候先运算 在赋值 如果++ -- 写在后面 先赋值后运算
int aaaa = 13;
int bbbb = 5;
print(aaaa+bbbb);
print(aaaa-bbbb);
print(aaaa*bbbb);
print(aaaa/bbbb);
print(aaaa~/bbbb);
print(aaaa%bbbb);
- 关系运算符 == != > < >= <= 二元运算符
print(aaaa == bbbb);
print(aaaa != bbbb);
print(aaaa > bbbb);
print(aaaa < bbbb);
print(aaaa <= bbbb);
print(aaaa >= bbbb);
-
逻辑运算符 ! 取反 && 并且 || 与
- && 并且 条件全部为true 为true 否则false
- || 或者 或 全部为false 为false 否则 true
-
基础赋值运算符 = ??= 赋值
b ??= 23; 表示如果b为空的话把23赋值给b
-
复合赋值运算符 += 、 -= 、 *= 、 %= 、 ~/=
-
三目运算符 它是唯一有3个操作数的运算符,也叫三元运算符。一般来说,三目运算符的结合性是右结合的。
var flag = true;
var ccc = flag ? '正确' : '错误';
- ?? 运算符
var aaa = 222;
var bbb = a ?? 333; //当a的值为空的时候把333赋值给bbb
六. 类型转换
- Number和String类型之间的转换
- Number类型转换为String类型用toSting()
- String转换为Number 用parse 通过int调用parse函数把要转换的字符串传进去
String ahahah = '123';
var nnnnn = int.parse(ahahah);
var nnnnn = double.parse(ahahah);//转换为double
//如果传入的值为空 可以通过 try catch来判断
try{
var nnnnn = int.parse(ahahah);
}catch (err){
print('error');
//如果转换失败会跳到catch中
}
var aaasaa = 222;
aaasaa.isNaN //判断是否为空
print(aaaa.toString());
七. 循环语句 流程控制语句(控制流)
- for循环
- 流程:
- 声明变量int i = 0
- 判断i <= 100
- print(i);
- i++
5.从第二步再来,直到判断为false
for(int i = 0 ; i <= 100 ; i++ ){
print(i);
}
// 打印0到50所有的偶数
for(int i = 0 ; i <= 50 ; i++ ){
if(i % 2 == 0){
print(i);
}
}
//求1+2+3+4+5+++100的和
var sum = 0;
for(int i = 1 ; i <= 100 ; i++ ){
sum += i;
}
//5050;
-
while 循环 和 do while 循环
- 语法格式
- 注意点
- 最后的分号不要忘记
- 循环条件中使用的变量需要经过初始化
- 循环体中,应有结束循环的条件否则会死循环
- while语句可能一次都不执行 do while 肯定会执行一次
- 不同点 第一次循环条件不成立的情况下 while不执行循环体 do while肯定会执行一次
while(表达式/循环条件){
循环体
}
do{
语句/循环体
}while(表达式/循环条件)
-
if else
-
switch case
-
break语句
- 在switch语句中使流程跳出switch结构
- 在循环语句中使流程跳出当前循环,遇到break 循环终止,后面的代码不会执行
-
break语句注意点
1.如果在循环中已经执行了break语句,就不能执行循环体中位于break后的语句。
2.在多层循环中,一个break语句只能向外跳出一层。
3.break可以用在switch case 中也可以用在for循环和while循环中。 -
ontinue语句
- [注]只能在循环语句中使用,使本次循环结束,既跳过循环体重下面尚未执行的语句,接着进行下continue可以用在for循环以及while循环中,但是不建议用在while循环中,不小心容易死循环
八. Dart函数
dart中的函数 函数的定义 可选参数 默认参数 命名参数 箭头函数 匿名函数 闭包等 函数也叫方法 在类外面叫函数 在类内部叫方法 这个都无所谓 都可以叫函数 也可以叫方法
- 自定义方法函数:
自定义方法的基本格式
返回类型 方法名称(形式参数1,形式参数2,......){
方法体 具体执行逻辑
return 返回值;
}
print();//内置方法/函数
- 定义一个带可选参数的方法
String method (String name ,[int age,String sex]){
//形参 可选参数放到参数后面 用[]中括号包裹 用,逗号隔开
}
- 定义一个带默认参数的方法
String method (String name ,[String sex = '男',int age]){
//形参 如果有默认参数 建议放到 不带默认参数的可选参数前面
}
- 定义一个命名参数的方法
String method (String name ,{String sex = '男',int age}){
//参数带名称的参数 需要用大括号包裹{}并且里面也可以设置默认参数
}
- 实现一个把函数当做参数的方法
fn1(){
print('fn1');
}
fn2(fn){
fn();
}
fn2(fn1);
//把方法函数fan1当做另一个方法fan2的形式参数传进去 然后执行
- 匿名函数 没有名字的函数
var fn = (){//没有方法名称
print('我是一个匿名方法');
}//直接通过变量fn调用方法
var printNumm = (){
//表示把一个函数赋值给了printNumm这个变量 调用方式和普通调用方法一样 传值方式和普通方法一样
}
- 箭头函数 箭头函数后面只能写一句代码
list.forEach((valye) => print(value));
list.forEach((value) => {//里面也只能写一句代码
print(value)//不用写分号
})
- 自执行方法 不主动去调用 方法自己去执行
((int n){
//方法在创建好后会自动执行 因为方法后面有括号会直接调用这个方法 可以传入参数 和 接收参数 还可以指定类型
print('我是自执行方法');
})(12);
//就相当于在函数外面包裹了一个函数
- 方法的递归 一个方法可以调用自己 记得写判断语句 当符合条件后跳出 否则死循环
var sum = 1;
fn(int n ){
sum*=n;
if(n == 1){
return;
}
fn(n-1);//关键这句 在符合条件后在此执行当前方法
}
fn(10);
-
闭包:函数嵌套函数,内部函数会调用外部函数的变量或参数.
- 全局变量特点:全局变量常驻内存,全局变量污染全局
- 局部变量的特点:不会常驻内存 会被垃圾回收机制回收,不会污染全局
-
通过闭包可以实现:常驻内存、不污染全局,产生了闭包,闭包可以解决这个问题。
闭包写法:函数嵌套函数,并return 里面的函数,这样就行成了闭包
fn(){
var a = 123; /*不会污染全局 常驻内存 *//
return (){
a++;
print(a);
}
}
print(fn());
九. 类
dart所有的东西都是对象,所有的对象都继承自Object类。是一门使用类和单继承的面向对象语言,所有的对象都是类的实例,并且所有的类都是Object的子类。
一个类通常由属相和方法组成
1. 定义Person类
class Person{//类名首字母大写
String name = '张三';
int age = 23;
//dart里面的构造函数可以写多个但是默认构造函数只能有一个
Person(String name,int age){
this.name = name;
this.age = age;
print('这是构造函数里面的内容,这个方法在实例化的时候触发')
}
//默认构造函数简写
Person(this.name,this.age);
//命名构造函数 可以有多个
Person.now(){
print('我是命名构造函数');
}
void getInfo(){
print("${this.name}--$age");//this指当前类 类似self 通过this.需要用{}大括号包裹
}
}
2. 实例化类
var p1 = Person();//2.0.0后不用new关键字 写也可以 推荐不写
Person p2 = Person();//默认实例化类的时候调用的是默认构造函数
Person p3 = Person.now();//命名构造函数
var time = DateTime.now(); //实例化datetime 调用它的命名构造函数
//dart和其他面向对象语言不一样 dart中没有public private protected这些访问修饰符
但是我们可以使用 “_” 下划线 把一个属性或者方法定义成私有
String _name;//私有属性 私有属性可以通过共有的方法来访问 间接访问私有属性
_run(){//私有方法
print('这是一个私有方法');//也可以通过公有方法来间接调用私有方法
}
alert(){
this._run();//通过公有方法访问私有方法 私有的不能直接访问
}
3. 类中的getter和setter修饰符
get 名称{ //getter方法 也就方法获取数据
return "返回值"
}
set 名称(形式参数){
//参数名称 = 形式参数;
}
4. 类中的初始化列表
dart中我们也可以在构造函数体运行之前初始化实例变量
int height;
int width
Rect():height = 2 , width= 3{//在实例化之前的操作
}
5. 静态成员
dart类中的静态成员:
- 使用static 关键字来实现类级别的变量和函数
- 静态方法不能访问非静态成员,非静态方法可以访问静态成员
- 静态方法成员变量不能在通过类的实例化对象访问 直接通过类来访问
static String name = "zhangsan";
static void show(){
}
void getInfo(){//非静态方法可以访问静态成员以及非静态成员
}
6. dart中的对象操作符
- ? 条件运算符
- as 类型转换
- is 类型判断
- .. 级联操作(连缀)
p1?.方法 如果p1对象不存在会自动返回 如果存在会访问方法
(p1 as Person).方法或者属性 //类型转换 转换为自己需要的类型
Person p1 = Person();
p1..name = "hhhh";
..age = 35;//连缀操作符 访问属性或者方法不用对象名可以直接访问
7. dart中的类的继承
通过super关键字来继承父类的属性和方法
重写父类方法是时@override关键字 建议加 可以不加
super.父类里面的方法 通过super调用父类的方法
- 子类使用extends类关键字来继承父类
- 子类会集成父类中可见的属性和方法 但是不会继承构造函数
- 子类能重写父类的方法getter和setter方法
8. dart中的抽象类 多态 和接口
dart抽象类主要用于定义标准,子类可以继承抽象类,可以实现抽象类的接口
- 抽象类通过abstract关键字来定义
- dart中的抽象方法不能用abstract声明,dart中没有方法体的方法我们称为抽象方法
- 如果子类继承抽象类必须得实现里面的抽象方法
- 如果把抽象类当做接口实现的话必须得实现抽象类里面定义的所有属性和方法
- 抽象类不能被实例化,只有继承它的子类可以
继承抽象类extends和implements关键字的区别
- 如果复用抽象类的方法,并且要用抽象方法约束自类的话我们就要用extends继承抽象类
- 如果只是把抽象类当做标准的话我们就用implements实现抽象类、
接口:就是约定规范
首先dart的接口没有interface关键字定义接口,而是普通类或者抽象类都可以作为接口被实现
同样适用implements关键字进行实现
但是dart的接口有点奇怪如果实现的类是普通类 会将普通类和抽象中的属性的方法全部需要重写一遍
而因为抽象类可以定义抽象方法,普通类不可以,所以一般如果要实现像Java接口那样的方式,一般会使用抽象类。
建议使用抽象类定义接口
abstract class Db{
add();//只写方法不实现 继承它的子类需要实现它的方法属性
}
class Mysql implements Db{
@override
add(){
}
}
dart中一个类实现多个接口 以及dart中的Mixins混入
abstract class a{
add();//只写方法不实现 继承它的子类需要实现它的方法属性
}
abstract class b{
remove();//只写方法不实现 继承它的子类需要实现它的方法属性
}
class implements a,b{
//需要实现上面俩个抽象类的方法和属性 这叫一个类实现多个接口
}
Mixins的中文意思是混入 就是类中混入其他功能,在dart中可以使用mixins实现类似多继承的功能,因为mixins使用条件 随着dart的版本一直在变 这里讲的是dart2.x中使用mixins的条件
- 作为mixins的类只能继承自object 不能继承其他类
- 作为mixins的类不能有构造函数
- 一个类可以mixins多个mixins类
mixins不是继承也不是接口 而是一种全新的特性,mixins的实例类型就是其超类的子类型 c 混入a b 就是ab的子类型。通过with关键字实现class c with a,b{}
c继承了a和b class c extends Person with a,b {}
c继承于Person类同时混入了a b 如果继承的有同样的方法和属性 后面的会覆盖前面的
9. 泛型 泛型方法 泛型类 泛型接口
通俗理解:泛型就是解决 类 接口 方法的复用性,以及对不特定数据类型的支持(类型效验)
泛型一般在方法前面加T 啥都行 说明是泛型
①. 泛型方法
T getDate<T>(T value){
return value;
}
调用:
一般 getDate(123) 这个没有类型校验 传啥返回啥。
类型校验 getData<String>('123213'); 这个String会传给尖括号的T代表这个方法的返回值是String 接收值也是String
②. 泛型类
class Person<T>{
List list = List<T>();
void ad(T value){
this.list.add(value);
}
}
dart中的泛型接口:
- 定义一个泛型接口 约束实现它的子类必须有getByKey(Key) 和 setBuKey(key,value)
2.要求setByKey的时候value的类型和实例化子类的时候指定的类型一致
abstract class Cache<T>{
getByKey(steing key);
void setByKey(string key ,T value);
}
class FlieCache<T> implements Cache<t>{
//继承泛型类 把当前类定义的泛型<T>传给父类的泛型<T>
void setByKey(string key ,T value){
}
}
③. async和await
- 只有async方法才能使用await关键字调用方法,如果调用别的async方法必须使用await关键字
- async是让方法变成异步
- await是等待异步方法执行完成
记录:
函数如果有一行的话可以使用箭头函数 只要超过一行就不能使用箭头函数 (value) => print(value);
dart中所有的类都继承于Object类
$ 符 字符串插值运算符 $+变量名
//引入别的类
import 'package:async/async.dart';
Rune 符号文件 用来表达Unicode字符
Unicode为所有世界写作系统中使用的每个字母、数字和符号定义了唯一的数值。
Unicode采用UTF-32位编码 dart采用了UTF-64编码
为了在字符串中表达32位的Unicode值需要用到特殊语法
\uXXXX 以\u开始后面跟着4个十六进制数(XXXX) , x的取值范围是0到f
var tempStr = '\u0F00';
print(tempStr);
当到指定多于或者少于4个十六进制数字是,使用{}包裹该值
var smeil = '\u{1F600}';
print(smeil);
全局函数 可以在main方法中调用 全局作用域 方法参数被称为形式参数 形参 调用方法传入的参数被称为实际参数 实参
//自定义方法
void printInfo(){
print('我是一个自定义方法');
int getNum(){//方法里面还可以嵌套方法 一个返回值为int的方法 这个方法只能在 当前函数体内调用 局部作用域
var myNum = 111;
return myNum;
}
}