前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《Flutter》-- 3.Dart语言

《Flutter》-- 3.Dart语言

作者头像
爱学习的程序媛
发布2022-04-07 16:20:48
2.9K0
发布2022-04-07 16:20:48
举报
文章被收录于专栏:学习/读书笔记学习/读书笔记

参阅书籍:

《Flutter跨平台开发入门与实践》-- 向治洪(著)

参阅网站:

https://www.dartcn.com/guides/language/language-tour

3. Dart语言

Dart是谷歌公司于2011年10月发布的一门全新的编程语言,已被欧洲计算机制造商协会(European Computer Manufacture Association,ECMA)认定为标准,初期目标是将其用于Web应用、服务器、移动应用和物联网等领域的开发。

Dart在设计之初参考了Java等面向对象的编程语言,因此Dart既有面向对象编程的特性,也有面向函数编程的特性。除了融合Java和JavaScript所长之外,Dart还提供了一些其他具有表现力的语法,如可选命名参数、级联运算符和条件成员访问运算符等。

使用Dart之前,需要先安装Dart SDK。Dart SDK包含了编写和运行Dart代码所需的一切工具,如虚拟机(Virtual Machine,VM)、库、分析器、包管理工具、文档生成器和代码调试等。

搭建开发环境时安装的Flutter SDK里已经包含了Dart SDK。

3.1 编写 Hello World

和大多数编程语言一样,Dart也把main()作为程序的入口。

首先,新建一个名为hello.dart的文件,添加如下代码。

然后在终端执行dart hello.dart命令,在终端可以看到输出了“Hello World!”。

3.2 变量和常量

变量仅存储对象引用。

3.2.1 声明变量

代码语言:javascript
复制
//用var声明变量,name变量的类型会被推断为String
var name = 'huahua';

//用dynamic声明变量,不限定name变量的类型
dynamic name = 'huahua';

//显示声明变量,限定name变量的类型为String
String name = 'huahua';

注:显示声明的变量必须初始化后才能使用。

3.2.2 默认值

在Dart中,一切皆为对象,未初始化的变量默认值是null。

3.2.3 声明常量

在Dart中,声明使用过程中不会被修改的变量(即常量)可以使用final或const关键字。

final变量的值只能被设置一次,const变量的值在编译时就已经固定。

实例变量可以是final变量,但不能是const变量。

const还可以用来创建常量值,以及声明创建常量值的构造函数。

3.3 内置数据类型

3.3.1 Number

Number有两种类型:int(整型) 和 double(浮点型)。

从 Dart 2.1 开始,必要的时候 int 字面量会自动转换成 double 类型。

字符串和数字相互转换的方法:

代码语言:javascript
复制
// 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);

3.3.2 String

String是一组UTF-16的单元序列,通过单引号或双引号进行声明。

字符串可以通过 ${expression} 的方式内嵌表达式。如果表达式是一个标识符,则 {} 可以省略。

代码语言:javascript
复制
var s = 'string interpolation';
print('Dart has $s, which is very handy.');//Dart has string interpolation, which is very handy.
print('That deserves all caps. ' + '${s.toUpperCase()} is very handy!');//That deserves all caps. STRING INTERPOLATION is very handy!

使用连续三个单引号或者三个双引号实现多行字符串对象的创建。

使用 r 前缀,可以创建原始raw字符串。

3.3.3 Boolean

Dart使用 bool 类型表示布尔值。Dart只有字面量 true 和 false 是布尔类型,这两个对象都是编译时常量。

3.3.4 List

在Dart中,List表示列表,和数组是同一概念。Dart中的List类型和JavaScript中的Array类型是类似的。

代码语言:javascript
复制
//通过字面量创建List
var list = [1, 2, 3];

3.3.5 Set

在Dart中Set是一个元素唯一且无序的集合。

代码语言:javascript
复制
//通过字面量创建Set
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};

//创建一个空集
var names = <String>{};
//或Set<String> names = {};

//这样会创建一个Map,而不是Set
var names = {}; 

3.3.6 Map

通常来说, Map 是用来关联 keys 和 values 的对象。keys 和 values 可以是任何类型的对象。在一个 Map 对象中一个 key 只能出现一次,但是 value 可以出现多次。

