Flutter基础-Dart基础语法
来源: https://www.youtube.com/channel/UCW5YeuERMmlnqo4oq8vwUpg
作者: The Net NinJa
视频看完了,今天内容有点儿多,但是今天的内容是后面学习Flutter的基础。如果把开发Flutter应用比喻成盖楼的话,今天的知识就是那些砖。一定要把今天的内容掌握牢,后面学起来才会更轻松。不过我保证,在这个系列中,只有今天的内容是最多的,所以大家一定要耐住性子把今天的东西学完。后面只会学起来越来越轻松~开始吧!
在开始讲变量之前,要先给大家看下变量的声明,举三个例子。
var name = 'Cyy'
这行代码的意思就是,我声明了一个变量,名字是name,然后给他了一个初始值Cyy。
再看下一个
dynamic name = 'Cyy';
这行代码的意思也是,我声明了一个变量,名字是name,然后给他了一个初始值Cyy。
我们继续看下一个
String name = 'Cyy';
这行代码的意思还是声明了一个变量,名字是name,然后赋值为Cyy。
但是!他与上面两个的区别是,我在这里给他声明了变量的类型,就是我指定了这个name是字符串类型的。上面两个都没有给变量指明类型,那么问题来了!
var和dynamic的区别是什么呢?
var 初始化确定类型后不可更改类型,dynamic 可以更改类型
简单说,就是如果你一开始给var的变量初始化了一个值,Dart会去推断这个值,如果是什么类型,那这个变量以后就是什么类型了,不能再改变了。但是 dynamic还是可以更改滴。
现在再来看看这三个东西吧~
在Dart中,所有的类型都是对象,未初始化的变量的初始值为null。甚至Number类型的变量最初都为null,怎么理解呢,就像java里的包装类,Integer , Double 等等。
final name = 'Cyy';
final String nickname = 'XiaoFo';
简单来说,被final声明过的变量,后面你就无法再更改它的值了,所以final修饰的变量必须初始化,且只能在初始化时赋值一次.
举个例子:
void main() {
final name = 'Cyy';
final String nickname = 'XiaoFo';
name = 'Cyy513'
}
运行结果: Error: Can't assign to the final variable 'name'.
它不会让我再去给他赋值,因为我声明它是final的变量,只允许被进行一次赋值。
在基础课,我们只需要知道const和final一样也只允许被赋值一次,后面不能再更改。他主要用来创建常量值、声明创建常量值的构造函数。后面用到的时候,会给大家细讲,基础阶段先暂时知道这些就可以。
常用的变量类型
Number对象包含两个子对象,分别是int和double。int在不同平台位数不同,但是最大就64位。在Dart VM上,他的值从-263 到 263 - 1。这个数字啥意思,就是说int允许的最大值和最小值。double学名是双精度浮点数,初学者的话,可以这么理解,整数是不带小数点儿的,double是带小数点儿的。
怎么声明一个int型变量和double型变量呢?这样就可以了
int a = 2;
double b = 1.1;
就是你声明的变量是一个字符串,举个例子应该就都明白了。
String cyy = "hello Cyy"
也是非常简单,但是他有很多种用法:
var sOne = 'Hello';
var stwo = 'Cyy';
var sThree = sOne + stwo; // 输出结果:HelloCyy
var sFour = '${sOne}ABC'; //HelloABC
var sFive = '$sOne $stwo'; //Hello Cyy
var sSix = '''
You can create
multi-line strings like this one.
''';
sSix的输出结果为:
You can create
multi-line strings like this one.
-------------------------------------
var sSeven= """This is also a
multi-line string.""";
sSeven的输出结果为:
This is also a
multi-line string
会这几个就够用了~
布尔类型,只允许两种值,true和false.
任何编程语言都有集合,而Dart 的集合是List。
//创建一个int类型的list
List list = [1, 2, 3];
// 输出[1, 2 3]
print(list);
有两种方式可以创建List
方法一:
// 使用List的构造函数
var listOne = new List();
// 添加元素
listOne.add('Mario');
// 添加多个元素
listOne.addAll(['chun-li', 'jack']);
------------------------------------------
//也可在括号里添加数字,表示List固定长度,但是不能进行添加 删除操作
var listTwo = new List(10);
如果执行了:listTwo.add('Mario');
输出结果:Uncaught Error: Unsupported operation: add
------------------------------------------
List listThree = ['Cyy', 'XiaoFo', 'Bob'];
// 添加多个元素
listThree.addAll(listOne);
// 输出:[Cyy, XiaoFo, Bob, Mario, chun-li, banans]
print(listThree);
// 获取List的长度
print(listThree.length);
// 获取第一个元素
print(listThree.first);
// 获取元素最后一个元素
print(listThree.last);
// 利用索引获取元素
print(listThree[0]);
--------------------------------------------
List还有其他几种有趣的玩儿法
第一种:您可以使用运算符(...)将列表的所有元素插入另一个列表
var list = [1, 2, 3];
var list2 = [0, ...list];
print(list2);
输出结果:[0, 1, 2, 3]
第二种:
/*
如果扩展运算符右边的表达式可能为空,
则可以使用可识别空值的扩展运算符(...?)避免出现异常
*/
var list;
var list2 = [0, ...?list];
print(list2);
输出结果:[0]
第三种:还可以使用for循环添加元素到集合中
var nav = [
'Home',
'Furniture',
'Plants',
if (true) 'Outlet'
];
print(nav);
输出结果:[Home, Furniture, Plants, Outlet]
List 是有序的集合,那Set就是无序的集合。
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
//创建一个空Set集合
var names =
{}; //添加单个元素
names.add('fluorine');
//添加多个元素
names.addAll(halogens);
map是将键和值相关联的对象。键和值都可以是任何类型的对象,Map的键是唯一的。
//键可以是整型也可以是字符串
var gifts = Map();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';
var nobleGases = Map();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';
//使用.length来获取映射中的键值对的数量
var gifts = {'first': 'partridge'};
print(gifts.length)
// 指定键值对的参数类型
var myMap = new Map
(); // 检索Map是否含有某Key
myMap.containsKey(1);
//删除某个键值对
myMap.remove(1);
// String -> int
var one = int.parse('1');
// String -> double
var onePointOne = double.parse('1.1');
// int -> String
String oneAsString = 1.toString();
// double -> String
String piAsString = 3.14159.toStringAsFixed(2);
举一个函数的例子,你应该就知道什么是参数了。
// 调用函数时,可以使用paramName:value指定命名参数,例如:
enableFlags(bold: true, hidden: false);
// 定义函数时,使用{param1,param2,…}指定命名参数,
// bold和hidden就是参数,bool是他们的类型
void enableFlags({bool bold, bool hidden}) {...}
// 尽管命名参数是一种可选参数,但是您可以使用@required对其进行注释
// 以指示该参数是强制性的-用户必须为该参数提供一个值
// 如果用户不传,就会报错!
const Scrollbar({Key key, @required Widget child})
//在[]中包装一组功能参数会将其标记为可选参数,就是可以传,也可以不传
// 例如:
String say(String from, String msg, [String device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
可以这么调用:say('Bob', 'Howdy', 'smoke signal')
也可以这么调用:say('Bob', 'Howdy')
您的函数可以使用=来定义命名参数和位置参数的默认值。默认值必须是编译时常量。如果未提供默认值,则默认值为null。举个栗子:
void enableFlags({bool bold = false, bool hidden = false}) {
}
调用:enableFlags(bold: true); //bold是true hidden是fals
可选参数使用默认值:
String say(String from, String msg,
[String device = 'carrier pigeon', String mood]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
if (mood != null) {
result = '$result (in a $mood mood)';
}
return result;
}
调用:say('Bob', 'Howdy')
输出结果为:Bob says Howdy with a carrier pigeon
------------------------------------------------
您还可以将列表或地图作为默认值传递,例如:
void doStuff(
{List
list = const [1, 2, 3], Map
gifts = const { 'first': 'paper',
'second': 'cotton',
'third': 'leather'
}}) {
print('list: $list');
print('gifts: $gifts');
}
调用:doStuff()
输出结果:
list: [1, 2, 3]
gifts: {first: paper, second: cotton, third: leather}
是不是很神奇,希望大家回去后,都自己去亲自敲一遍。
每个应用程序都必须有一个顶层main()函数,它可以作为应用程序的入口点。该main()函数返回void,视频中也说了,void就是不返回任何值。并具有List
void main() {
//在这里写你的逻辑
}
正常的函数是这样的
void setName(String name){
}
是这样的
String getName(){
}
匿名函数是这样的
(){
}
这是啥?像不像一个人没有头?
但是他跟普通函数一样,能传各种参数,普通函数能传啥参数就,他也能传。
那他平时咋用呢?再举个例子:
void main() {
var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
print('${list.indexOf(item)}: $item');
});
}
打印结果:
0: apples
1: bananas
2: oranges
这个item叫啥都行,你传个a进去,效果是一样的。
老师在视频中也说了,如果该函数仅包含一个语句,则可以使用箭头符号将其缩短。那么上面那个代码就可以改成:
void main() {
var list = ['apples', 'bananas', 'oranges'];
list.forEach(
(item) => print('${list.indexOf(item)}: $item'));
}
效果是一样的。
foo(){
}
所有函数都返回一个值。如果未指定返回值,则语句返回null;
void就是没有返回值
Dart 是静态作用域语言,变量的作用域在写代码的时候就确定过了。基本上大括号里面定义的变量就 只能在大括号里面访问,和 Java 作用域 类似。举个例子:
var topLevel = true;
main() {
var insideMain = true;
myFunction() {
var insideFunction = true;
nestedFunction() {
var insideNestedFunction = true;
}
}
}
nestedFunction可以访问:
topLevel,insideMain,insideFunction,insideNestedFunction 4个变量
他拥有最高权限。
myFunction可以访问:
topLevel,insideMain,insideFunction 3个变量
main可以访问:
topLevel,insideMain 2个变量
思考一下:如果main访问了insideFunction 会提示什么?
答案:会提示"找不到insideFunction";
概念太抽象了,但是我还是要写一下,哈哈, 我好幽默。
维基百科上对闭包的解释就很经典:在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。
函数可以关闭周围范围中定义的变量。
在以下示例中,makeAdder()捕获变量addBy。
无论返回的函数到哪里,它都会记住addBy。
Function makeAdder(num addBy) {
return (num i) => addBy + I;
}
void main() {
// 引用了自由变量2
var add2 = makeAdder(2);
// 引用了自由变量4
var add4 = makeAdder(4);
print(add2(3));
print(add4(3));
}
运行结果:
5
7
这个要多品品~不懂的一定要来群里问,大家一起讨论,这个一定要懂!
Dart支持if语句和可选的else语句,条件判断语句中必须是布尔值,举个例子。
if (isRaining()) {
//如果isRaining()返回true,则走这里
you.bringRainCoat();
} else if (isSnowing()) {
//如果isRaining()返回false,isSnowing()返回true,则走这里
you.wearJacket();
} else {
//如果isRaining()返回false,isSnowing()返回false,则走这里
car.putTopDown();
}
var message = StringBuffer('Dart is fun');
for (var i = 0; i < 5; i++) {
message.write('!');
}
pritn(message);
输出结果:Dart is fun!!!!
List和Set之类的可迭代类也支持for-in形式的迭代
var collection = [0, 1, 2];
for (var x in collection) {
print(x); //输出结果 0 1 2
}
while:只有测试条件成立,才会去执行循环体中的语句,否则跳出循环。
// 只有isDone()返回false时,才会去执行doSomething
while (!isDone()) {
doSomething();
}
do-while:
只有循环体中的语句被执行后,才去测试循环条件,
只有循环条件成立,就继续执行下去,不成立就跳出循环。
//先去执行一次printLine(),再去判断atEndOfPage()是否返回false
do {
printLine();
} while (!atEndOfPage());
使用break终止循环
while (true) {
if (shutDownRequested()) break;
processIncomingRequests();
}
使用continue跳出当前循环,进行下一次循环
for (int i = 0; i < candidates.length; i++) {
var candidate = candidates[i];
if (candidate.yearsExperience < 5) {
// 如果candidate.yearsExperience<5
// 不执行candidate.interview();继续开始下一次循环
continue;
}
candidate.interview();
}
看个例子就明白了。
var command = 'OPEN';
switch (command) {
case 'CLOSED':
executeClosed();
break;
case 'PENDING':
executePending();
break;
case 'APPROVED':
executeApproved();
break;
case 'DENIED':
executeDenied();
break;
case 'OPEN':
executeOpen();
break;
default:
executeUnknown();
}
相当于:
if(command=='OPEN'){
}else if(command=='CLOSED'){
}...后面省略
但是要强调几点:
1. case后面如果省略了break,他就跳不出这个循环,会走到下一个case下面.
2. case子句可以声明局部变量,这些局部变量仅在该子句的范围内可见
3. Dart的case里面,逻辑可以是空,例如:
var command = 'CLOSED';
switch (command) {
case 'CLOSED': // Empty case falls through.
case 'NOW_CLOSED':
// Runs for both CLOSED and NOW_CLOSED.
executeNowClosed();
break;
}
在开发过程中,使用断言语句— assert(); 如果布尔条件为false,则中断执行;
对象具有由函数和数据(分别为方法和实例变量)组成。调用方法时,您可以在对象上调用它,该方法可以访问该对象的功能和数据。
var p = Point(2, 2);//Point是一个类
// 给Point类中的y赋值为3
p.y = 3;
// 打印p.y
print(p.y);// 3
采用 ?.代替 .为了避免p为null时发生异常
p?.y = 4;
您可以使用构造函数创建对象。构造函数名称可以是ClassName或ClassName.identifier。
class Point {
double x, y;
//这个就是Point这个对象的构造函数
Point(double x, double y) {
this.x = x;
this.y = y;
}
}
this关键字指向了当前类的实例, 上面的代码可以简化为
class Point {
num x;
num y;
// 语法棒棒糖
Point(this.x, this.y);
}
-----------------命名构造函数--------------------------------
命名构造函数:是在初始化时可直接使用类调用
class User {
String name;
int age;
//声明一个命名构造函数
User.getInfo(String name, int age){
this.name = name;
this.age = age;
print("命名构造函数");
}
}
//调用命名构造函数
User u = User.getInfo();
-----------------使用构造函数创建对象--------------------------
例如,以下代码使用Point()和Point.fromJson()构造函数创建Point对象
var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
也在构造函数名称之前使用可选的new关键字创建对象
var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});
创建构造函数还有很多种方法,这里就不一一列举了,这里只列举了两种,总共大概有八九种,大家可以去网上看下,很多这样的文章,写的都很好。我们继续看下面的。
获取对象的类型
要在运行时获取对象的类型,可以使用Object的runtimeType属性,会返回Type对象。
print('The type of a is ${a.runtimeType}');
方法是提供对象行为的函数,可以通过他访问对象的变量。
class Rectangle {
num left;
num top;
num width;
num height;
Rectangle(this.left, this.top, this.width, this.height);
// 定义两个计算属性: right and bottom.
num get right => left + width;
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
}
main() {
var rect = new Rectangle(3, 4, 20, 15);
print(rect.left);//调用了隐含的getLeft 所以是3
rect.right = 12;//执行了上面right方法
print(rect.left);//这里left = 12-20 这块儿是个匿名函数
}
运行结果:3 -8
这个例子多看一会儿,思考一下。
Instance , getter 和 setter 方法可以是抽象的,也就是定义一个接口,但是把实现交给其他的类。要创建一个抽象方法,使用分号(;)代替方法体。
abstract class Doer {
// 定义实例变量和方法
void doSomething(); // 定义一个抽象方法。
}
class EffectiveDoer extends Doer {
void doSomething() {
// 提供一个实现
}
}
!*****抽象类是不能被实例化的****!这句话很重要,需要被注意到,还需要细品
下面的类不是抽象类,因此它可以被实例化,即使定义了一个抽象方法
class SpecializedContainer extends AbstractContainer {
// 定义更多构造函数,域,方法
void updateChildren() {
// 实现 updateChildren()
}
// 抽象方法造成一个警告,但是不会阻止实例化。
void doSomething();
}
每个类都隐式地定义一个接口,该接口包含类的所有实例成员及其实现的任何接口。如果您想创建一个类A,它支持类B的API而不继承B的实现,那么类A应该实现B接口。
简单的说,当我们定义了一个类的时候,同时就会产生一个和此类同名的接口,而且此接口包含了我们定义的类中所有的方法,以及它的成员变量。
//定义一个父类Vehicle
class Person{
num age = 0 ;
Person(){
print("super Constructor") ;
}
Person.get(){
print("super Run") ;
}
Person.create(){
print("super create") ;
}
void run(){
print("person run") ;
}
void printAge(){
print("age = > $age") ;
}
}
怎么用?
class Cyy implements Person{
@override
void run() {
//重写了run方法
print("Cyy running");
}
@override
void printAge() {
print("Cyy age = $age") ;
}
//覆盖(实现)成员变量
@override
num age;
}
使用 extends 创建一个子类,同时 supper 将指向父类
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
}
当实例化SmartTelevision这个类后,调用turnOn()方法时会执行:
_illuminateDisplay(),_activateIrSensor(),
_bootNetworkInterface(),_initializeMemory(),
_upgradeApps() 5个方法
枚举类型,通常被称为 enumerations 或 enums ,是一种用来代表一个固定数量的常量的特殊类。
声明一个枚举类型需要使用关键字 enum
enum Color {
Mario,
chun-li,
Rose
}
要得到枚举列表的所有值,可使用枚举的 values 常量
例如:Color.values
因为我们课程是基础课程,所以Mixins这里就暂时先不讲了,如果后面有需要,我们会单独写一片文章来讲解。但是我会把我觉得写的比较好的文章,放到今日推荐里,感兴趣的同学可以去看看。
算数运算符,这里就不一一列出来了,大家看下方推荐文章,官方文档里有写,如果有不懂的,可以下方留言或者加群,问我们,肯定知无不言。
等号和关系运算符
要测试两个对象x和y是否表示同一事物,请使用==运算符。(在极少数情况下,您需要知道两个对象是否是完全相同的对象,请改用same()函数)
as is is!
主要是在运行过程中,用来检查类型的。
当且仅当您确定对象属于该类型时,才使用as运算符将对象转换为特定类型
(emp as Person).firstName = 'Bob';
也就是说,只有你确定emp是Person类型时,才能这么用。
不然会报错:TypeError: "emp": type 'JSString' is not a subtype of type 'Person'
----------------------------------------------------------------------------
如果您不确定该对象的类型为T,使用该对象之前使用is检查类型。
if (emp is Person) {
// Type check
emp.firstName = 'Bob';
}
// 把value 赋值给 a
a = value;
// 如果b为null,则为b赋值;否则,b保持不变
b ??= value;
condition ? expr1 : expr2
如果条件为true,则返回expr1否则返回expr2。
expr1 ?? expr2
如果expr1不为null,则返回expr1否则返回expr2。
看一个例子就明白了,暂时知道怎么用就行.
class Person {
var name;
var age;
Person(this.name, this.age);
getUserInfo() {
print("${this.name},${this.age}");
}
}
void main() {
var p = new Person('Cyy', 20);
p.getInfo();
//..为级联操作,可以同时赋值执行方法
p
..name = "Mario"
..age = 30
..getInfo();
}
运行结果:
Cyy,20
Mario,30
好啦,今天的内容实在太多了,但我认为只要好好读,一定能学懂的。如果后面的课程遇到了一些问题,那就说明今天的内容,你没有完全掌握。本篇内容需要反复的阅读,最好每个代码都尝试着跑一遍。因为这个课程是入门课,所以一些,泛型,异步的东西,这里没有讲,感兴趣的同学可以自己去看下,后面的老师也会讲到。下篇我们就要开始Flutter征程啦~期待吧!
本文内容有点儿多,建议大家使用浏览器查看。
作业:把文章中的代码,在DartPad中敲一遍,DartPad有国内特供版。
地址:https://dartpad.cn/
学习完成别忘了打卡哦~,大家一起学习交流才更有趣。在下方评论留言 “打卡第*天”,大家互相监督,一起进步吧!
亦可加微信:wtr513 备注: "Flutter 技术交流", 进群和大家一起学习。
今日推荐文章:
Dart官网文档:
https://dart.dev/guides/language/language-tour#methods 【官方文档】
Flutter学习指南-熟悉Dart语言:
https://juejin.im/post/5bcc8527f265da0aff17729a 【玉刚说】
我是刚哥老铁粉了~~
Dart之Mixin详解:
https://blog.csdn.net/lmh_19941113/article/details/99762712【写的很棒】