Flutter 开发基础:Dart 基础语法
Dart 是 Google 公司开发的编程语言,最开始的目标是成为下一代 Web 开发语言,设计之初就吸收了其它编程语言的优点,入门还是比较简单的。
特点
- 一切皆对象:包括基本类型(数值、布尔值)、函数都是对象
- 面向接口:没有
final
方法,允许重写除了内置操作符之外的所有方法 - 封装性:外部操作都是使用
get
/set
方法来改变对象的状态 - 类型可选
- 类型在语法层面可选
- 类型对运行时的语义没有影响
- 存在类型不一致,会警告,但不会报错
HelloWorld
1 | void main() { |
变量与类型
Dart 使用 var
关键字定义实例变量,这点上和 JavaScript ES5 定义方式是一致的。如果定义变量,没有赋值,默认为 null
。
Dart 是强类型语言。 如果第一次已经赋值了,之后代码就不能将变量更改为其它类型。JavaScript 作为弱类型语言,则可以随意改变类型。如果使用 Dart 语言,定义一个变量之后需要改变类型,可以使用 dynamic 关键字。
final 与 const
const
和 final
的共同点是初始化后无法更改。区别在于,const
在编译时会检查值,而 final
值在运行时才检查值。
数值类型
Dart 提供了 int
和 double
,但没有 float
类型。
-0.0
和0.0
相等但不相同NaN
(非数值)与自身不相等但相同
字符串类型
创建字符串
'
或者"
- 多行字符串:
'''
或者"""
- 原始字符串:
r
操作
- 运算符操作:
+
、*
、==
、[]
等 - 插值
${name}
- 字符串属性:
length
、isEmpty
、isNotEmpty
等 - 常用方法:
contains()
、subString()
、replaceAll()
、split()
列表类型
1 | // 赋值 |
常用方法:add()
、length()
、remove()
、insert()
、indexOf()
、sublist()
、forEach
、shuffle()
键值对类型
1 | Map map = {'name': 'lin', 'age': '21' }; |
动态类型与 Object
在 Dart 语言中,一切皆对象,而这些对象的父类都是 Object。
在实际的开发中,还是要尽量为变量确定一个类型,这样才能提高程序的安全性,也能加快程序的运行速度。
进行赋值操作的时候,最好使用 is
和 as
进行判断。
1 | dynamic map = {"name": "John", "age": "25"}; |
is
是判断类型的时候使用的;as
是转换类型的时候使用的。is!
与 is
的功能正好相反。
符号字符
Dart 提供的 UTF-32 编码的字符串,可以通过这些编码直接转换成表情包与特定的文字。
1 | Runes input = new Runes('\u2665 \u{1f605} \u{1f60e}'); |
符号
符号用来表示程序中声明的名称,使用 #
作为开头,后面跟着一个或多个用点分隔的符号或运算符。
在实际的项目中,基本用不到这个内置类型。
运算符
三目运算符
三目运算符通常与状态管理结合使用,用于判断 Flutter 组件的状态
1 | var number = null; |
Dart语言也支持常规的三目运算符:
1 | int a = 200; |
取商运算符
~/
是 Dart 语言中的取商运算符,返回一个整数。
1 | int c = 20; |
自定义类操作符
Dart 支持运算符重载,可以自定义运算符操作。
1 | class Point { |
级联操作符
级联操作符非常类似于程序的链式调用。
1 | String fruits = (new StringBuffer() |
get 和 set 方法
在 Dart 语言类的 set
和 get
方法中,不要调用自身类的方法,因为 Dart 语言有层级树的概念,递归调用会导致 Stack Overflow(堆栈溢出)异常。如果属性是公开的,那么,可以直接通过 类.属性
访问,或者通过 类.属性=某值
设置值,这样的调用方法其实就是默认调用了 get
与 set
方法。
如果用 _
将属性设为私有,例如上面的 x
、y
,代码中设置为 var _x, _y
,那么默认的 get
与 set
方法就失效了。
1 | class Point { |
异常捕获
在 Dart 语言中,可以抛出异常或者捕获异常,如果没有捕获异常,就会和其他编程语言一样,程序会终止。与 Java 相比,所有 Dart 异常都是没有被终止的,可以继续传递。
throw
在 Dart 语言中,可以抛出任何类型的异常,并不要求被抛出的对象是某个特殊异常类的实例或子类。在 Dart 语言中,throw
是一个表达式,并不像在 Java 中那样是一个语句。
1 | throw Exception('error'); |
try-catch
try
语句由多个部分组成。首先是一条可能抛出异常的语句,然后是一个或者多个 catch
子句,以及一个 finally
子句,当然也可以省略这两种子句中的某一种。
finally
子句用于定义异常捕获处理结束后需要做什么,不论异常是否发生,finally
子句都会运行。
rethrow
:捕获的异常经检查之后,如果我们发现本地不需要处理异常,并且异常应该在调用链中向上传播,那么可以使用rethrow
,它在catch
子句中将捕获的异常重新抛出。(基本用不到)
循环语句
Dart 支持 3 种形式的循环:
for
while
do-while
for
在 Dart 语言中,同时支持两种传统的 for
循环,分别为 for
循环和 for-in
循环。建议在开发中尽可能使用 for-in
循环。
1 | for(int i in [1,2,3,4]) print(i); |
传统 for
循环:
1 | for(int i = 1;i <= 100;i++) print(i); |
while
1 | int i = 1; |
do-while
1 | int i = 1; |
switch 语句
1 | var name = 'Bob'; |
函数
在 Dart 语言中,一切皆对象,所以函数也是对象,并且函数的对象类型为 Function
。Dart 的函数作为一等公民,可以作为参数传递、保存在变量中,也能作为参数和函数的返回值。
可选参数
在 Dart 语言中,通过 {}
声明可选参数:
1 | void setUser({var name, var age}) { |
使用这个函数的时候,可以不传入参数,也可以只传入 name
或者 age
,或者两者都传入。
必选参数
必选参数有两种定义方法,一种就是直接定义:
1 | void setUser(String name,int age) { |
还可以在引入 package:meta/meta.dart
时使用 @required
。
可选位置参数
在函数的定义中,我们还可以使用 []
定义可选位置参数:
1 | void printSomething(int a, int b, [int c = 99]) { |
默认参数
1 | void setUser({String name: "zhao"}) { |
函数作为参数传递
Dart 的函数可以作为参数传递。
1 | List list = [1, 2, 3]; |
也可以传入匿名函数:
1 | list.forEach((element) { print(element); }); |
函数作为变量
1 | List list = [1, 2, 3]; |
级联
函数也是对象,所以它也可以使用级联操作。
级联操作返回的还是对象,并不是返回值:
1 | print("Hello".length.toString()); // 5 |
异步编程
大多数 Flutter 项目是并发的。Dart 与 JavaScript 语言有一个共同点,它们都是单线程的。Dart 语言与 JavaScript 语言有一个共同点,它们都是单线程的,如果代码中直接出现同步代码,就会阻塞线程。
Future
Future
表示未来或者将来,也就是说,它代表将来运算结果的对象,结果可能在未来某个时刻知道(可以理解为先占一个位置,后面等到结果就填进去)。
Future
本身也是一个泛型对象,程序中大多不会单独使用 Future
,而是使用 Future<T>
,运算返回的结果对象就是 T
。如果返回结果不可用,或者没有返回结果,Future
类型就会是 Future<void>
。
在 Dart 语言中,Future
由 dart:async
库提供,如果返回 Future
函数,将会发生以下两件事情。
- 这个函数加入待完成的队列并且返回一个未完成的
Future
对象。 - 当这个操作结束时,
Future
会返回一个值或者返回错误。
当然,单独使用 Future
的情况非常少,往往需要搭配 then()
方法使用。then()
方法接收一个 onValue
闭包作为参数,该闭包在 Future
成功完成时被调用。
async 和 await
Future
开始工作后,有成功处理、错误处理,以及后续的任务处理,任务相当繁重。为了减轻使用异步操作的工作量,Dart 语言为异步函数提供了 async
关键字。函数体可以使用 async
进行操作。注意,await
必须被包裹在 async
里面。
1 | int frac(int n) { |
抽象方法和抽象类
在 Dart 语言中,如果简单地声明一个方法而不提供它的实现,这种方法被称为抽象方法。一个抽象方法本身属于一个抽象类,抽象类与 Java 语言一样都是通过 abstract
关键字进行声明的。
1 | abstract class Point { |
在 Dart 语言中,抽象类同样不能被实例化,因为它缺失部分实现。对抽象类进行实例化,会产 abstractClassInstantiationError
错误,Dart 解析器也会发出警告。
接口
在 Dart 语言中,每个类都隐含地定义了一个接口,此接口描述了类的实例具有哪些方法。不过,Dart 语言虽然有接口,但没有接口声明。Dart 语言的设计者在设计之初就觉得这是不必要的,因为我们始终可以定义一个抽象类来描述所需的接口,使用过 Java 的开发人员应该很清楚这一点。
换句话说,Dart 语言并不关心对象是如何实现的,而只在意它支持哪些接口(面向接口)。
1 | abstract class Point { |
继承
Dart 语言和 Java 语言一样都是支持继承操作的。不过,在 Flutter 中的继承只能是单继承。
当继承一个类之后,子类不仅可以通过 @override
关键字来重写父类的方法,还可以使用 super
来调用超类中的方法。不过,需要注意的一点是构造方法不能被继承。另外,Dart 语言中也没有公有与私有的访问修饰符,所以子类可以访问超类中的所有方法与属性。
1 | abstract class Point { |
mixin
mixin 的出现是为了解决多继承问题。假如有一个 Widget 包含很多个子 Widget,那么它肯定会继承 Collection 集合,同时它是一个组件,所以它肯定也会继承 Widget。这样就会导致同一个类继承多个父类。前面已经说了,Dart 不能多继承。
mixin 是一个可以把自己的方法提供给其他类,而不用成为其父类的类。它以非继承的方式来复用类中的方法。在 Dart 语言中使用 mixin 时需要用到关键字 with
。
如果你继承某个抽象类,那么你就必须重写其方法,而对于通过 mixin 混入的类,不必强制重写其方法。
1 | abstract class Animal { |
泛型
1 | class Animal{} |
库
导入库出现命名冲突可以进行重新命名:
1 | import 'package:http/http.dart' as htp; |
如果需要显示 / 隐藏某些成员,可以使用 show
/ hide
。
1 | import 'package:http/http.dart' show http; |