代码语言:javascript
复制
//通过字面量创建Map
var gifts = {
  // Key: Value
  'first': 'partridge',
  'second': 'turtledoves',
  'fifth': 'golden rings'
};

//通过构造函数创建Map
var gifts = new Map();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';

3.3.7 Rune

在 Dart 中,Rune 用来表示字符串中的 UTF-32 编码字符。

表示 Unicode 编码的常用方法是 \uXXXX, 这里 XXXX 是一个4位的16进制数。对于特殊的非 4 个数值的情况,把编码值放到大括号中即可。

代码语言:javascript
复制
var clapping = '\u{1f44f}';
print(clapping);//👏

Runes input = new Runes('\u2665  \u{1f605}  \u{1f60e}  \u{1f47b}  \u{1f596}  \u{1f44d}');
print(new String.fromCharCodes(input));//♥  😅  😎  👻  🖖  👍

3.3.8 Symbol

一个 Symbol 对象表示 Dart 程序中声明的运算符或者标识符。通过字面量 Symbol ,也就是标识符前面添加一个 # 号,来获取标识符的 Symbol 。

代码语言:javascript
复制
 print(#radix);//Symbol("radix")
 print(#bar);//Symbol("bar")

3.4 函数

Dart 是一门真正面向对象的语言, 其中的函数也是对象,并且有它的类型 Function。这也意味着函数可以被赋值给变量或者作为参数传递给其他函数。也可以把 Dart 类的实例当做方法来调用。

3.4.1 main() 函数

任何应用都必须有一个顶级 main() 函数,作为应用服务的入口。main() 函数返回值为空,参数为一个可选的 List<String> 。

Flutter应用的main():

代码语言:javascript
复制
void main() => runApp(MyApp());

3.4.2 函数参数

函数的参数类型有两种,即必传参数和可选参数。通常,可选参数写在必传参数的后面,可选参数使用命名参数或位置参数进行传值。

可选命名参数用 {} 包裹。

代码语言:javascript
复制
void main() {
  //调用函数时,使用指定命名参数 paramName: value
  param(2, age: 30);//num: 2, name: huahua, age: 30
}

void param(int num, {String name = 'huahua', int age = 20}){
  print('num: $num, name: $name, age: $age');
}

可选位置参数用 [] 包裹。

代码语言:javascript
复制
void main() {
  param(2, 'huanhuan');//num: 2, name: huanhuan, age: 20
}

void param(int num, [String name = 'huahua', int age = 20]){
  print('num: $num, name: $name, age: $age');
}

list 或 map 可以作为默认值传递。

代码语言:javascript
复制
void main() {
  doStuff(list: [4, 5, 6]);
  /*
  * list:  [4, 5, 6]
  * gifts: {first: paper, second: cotton, third: leather}
  */
}

void doStuff (
    {List<int> list = const [1, 2, 3],
    Map<String, String> gifts = const {
      'first': 'paper',
      'second': 'cotton',
      'third': 'leather'
    }}) {
  print('list:  $list');
  print('gifts: $gifts');
}

3.4.3 返回值

所有函数都会返回一个值,如果没有明确指定返回值, 函数体会被隐式的添加 return null; 语句。

代码语言:javascript
复制
void main() {
  foo(){}
  print(foo());//null
}

3.4.4 匿名函数

和其他编程语言一样,Dart也支持匿名函数。

代码语言:javascript
复制
void main() {
  var list = ['apples', 'bananas', 'oranges'];
  list.forEach((item) => print('${list.indexOf(item)}: $item'));
  /*
   * 0: apples
   * 1: bananas
   * 2: oranges
   */
}

3.5 运算符

Dart支持各种类型的运算符,部分运算符支持重载操作。

3.5.1 算数运算符

+(加法)、-(减法)、*(乘法)、/(除法)、~/(除法,取整)、-expr(取负)、%(取余)

代码语言:javascript
复制
print(3 + 2);//5
print(3 - 2);//1
print(3 * 2);//6
print(3 / 2);//1.5
print(3 ~/ 2);//1
print(-3);//-3
print(3 % 2);//1

Dart也支持自增和自减运算符。

代码语言:javascript
复制
int num = 3;
print(num++);//3
print(++num);//5
print(num--);//5
print(--num);//3

3.5.2 关系运算符

==(相等)、!=(不相等)、>(大于)、<(小于)、>=(大于等于)、<=(小于等于)

代码语言:javascript
复制
int a = 3, b = 2;
print(a == b);//false
print(a != b);//true
print(a > b);//true
print(a < b);//false
print(a >= b);//true
print(a <= b);//false

3.5.3 类型判断运算符

as:类型转换,也被用于指定库前缀。

is:对象具有指定的类型,返回true。

is!:对象具有指定的类型,返回false。

3.5.4 位运算符

&(按位与)、|(按位或)、^(按位异或)、~(按位取反)、>>(右移)、<<(左移)

代码语言:javascript
复制
final value = 0x22;
final bitmask = 0x0f;

print((value & bitmask) == 0x02); //true
print((value & ~bitmask) == 0x20); //true
print((value | bitmask) == 0x2f); //true
print((value ^ bitmask) == 0x2d); //true
print((value << 4) == 0x220); //true
print((value >> 4) == 0x02); //true

3.5.5 逻辑运算符

&&(与)、||(或)、!(非)

3.5.6 赋值运算符

=、+=、-=、*=、/=、%=、~/=、>>=、<<=、^=、&=、!=

使用 ??= 运算符时,只有当被赋值的变量为 null 时才会赋值给它。

代码语言:javascript
复制
var a;
var b = 10;
print(a ??= b); //10

3.5.7 条件表达式

三目表达式:condition ? expr1 : expr2

代码语言:javascript
复制
//如果条件为true,执行并返回expr1的值;如果条件为false,执行并返回expr2的值。
var isPublic = true;
var visibility = isPublic ? 'public' : 'private';
print('visibility: $visibility');//visibility: public

??运算符:expr1 ?? expr2

代码语言:javascript
复制
//如果expr1是non-null,返回expr1的值;否则,执行并返回expr2的值。
String playName(var name) => name ?? 'huahua';
print(playName('huanhuan'));//huanhuan

3.5.8 级联运算符

级联运算符 (..) 可以实现对同一个对象进行一系列的操作。除了调用函数, 还可以访问同一对象上的字段属性。

3.6 流程控制语句

Dart的流程控制语句主要由if-else、for、while与do-while、break与continue、switch与case、assert等构成。

assert用于表示断言,用于判断语句是否满足条件。如果assert修饰的语句中的判断结果为false,那么正常的程序执行流程会被中断;如果assert修饰的判断条件结果为true,则继续执行后面的语句。

代码语言:javascript
复制
var text = 'hello';
assert(text != null);
print('$text world!');//hello world!

注:Flutter中的assert只在Debug模式中生效,在生产环境是无效的。

3.7 类

3.7.1 类的成员变量

在面向对象编程中,类的对象通常由函数和数据组成。方法的调用需要通过对象来完成,被调用的方法还可以访问其对象的函数和数据。我们使用点操作符来引用对象的变量和方法。

代码语言:javascript
复制
void main() {
  Point p = new Point();
  p.x = 2;
  p.y = 2;
  print(p.x);//2
  print(p.y);//2
}

class Point {
  var x;
  var y;
}

注:使用 ?. 来代替 . , 可以避免因为左边对象可能为 null 导致的异常。

3.7.2 构造函数

构造函数是一种特殊的函数,主要用来在创建对象时初始化对象,即初始化对象成员变量,构造函数的函数名必须要与类名相同。

代码语言:javascript
复制
void main() {
  Point p = new Point(1, 2);
  print(p.x);//1
  print(p.y);//2
}

class Point {
  dynamic x;
  dynamic y;
  Point (int x, int y) {
    this.x = x;
    this.y = y;
  }
}

3.7.3 继承类

和其他编程语言一样,Dart使用extends关键字来创建一个子类,使用super关键字来引用继承的父类。

代码语言:javascript
复制
void main() {
  var student = new Student();
  student.name = 'huahua';
  student.study();//Student huahua is study...
  student.run();//Student huahua is run...
}

//父类
class Person {
  String name = 'huanhuan';
  int age = 30;
  void run() {
    print('Person is run...');
  }
}

//子类
class Student extends Person {
  @override
  void run() {
    print('Student ${name} is run...');
  }
  void study() {
    print('Student ${name} is study...');
  }
}

3.7.4 抽象类

使用abstract修饰定义的类被称为抽象类,抽象类只能被继承,不能实例化。Dart的抽象类可以用来定义接口和部分接口实现,子类可以继承抽象类也可以实现抽象类接口。

代码语言:javascript
复制
void main() {
  var dog = Dog();
  dog.eat();//小狗在啃骨头...
  dog.run();//小狗在跑...
  dog.printInfo();//抽象类中的普通方法...
}

// 抽象类
abstract class Animal {
  eat();
  run();
  printInfo(){
    print('抽象类中的普通方法...');
  }
}

//继承抽象类
class Dog extends Animal {
  @override
  eat(){
    print('小狗在啃骨头...');
  }
  @override
  run(){
    print('小狗在跑...');
  }
}

3.7.5 枚举类型

枚举类型是一种特殊的类,用来表示相同类型的一组常量值。枚举类型使用enum关键字进行定义,枚举类型中的每个值都有一个index的getter方法,用来标记元素在枚举类型中的位置。

代码语言:javascript
复制
void main() {
  print(Color.red.index);//0
  print(Color.green.index);//1
  print(Color.blue.index);//2
}

enum Color {red, green, blue}

使用枚举类型的values常量可以获取所有枚举值列表。

代码语言:javascript
复制
void main() {
  List<Color> colors = Color.values;
  print(colors);//[Color.red, Color.green, Color.blue]
}

enum Color {red, green, blue}

注:在Dart中,枚举类型不能被子类化、继承或实现,不能被显示实例化。

3.7.6 Mixin

Mixin是复用类代码的一种途径,复用的类可以在不同层级,并且复用的类之间可以不存在任何继承关系。

Dart的Mixin相当于多继承,也就是说一个子类可以继承多个父类。Dart使用with关键字来实现Mixin的功能。

代码语言:javascript
复制
void main() {
  var engineer = Engineer();
  engineer.doWork();//工程师在写代码...
  engineer.fixComputer();//工程师在修电脑...
}

class SoftEngineer {
  void doWork() {
    print('工程师在写代码...');
  }
}

class HardEngineer {
  void fixComputer() {
    print('工程师在修电脑...');
  }
}

class Engineer extends SoftEngineer with HardEngineer {
  
}

3.8 泛型

泛型的本质是数据类型的参数化,它给强类型编程语言增加了灵活性,并且使用泛型可以减少重复代码,提高代码的质量。

代码语言:javascript
复制
abstract class ObjectCache {
  Object getByKey(String key);
  void setByKey(String key, Object value);
}

abstract class StringCache {
  String getByKey(String key);
  void setByKey(String key, String value);
}

//可以通过创建一个带有泛型参数的接口来代替ObjectCache和StringCache接口
abstract class Cache<T> {
  T getByKey(String key);
  void setByKey(String key, T value);
}

泛型可以用于集合中List、Set和Map类型的参数化。

代码语言:javascript
复制
var names = <String>['Seth', 'Kathy', 'Lars'];
var uniqueNames = <String>{'Seth', 'Kathy', 'Lars'};
var pages = <String, String>{
  'index.html': 'Homepage',
  'robots.txt': 'Hints for web robots',
  'humans.txt': 'We are people, not machines'
};

在调用构造函数时,可以在类名字后面使用尖括号来指定泛型类型。

代码语言:javascript
复制
var nameSet = Set<String>.from(names);

在使用泛型的时候,也可以使用extends关键字来限定参数的类型。

代码语言:javascript
复制
void main() {
  var someBaseClassFoo = Foo<SomeBaseClass>();
  var extenderFoo = Foo<Extender>();
  print(someBaseClassFoo);//Instance of 'Foo<SomeBaseClass>'
  print(extenderFoo);//Instance of 'Foo<Extender>'
}

class SomeBaseClass {}

class Foo<T extends SomeBaseClass> {
  String toString() => "Instance of 'Foo<$T>'";
}

class Extender extends SomeBaseClass {}

除了作用于类外,还可以使用泛型来定义泛型方法。

代码语言:javascript
复制
void main() {
  var names = <String>['huahua', 'huanhuan'];
  print(printFirstName(names));//huahua
}

T printFirstName<T> (List<T> ts) {
  T tmp = ts[0];
  return tmp;
}

3.9 元数据

元数据又称中介数据、中继数据,主要用来描述数据的属性信息,如存储位置、历史数据、文件记录等。元数据注释以字符@开头,后接对编译时常量的引用或对常量构造函数的调用。

目前,Dart支持3种元数据注解:

@deprecated:用来表示被标注的元素已过时;

@override:用来表示需要覆盖父类方法;

@proxy:可以用来在编译时避免错误警告。

3.10 异步编程

Dart是目前少数几个支持异步编程的语音之一,可以使用异步函数或await表达式来实现异步编程。

异步函数指的是被async标记符标记的函数,该函数会返回Future对象。

代码语言:javascript
复制
void main() {
  lookUpVersion() async => '1.0.0';
  print(lookUpVersion());//Instance of 'Future<String>'
}

3.10.1 Future

在Java并发编程中,经常会使用Future来处理异步或延时任务,在Dart中通用也使用Future来处理异步任务。

默认情况下,Future执行的是一个事件队列任务,Future提供了多个API和特性来辅助任务队列的完成。

Dart的Future与JavaScript的Promise非常类似,主要用来处理异步任务的最终完成结果。异步任务处理成功就执行成功的操作,异步任务处理失败就捕获错误或停止后续操作。

在Dart中,常见的创建Future的函数有:

Future():默认构造函数,返回值可以是普通值或Future对象;

Future.microtask():将Future对象添加到异步任务队列;

Future.sync():创建一个同步任务,该任务会被立即执行;

Future.delayed():创建一个延时任务,延时不一定准确;

Future.error():创建一个Future对象,返回结果为错误。

在异步任务中,Future中的任务完成后需要添加一个回调函数,用于处理回调的结果,回调会被立即执行,不会被添加到事件队列。

如果要捕获异步任务的异常,可以使用catchError()。

then()也提供了一个可选参数onError来捕获异常。

如果要执行多个任务反馈执行的结果,可以使用Future.wait()。

3.10.2 async/await

使用async/await可以编写更简洁的异步代码,且不需要再调用Future的相关API。

3.10.3 Stream

除了Future外,Stream也可以用于接收异步事件数据。Stream除了可以接收单个异步事件数据外,还可以接收多个异步任务的结果。在执行异步任务时,可以通过多次触发成功或失败事件来传递结果数据或错误异常。

Stream常见的创建方式:

Stream<T>.fromFuture:接收一个Future对象来创建Stream;

Stream<T>.fromFutures:接收一个Future集合对象来创建Stream;

Stream<T>.fromIterable:接收一个集合对象来创建Stream;

Stream<T>.periodic:接收一个Duration对来创建Stream。

根据数据流监听器个数的不同,Stream数据流可以分为单订阅流和多订阅流。实际开发中,创建Stream数据流使用StreamController。

用StreamController创建单订阅流:

使用StreamController创建多订阅量可以直接创建或将单订阅流转成多订阅流。

代码语言:javascript
复制
import 'dart:async';

void main() {
  //直接创建多订阅流
  StreamController<String> s1 = StreamController.broadcast();
  s1.stream.listen(
    (data) => print(data),
    onError: (error) => print(error.toString())
  );
  s1.stream.listen((data) => print(data));
  s1.add('a');

  //将单订阅流转成多订阅流
  StreamController<String> s2 = StreamController();
  Stream stream = s2.stream.asBroadcastStream();
  stream.listen((data) => print(data));
  stream.listen((data) => print(data));
  s2.sink.add('a');
  s2.close();
}

注:为了避免造成资源浪费,数据流用完后调用close()关闭Stream数据流。

在Dart中,Stream和Future是异步编程的两个核心API。Future用于处理异步或延迟任务等,返回值是一个Future对象。Future通常用于获取一次异步获得的数据,而Stream则可以通过多次触发成功或失败事件来获取数据。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-04-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 爱学习的程序媛 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档