接近半年没有在简书冒泡了。这段时间一是忙于使用云信IM开发相应项目,二是整理和收集相关Flutter的相关资料进行学习。国内关于Flutter的资料还是太过于稀少,以至于我只能去YouTube和Udemy上看英文版视频去学习,其间的感受可想而知。。。不过不可否认的是,有些视频讲解的内容还是很好的,不是那种简简单单的零基础学习。
言归正传,我们还是开始今天的学习。
相关代码已经上传到github,欢迎大家star、follow
Dart语言是什么
Dart是谷歌开发的计算机编程语言,亮相于2011年10月10至12日在丹麦奥尔胡斯举行的GOTO大会上。2015年5月Dart开发者峰会上,亮相了基于Dart语言的移动应用程序开发框架Sky,后更名为Flutter。
为什么Flutter会选择Dart?我觉得主要基于以下三点:
a) 热重载。我想每个Android开发者都对漫长的编译时长感到很无奈,往往选择上厕所或者倒杯水舒缓一下心情。但是Dart不一样,直接给你一个热重载,瞬间就能看到效果。虽然之前阿里也出过Android开发热重载的相关插件Freeline,但是效果不仅一般,而且插件本身开发难度较高,导致项目停摆1年多,已经失去使用意义。所以总的来说,Dart的热重载绝对是一个跨时代的功能
b) Dart可以在没有锁的情况下进行对象分配和垃圾回收。就像JavaScript一样,Dart避免了抢占式调度和共享内存(因而也不需要锁),所以Dart可以更轻松地创建以60fps运行的流畅动画和转场。这个在你使用Dart开发出App之后必定会深有感触
c) Dart语言特别容易学习。Dart语言与Java语言非常类似,这也是本篇为什么以“给Java开发者”来开篇的原因。
话不多说,开始学习吧。
开始
1. Dart的一个简单例子
这是使用Dart语言来实现最基础的计算器加减乘除功能的范例。我们先宏观的了解一下Dart语言大体风格。
void main() {
print("加法结果为:${operation(10, 5, add)}");
print("减法结果为:${operation(10, 5, sub)}");
print("乘法结果为:${operation(10, 5, multi)}");
print("除法结果为:${operation(10, 5, div)}");
}
int add(int a, int b) {
return a+b;
}
int sub(int a, int b) {
return a-b;
}
int multi(int a, int b) {
return a*b;
}
int div(int a, int b) {
return a~/b;
}
int operation(int a, int b, int method(int a, int b)) {
return method(a, b);
}
如果你之前学习过Java或者Kotlin语言(当然,部分Dart语法可能与其他语言类似,不过我没学过,不在此深究),那么Dart给你的第一感觉便是:哇,这个看上去似曾相识
Dart语言的基本类型也是诸如int、String等寻常关键字。同Kotlin一样,也可以使用${表达式}将表达式放入字符串中。Dart的访问控制符没有显式的展现出来,这个我们会在稍后进行详细介绍
同Kotlin一样,在Dart中我们可把一个函数当做一个变量传入到另外一个函数中
只要你有程序开发基础,我想上面的范例对你来说应该没啥问题。下面我们从细节上开始来详细了解Dart语言吧,这里我有一个声明:过于基础的知识点我只会一笔带过,时间应该花费在重要的知识点上
2. 变量、类型
Dart的几种内置的数据类型如下:数值型- num
、布尔型-bool
、字符串-String
、列表-List
、键值对-Map
、其他类型-Runes
、Symbols
。而数值型仅有int
与double
,不像其他语言分的很细
num num1 = 1;
num num2 = 1.1;
int int1 = 1;
double double1 = 1.1;
如果变量使用num
进行声明,则可以随意在使用中转换为int
或double
类型;但如果使用int
或者double
进行明确的声明,那么就不能随意转换了
num1 = 1.1;
num2 = 1;
// int1 = 1.1; 错误
除了以上所说的数据类型,Dart还有var
、dynamic
与const
三种数据类型var
可以用来声明任意类型,Dart会根据其被赋予的数值的数据类型进行自动推导
var var1 = "String";
var var2 = 1;
var var3 = 1.1;
var var4 = true;
如果你仅使用var
声明一个变量但是并未对其进行赋值,那么你可以在使用过程中将其更改为任意数据类型的值
var var5;
var5 = 1;
var5 = 1.1;
但如果你使用var
声明变量的时候已经对其赋予指定数据类型的值,那么其数据类型就不可以更改了,因为此时已经决定了它是什么类型。var
修饰的变量一旦被编译,则会自动匹配var变量的实际类型,并用实际类型来替换该变量的申明
var var6 = 1;
// var6 = 1.1; 错误
dynamic
被编译后,实际是一个Object
类型,只不过编译器会对dynamic
类型进行特殊处理,让它在编译期间不进行任何的类型检查,而是将类型检查放到了运行期
dynamic dynamic1 = 1;
dynamic1 = 1.1;
dynamic1 = "";
dynamic1 = true;
正因为类型检查放到了运行期,所以在使用dynamic
的时候需要倍加小心
// dynamic1++; 错误,编译期可以通过,但是运行时报错
再来看看const
。Dart中有两种数据常量数据类型,const
和final
。与final
不同的是,const
是编译时常量。什么是编译时常量,来看这么个例子
在String
类里面有一个判断其是不是为空的函数isEmpty
。在代码编译过程中是没法知道字符串""是不是为空的,只有当代码运行到此行之后,才能通过isEmpty
函数的调用知道。所以const
就不满足这一类型的常量声明,只能使用final
final int int3 = "".isEmpty ? 10 : 6;
// const int int4 = "".isEmpty ? 10 : 6; 错误
同理,下面这个例子就可以。因为代码在编译期就知道8是肯定大于6的,所以这个值就能被确定下来
const int int4 = 8 > 6 ? 8 : 6;
可以使用const
进行初始化的对象也可以使用final
进行赋值,反之则不行
final int5 = 8 > 6 ? 8 : 6;
任何数据类型,包括int
或者double
这种看似基本类型的,如果没有赋默认值,Dart都会用null
来作为其默认值。这是不是比Java还要面向对象?
int int7;
print(int7);
这里补充一个不相干的知识点。
print
可以打印任意
Object
类型的对象,
String
、
double
、
int
、
bool
的父类都是
Object
。
print("Hello World");
print(3);
print(1.1);
print(true);
print(null);
Dart中操作符大体上与其他语言差不多,这里只介绍一些不常见的操作符
-
~/
。Java里面如果int/int
的话,得到的也是int
,这就是通常所说的取整。如果要得到浮点型数值的结果,则需要将其中一个数值变成浮点型数值才行,但是Dart不需要这样
var double2 = 7 / 3;
print("double2:$double2");
有什么办法可以得到int
呢?那就需要用到~/
了
var int2 = 7 ~/ 3;
print("int2:$int2");
-
as
强转操作符。这个跟Kotlin是一样的。这边要是转换格式不匹配,则会报错
// int int8 = num1 as int; 运行时报错
double double3 = num1 as double;
所以在使用之前最好判断一下
if (num1 is int) {}
-
??=
。空赋值操作符
int int9;
int9 ??= 11;
print("int9:$int9");
- ??运算符。如果前者不为空,返回前者;否则返回后面的
int int10;
int int11 = int10 ?? 11;
print("int11 = ${int11}");
-
?.
。类似于Kotlin的非空判断
int a10;
print(a10?.toString());
a10 = 10;
print(a10?.toString());
- 级联符号
..
允许您在同一个对象上进行一系列操作。 除了函数调用之外,还可以访问同一对象上的字段。其实相当于Java的链式调用
CircleShape shape = new CircleShape()
..radius = 3
..color = 1;
Dart里没有private
/protected
/public
等权限修饰符,这就意味着默认情况下函数或者常量、变量都是可访问的。但是Dart还是有私有权限设置的办法的,只需要将需要修饰的函数或者常量、变量加上_
前缀即可。但是这里比较坑的一点是,_
并不是从class级别去限制,而是从package级别去限制
在同一个包下面创建一个私有属性
class PrivateTest {
String _private = "private";
}
使用正常
print(PrivateTest()._private);
将该类移至到其他包下
// print(PrivateTest2()._private2); 错误,无法访问private
3. 控制流和异常
这个很简单,不多啰嗦了
int age = 30;
if (age < 30) {
print("young");
} else if (age > 33) {
print("old");
} else {
print("Just so so");
}
for (int i = 0; i < 7; i++) {
print(i);
}
var list2 = <String>["1", "2", "3", "4"];
for (String value in list2) {
print(value);
}
int a3 = 0;
while (a3 < 10) {
print("End?");
a3++;
}
do