上一篇介绍了跨平台移动开发解决方案Flutter以及Flutter开发环境的搭建,因为Flutter开发使用的是Dart语言,故本篇记录的是Dart语言的语法基础,但愿跟小伙伴们一块儿温故知新。html
Dart是Google推出的一门编程语言,最初是但愿取代Javascript运行在浏览器端,后来慢慢发展成能够开发Android、iOS和Web端App的一门高质量的编程语言,目前Dart的版本是Dart2,官网是:www.dartlang.org/react
在Dart官方网站上,对于Dart的描述以下:git
Developers at Google and elsewhere use Dart to create high-quality, mission-critical apps for iOS, Android, and the web. With features aimed at client-side development, Dart is a great fit for both mobile and web apps.github
Google和其余地方的一些开发者使用Dart语言为Android、iOS和web构建高质量,关键任务的应用程序,针对客户端开发的特色,Dart很是适合移动和Web应用程序。web
Dart’s syntax is clear and concise, its tooling simple yet powerful. Sound typing helps you to identify subtle errors early. Dart has battle-hardened core libraries and an ecosystem of thousands of packages.编程
Dart的语法清晰明了,工具简单但功能强大。Sound typing有助于早期识别细微的错误。Dart拥有久经沙场的核心库和数以千计的生态系统。segmentfault
Dart provides optimizing ahead-of-time compilation to get predictably high performance and fast startup across mobile devices and the web.浏览器
Dart提供提早优化编译,以在移动设备和Web上得到可预测的高性能和快速启动。bash
Dart compiles to ARM and x86 code, so that Dart mobile apps can run natively on iOS, Android, and beyond. For web apps, Dart transpiles to JavaScript.网络
Dart可编译成ARM和X86代码,这样Dart移动应用程序能够在iOS、Android和其余地方运行。对于Web应用程序,DART可编译成JavaScript。
Dart is familiar to many existing developers, thanks to its unsurprising object orientation and syntax. If you already know C++, C#, or Java, you can be productive with Dart in just a few days.
Dart对于许多现有的开发人员来讲是熟悉的,这得益于其使人惊讶的对象定位和语法。若是你已经知道C++,C语言,或者Java,你能够在短短几天内用Dart来开发。
Dart is well-suited to reactive programming, with support for managing short-lived objects—such as UI widgets—through Dart’s fast object allocation and generational garbage collector. Dart supports asynchronous programming through language features and APIs that use Future and Stream objects.
Dart很是适合于反应式编程,支持经过Dart的快速对象分配和代垃圾收集器来管理诸如UI小部件之类的短命对象。Dart经过使用将来和流对象的语言特征和API支持异步编程。
关于Dart的语法,若是你熟悉Java,应该很快能掌握Dart,官网上对于Dart的语法也有详细介绍,不过是全英文的,若是对英文没有什么阅读障碍,能够直接移步官方文档。
为了了解Dart的语法基础,这里咱们使用Android Studio做为开发工具(你也可使用dartpad来运行代码,它是一个基于浏览器的dart运行时环境),若是你按照上一篇文章中搭建好了Flutter开发环境,那么能够直接在Android Studio中新建Flutter项目,以下图所示:
lib/main.dart
文件中,因为本篇主要是讲Dart的语法,故暂时不看main.dart文件,在lib目录下咱们建立一个新的
.dart
文件
demo.dart
,以下图所示:
demo.dart
文件中,输入以下代码:
// Define a function.
printInteger(int aNumber) {
print('The number is $aNumber.'); // Print to console.
}
// This is where the app starts executing.
main() {
var number = 42; // Declare and initialize a variable.
printInteger(number); // Call a function.
}
复制代码
而后在代码编辑区域鼠标右键,选择Run demo.dart
,便可运行一个最简单的dart程序,以下图所示:
Dart中单行注释使用//
,Dart同时支持多行注释和文档注释,能够点击这里查看更多
int
是Dart中的一种数据类型,同时还有其余的一些内置数据类型如String
List
bool
等
控制台输出使用print
语句
字符串使用单引号或双引号都可,如'hello', "hello"
字符串插入可使用相似$name
或${name}
的语法,好比下面的代码:
var name = 'zhangsan';
print("hello, I am $name");
int a = 10, b = 20;
print("$a + $b = ${a + b}");
复制代码
若是使用${name}
这种方式,大括号中能够是表达式
你可能已经注意到了,Dart的变量类型是可选的,你能够为某个变量指定类型,或者使用var
定义变量,Dart会自动推断变量的类型
当你在学习Dart语言时,下面的这些事实和概念请牢记于心:
Object
List<int>
表示一个整型的数据列表,List<dynamic>
则是一个对象的列表,其中能够装任意对象main
方法),也支持类方法或对象方法,同时你也能够在方法内部建立方法public
protected
private
等关键字,若是某个变量如下划线(_
)开头,表明这个变量在库中是私有的,具体能够看这里如下代码是Dart中定义变量的方法:
main() {
var a = 1;
int b = 10;
String s = "hello";
dynamic c = 0.5;
}
复制代码
你能够明确指定某个变量的类型,如int
bool
String
,也能够用var
或 dynamic
来声明一个变量,Dart会自动推断其数据类型。
注意:没有赋初值的变量都会有默认值null
若是你毫不想改变一个变量,使用final
或const
,不要使用var
或其余类型,一个被final
修饰的变量只能被赋值一次,一个被const
修饰的变量是一个编译时常量(const常量毫无疑问也是final常量)。能够这么理解:final
修饰的变量是不可改变的,而const
修饰的表示一个常量。
注意:实例变量能够是final的但不能是const的
下面用代码说明:
final String name = 'zhangsan';
name = 'lisi'; // 编译不经过,被final修饰的是常量,不可从新赋值
const a = 0;
a = 1; // 错误
复制代码
final
和const
的区别:
final
要求变量只能初始化一次,并不要求赋的值必定是编译时常量,能够是常量也能够不是。而 const
要求在声明时初始化,而且赋值必需为编译时常量。final
是惰性初始化,即在运行时第一次使用前才初始化。而 const
是在编译时就肯定值了。Dart有以下几种内建的数据类型:
下面用一段代码来演示以上各种数据类型:
main() {
// numbers
var a = 0;
int b = 1;
double c = 0.1;
// strings
var s1 = 'hello';
String s2 = "world";
// booleans
var real = true;
bool isReal = false;
// lists
var arr = [1, 2, 3, 4, 5];
List<String> arr2 = ['hello', 'world', "123", "456"];
List<dynamic> arr3 = [1, true, 'haha', 1.0];
// maps
var map = new Map();
map['name'] = 'zhangsan';
map['age'] = 10;
Map m = new Map();
m['a'] = 'a';
//runes,Dart 中 使用runes 来获取UTF-32字符集的字符。String的 codeUnitAt and codeUnit属性能够获取UTF-16字符集的字符
var clapping = '\u{1f44f}';
print(clapping); // 打印的是拍手emoji的表情
// symbols
print(#s == new Symbol("s")); // true
}
复制代码
Dart是一个面向对象的编程语言,因此即便是函数也是一个对象,也有一种类型Function
,这就意味着函数能够赋值给某个变量或者做为参数传给另外的函数。虽然Dart推荐你给函数加上返回值,可是不加返回值的函数一样能够正常工做,另外你还能够用=>
代替return
语句,好比下面的代码:
// 声明返回值
int add(int a, int b) {
return a + b;
}
// 不声明返回值
add2(int a, int b) {
return a + b;
}
// =>是return语句的简写
add3(a, b) => a + b;
main() {
print(add(1, 2)); // 3
print(add2(2, 3)); // 5
print(add3(1, 2)); // 3
}
复制代码
使用花括号将函数的参数括起来就是定义了命名参数,以下面的代码所示:
sayHello({String name}) {
print("hello, my name is $name");
}
sayHello2({name: String}) {
print("hello, my name is $name");
}
main() {
// 打印 hello, my name is zhangsan
sayHello(name: 'zhangsan');
// 打印 hello, my name is wangwu
sayHello2(name: 'wangwu');
}
复制代码
能够看到,定义命名参数时,你能够以{type paramName}
或者{paramName: type}
两种方式声明参数,而调用命名参数时,须要以funcName(paramName: paramValue)
的形式调用。
命名参数的参数并非必须的,因此上面的代码中,若是调用sayHello()
不带任何参数,也是能够的,只不过最后打印出来的结果是:hello, my name is null
,在Flutter开发中,你可使用@required
注解来标识一个命名参数,这表明该参数是必须的,你不传则会报错,好比下面的代码:
const Scrollbar({Key key, @required Widget child})
复制代码
使用中括号[]
括起来的参数是函数的位置参数,表明该参数可传可不传,位置参数只能放在函数的参数列表的最后面,以下代码所示:
sayHello(String name, int age, [String hobby]) { // 位置参数能够有多个,好比[String a, int b]
StringBuffer sb = new StringBuffer();
sb.write("hello, this is $name and I am $age years old");
if (hobby != null) {
sb.write(", my hobby is $hobby");
}
print(sb.toString());
}
main() {
// hello, this is zhangsan and I am 20 years old
sayHello("zhangsan", 20);
// hello, this is zhangsan and I am 20 years old, my hobby is play football
sayHello("zhangsan", 20, "play football");
}
复制代码
你能够为命名参数或者位置参数设置默认值,以下代码所示:
// 命名参数的默认值
int add({int a, int b = 3}) { // 不能写成:int add({a: int, b: int = 3})
return a + b;
}
// 位置参数的默认值
int sum(int a, int b, [int c = 3]) {
return a + b + c;
}
复制代码
不论在Dart
仍是Flutter
中,必须都须要一个顶层的main()
函数,它是整个应用的入口函数,main()
函数的返回值是void
,还有一个可选的参数,参数类型是List<String>
。
你能够将一个函数做为参数传给另外一个函数,好比下面的代码:
printNum(int a) {
print("$a");
}
main() {
// 依次打印:
// 1
// 2
// 3
var arr = [1, 2, 3];
arr.forEach(printNum);
}
复制代码
你也能够将一个函数赋值给某个变量,好比下面的代码:
printNum(int a) {
print("$a");
}
main() {
var f1 = printNum;
Function f2 = printNum;
var f3 = (int a) => print("a = $a");
f1(1);
f2(2);
f3(6);
}
复制代码
大多数函数都是有名称的,好比main()
printName()
等,可是你也能够写匿名函数,若是你对Java
比较熟悉,那下面的Dart
代码你确定也不会陌生:
test(Function callback) {
callback("hello");
}
main() {
test((param) {
// 打印hello
print(param);
});
}
复制代码
匿名函数相似于Java
中的接口,每每在某个函数的参数为函数时使用到。
全部的函数都有返回值,若是没有指定return
语句,那么该函数的返回值为null
。
Dart
中的运算符与Java
中的相似,好比++a
a == b
b ? a : b
,可是也有一些与Java
不太同样的运算符,下面用代码说明:
main() {
// 与Java相同的运算符操做
int a = 1;
++a;
a++;
var b = 1;
print(a == b); // false
print(a * b); // 3
bool real = false;
real ? print('real') : print('not real'); // not real
print(real && a == b); // false
print(real || a == 3); // true
print(a != 2); // true
print(a <= b); // false
var c = 9;
c += 10;
print("c = $c"); // c = 19
print(1<<2); // 4
// 与Java不太同样的运算符操做
// is运算符用于判断一个变量是否是某个类型的数据
// is!则是判断变量不是某个类型的数据
var s = "hello";
print(s is String); // true
var num = 6;
print(num is! String); // true
// ~/才是取整运算符,若是使用/则是除法运算,不取整
int k = 1;
int j = 2;
print(k / j); // 0.5
print(k ~/ j); // 0
// as运算符相似于Java中的cast操做,将一个对象强制类型转换
(emp as Person).teach();
// ??=运算符 若是 ??= 运算符前面的变量为null,则赋值,不然不赋值
var param1 = "hello", param2 = null;
param1 ??= "world";
param2 ??= "world";
print("param1 = $param1"); // param1 = hello
print("param2 = $param2"); // param2 = world
// ?.运算符
var str1 = "hello world";
var str2 = null;
print(str1?.length); // 11
print(str2?.length); // null
print(str2.length); // 报错
}
复制代码
若是你对Java中的建造者模式比较熟悉的话,Dart
中的..
运算符也很好理解,先看下面的代码:
class Person {
eat() {
print("I am eating...");
}
sleep() {
print("I am sleeping...");
}
study() {
print("I am studying...");
}
}
main() {
// 依次打印
// I am eating...
// I am sleeping...
// I am studying...
new Person()..eat()
..sleep()
..study();
}
复制代码
能够看到,使用..
调用某个对象的方法(或者成员变量)时,返回值是这个对象自己,因此你能够接着使用..
调用这个对象的其余方法,这不就相似于Java
中的建造者模式,每次build某个属性时,都返回一个this
对象吗。
if / else
switch
for /while
try / catch
语句跟Java
中都相似,try / catch
语句可能稍有不一样,下面用一段代码说明:
main() {
// if else语句
int score = 80;
if (score < 60) {
print("so bad!");
} else if (score >= 60 && score < 80) {
print("just so so!");
} else if (score >= 80) {
print("good job!");
}
// switch语句
String a = "hello";
// case语句中的数据类型必须是跟switch中的类型一致
switch (a) {
case "hello":
print("haha");
break;
case "world":
print("heihei");
break;
default:
print("WTF");
}
// for语句
List<String> list = ["a", "b", "c"];
for (int i = 0; i < list.length; i++) {
print(list[i]);
}
for (var i in list) {
print(i);
}
// 这里的箭头函数参数必须用圆括号扩起来
list.forEach((item) => print(item));
// while语句
int start = 1;
int sum = 0;
while (start <= 100) {
sum += start;
start++;
}
print(sum);
// try catch语句
try {
print(1 ~/ 0);
} catch (e) {
// IntegerDivisionByZeroException
print(e);
}
try {
1 ~/ 0;
} on IntegerDivisionByZeroException { // 捕获指定类型的异常
print("error"); // 打印出error
} finally {
print("over"); // 打印出over
}
}
复制代码
Dart
中的类没有访问控制,因此你不须要用private
, protected
, public
等修饰成员变量或成员函数,一个简单的类以下代码所示:
class Person {
String name;
int age;
String gender;
Person(this.name, this.age, this.gender);
sayHello() {
print("hello, this is $name, I am $age years old, I am a $gender");
}
}
复制代码
上面的Person
类中有3个成员变量,一个构造方法和一个成员方法,看起来比较奇怪的是Person
的构造方法,里面传入的3个参数都是this.xxx
,并且没有大括号{}
包裹的方法体,这种语法是Dart比较独特而简洁的构造方法声明方式,它等同于下面的代码:
Person(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
复制代码
要调用Person
类的成员变量或成员方法,能够用下面的代码:
var p = new Person("zhangsan", 20, "male");
p.sayHello(); // hello, this is zhangsan, I am 20 years old, I am a male
p.age = 50;
p.gender = "female";
p.sayHello(); // hello, this is zhangsan, I am 50 years old, I am a female
复制代码
因为Dart
中的类没有访问控制权限,因此你能够直接用obj.var
的方式访问一个对象的成员变量。
类除了有跟类名相同的构造方法外,还能够添加命名的构造方法,以下代码所示:
class Point {
num x, y;
Point(this.x, this.y);
// 类的命名构造方法
Point.origin() {
x = 0;
y = 0;
}
}
main() {
// 调用Point类的命名构造方法origin()
var p = new Point.origin();
var p2 = new Point(1, 2);
}
复制代码
Dart
中使用extends
关键字作类的继承,若是一个类只有命名的构造方法,在继承时须要注意,以下代码:
class Human {
String name;
Human.fromJson(Map data) {
print("Human's fromJson constructor");
}
}
class Man extends Human {
Man.fromJson(Map data) : super.fromJson(data) {
print("Man's fromJson constructor");
}
}
复制代码
因为Human
类没有默认构造方法,只有一个命名构造方法fromJson
,因此在Man
类继承Human
类时,须要调用父类的fromJson
方法作初始化,并且必须使用Man.fromJson(Map data) : super.fromJson(data)
这种写法,而不是像Java
那样将super
写到花括号中。
有时候你仅仅只是在某个类的构造方法中,调用这个类的另外一个构造方法,你能够这么写:
class Point {
num x, y;
Point(this.x, this.y);
// 命名构造方法调用了默认的构造方法
Point.alongXAxis(num x) : this(x, 0);
}
复制代码
一个类的成员方法是一个函数,为这个类提供某些行为。上面的代码中已经有了一些类的成员方法的定义,这些定义方式跟Java
很相似,你能够为某个类的成员变量提供getter/setter
方法,以下代码:
class Rectangle {
num left, top, width, height;
// 构造方法传入left, top, width, height几个参数
Rectangle(this.left, this.top, this.width, this.height);
// right, bottom两个成员变量提供getter/setter方法
num get right => left + width;
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
}
复制代码
使用abstract修饰一个类,则这个类是抽象类,抽象类中能够有抽象方法和非抽象方法,抽象方法没有方法体,须要子类去实现,以下代码:
abstract class Doer {
// 抽象方法,没有方法体,须要子类去实现
void doSomething();
// 普通的方法
void greet() {
print("hello world!");
}
}
class EffectiveDoer extends Doer {
// 实现了父类的抽象方法
void doSomething() {
print("I'm doing something...");
}
}
复制代码
Dart中有相似于C++中的运算符重载语法,好比下面的代码定义了一个向量类,重载了向量的+
-
运算:
class Vector {
num x, y;
Vector(this.x, this.y);
Vector operator +(Vector v) => new Vector(x + v.x, y + v.y);
Vector operator -(Vector v) => new Vector(x - v.x, y - v.y);
printVec() {
print("x: $x, y: $y");
}
}
main() {
Vector v1 = new Vector(1, 2);
Vector v2 = new Vector(3, 4);
(v1 - v2).printVec(); // -2, -2
(v1 + v2).printVec(); // 4, 6
}
复制代码
使用enum
关键字定义一个枚举类,这个语法跟Java
相似,以下代码:
enum Color { red, green, blue }
复制代码
mixins是一个重复使用类中代码的方式,好比下面的代码:
class A {
a() {
print("A's a()");
}
}
class B {
b() {
print("B's b()");
}
}
// 使用with关键字,表示类C是由类A和类B混合而构成
class C = A with B;
main() {
C c = new C();
c.a(); // A's a() c.b(); // B's b()
}
复制代码
// 类的静态成员变量和静态成员方法
class Cons {
static const name = "zhangsan";
static sayHello() {
print("hello, this is ${Cons.name}");
}
}
main() {
Cons.sayHello(); // hello, this is zhangsan
print(Cons.name); // zhangsan
}
复制代码
Java
和C++
语言都有泛型,Dart
语言也不例外,使用泛型有不少好处,好比:
Dart
内置的数据类型List
就是一个泛型数据类型,你能够往List
中塞任何你想的数据类型好比整型、字符串、布尔值等
关于Dart
更多的泛型知识点,能够查看这里。
Dart
目前已经有不少的库提供给开发者,许多功能不须要开发者本身去实现,只须要导入对应的包便可,使用import
语句来导入某个包,好比下面的代码:
import 'dart:html';
复制代码
若是你想导入本身写的某个代码文件,使用相对路径便可,例如当前有一个demo.dart
文件,跟该文件同级目录下有个util.dart
文件,文件代码以下:
// util.dart文件内容
int add(int a, int b) {
return a + b;
}
复制代码
在demo.dart
文件中若是要引用util.dart
文件,使用下面的方式导入:
// demo.dart
import './util.dart';
main() {
print(add(1, 2));
}
复制代码
你可使用as
关键字为导入的某个包设置一个前缀,或者说别名,好比下面的代码:
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// Uses Element from lib1.
Element element1 = Element();
// Uses Element from lib2.
lib2.Element element2 = lib2.Element();
复制代码
你也能够在导入包时使用show
hide
关键字来导入某个包中的部分功能,好比下面的代码:
// 只导入foo
import 'package:lib1/lib1.dart' show foo;
// 导入除了foo的全部其余部分
import 'package:lib2/lib2.dart' hide foo;
复制代码
导入包时使用deferred as
可让这个包懒加载,懒加载的包只会在该包被使用时获得加载,而不是一开始就加载,好比下面的代码:
import 'package:greetings/hello.dart' deferred as hello;
复制代码
Dart
提供了相似ES7中的async
await
等异步操做,这种异步操做在Flutter开发中会常常遇到,好比网络或其余IO操做,文件选择等都须要用到异步的知识。 async
和await
每每是成对出现的,若是一个方法中有耗时的操做,你须要将这个方法设置成async
,并给其中的耗时操做加上await
关键字,若是这个方法有返回值,你须要将返回值塞到Future
中并返回,以下代码所示:
Future checkVersion() async {
var version = await lookUpVersion();
// Do something with version
}
复制代码
下面的代码使用Dart
从网络获取数据并打印出来:
import 'dart:async';
import 'package:http/http.dart' as http;
Future<String> getNetData() async{
http.Response res = await http.get("http://www.baidu.com");
return res.body;
}
main() {
getNetData().then((str) {
print(str);
});
}
复制代码
关于Dart
异步操做,能够查看这篇文章了解更多。
本篇博客较长,主要是对官方文档的一个翻译(大部分),若是你对英文阅读没有太大障碍,建议直接查看官方的英文文档,但愿各位都能愉快的学习Dart
和Flutter
。
上一篇 | 下一篇 |
---|---|
从0开始写一个基于Flutter的开源中国客户端(1) ——Flutter简介及开发环境搭建 |
从0开始写一个基于Flutter的开源中国客户端(3)——初识Flutter & 经常使用的Widgets |