Flutter学习2-dart学习

简介

Dart 是一种专为客户端优化的语言,用于在任何平台上开发高性能应用程序。它的目标是为多平台开发提供最高效的编程语言,并为应用程序框架提供灵活的执行运行时平台。

优点如下

  • 为基于事件驱动的用户界面提供成熟且完备的 异步-等待 体系,同时配备了 基于 isolate 的并发
  • 为构建用户界面优化的语言,其中包含了 健全的空安全、用于展开集合的展开操作符 以及集合内的条件语句 用于为每个平台定制 UI
  • 语法相近、易于上手的编程语言,类似python的极简风格
  • 在正在运行的应用中使用 热重载,从而快速并持续地应用更改,并且立刻看到更改的效果
  • 在编写代码时享受灵活的类型系统,以及强大且 可配置的静态分析工具
  • 使用您选择的代码编辑器进行 性能分析、 日志记录以及 调试
  • 使用 AOT 编译为机器码, 极速启动 您的应用
  • 使用完整、成熟且快速的 JavaScript 编译器 为 Web 平台编译应用
  • 仅用一种语言即可编写应用的 后端代码

安装

flutter自带dart sdk不用安装

基本语法

运行入口main,每个文件都可以用main做入口,类似python
dart还是相当友好的,毕竟是2011年创建的,十分现代的语言,现代语言的特点,详见https://blog.csdn.net/hn_lgc/article/details/124024197
主要特点就是简单友好,融合多种编程范式,不会想传统语言那么死板

比如,创建变量是,可以使用new 也可以不使用
函数可以声明返回值,也可以不声明

if else
for
while
switch
case
assert
基本语法都与目前的经典语言兼容,简直不要太完美!

变量

变量仅存储对象的引用
支持类型推断,可以只写个var,指定为基类Object或者dynamic类型,不是无类型的比如python
也可以指定类型 String name = ‘Bob’;
建议,通过 var 声明局部变量而非使用指定的类型。

一样的一些皆对象,包括所谓的基本类型,int,所以int也可能为null,和Java不一样了。

综上,变量类型有
一般变量,可空变量,final变量,const变量

常量 概念

注意常量概念在dart中是比较重要的,基于不可变编程思想
final 和 const 的区别
const ==常量,必须声明时赋值,只能作用于静态对象,用于非静态理论上可以,但是没有必要。
final == 不可变变量,意思是只能赋值一次,不用声明时赋值

常量类和常量构造函数:

当我们需要创建不可变对象的时候,就可以使用常量类的常量构造函数

dart这个语法有点反人类,定义常量类不是给类添加修饰符,而是给它的构造函数添加修饰符。。。。
给构造函数添加修饰符之后,这个类就变成了常量类,其成员变量必须是final。

扫描二维码关注公众号,回复: 14646910 查看本文章

常量类实例化时最好加上const修饰符。

字符串

正则表达式

RegExp 类提供与 JavaScript 正则表达式相同的功能。使用正则表达式可以对字符串进行高效搜索和模式匹配。

// Here’s a regular expression for one or more digits.
var numbers = RegExp(r’\d+');
var allCharacters = ‘llamas live fifteen to twenty years’;
var someDigits = ‘llamas live 15 to 20 years’;

// contains() can use a regular expression.
assert(!allCharacters.contains(numbers));
assert(someDigits.contains(numbers));

// Replace every match with another string.
var exedOut = someDigits.replaceAll(numbers, ‘XX’);

内置类型

数字,默认只有int 和 double,int支持位运算 都是64位,如果需要更低位数的,比如32,8需要使用特殊的对象。
这也是个问题点吧,客户端语言用得着64位的int?
这里在一些特殊场景下,要特别注意这个位数差别,比如传递C++,要用64位接收,不然会出问题
字符串, UTF-16 编码, 在字符串中,请以 ${表达式} 的形式使用表达式。和python比较像,可以使用单引号,可以使用三个引号多行,可以使用字符串前缀 r’xxxx\xxxx’
反人类语法:int 不能自动转double,需要 toDouble()

集合

列表:
可以使用扩展操作符(…)将一个 List 中的所有元素插入到另一个 List 中: [0, …list]; [0, …?list];空安全 这里就是一个解操作吧,*不就行了吗,这个语法不行
类似python
集合中的 if 和 集合中的 for 操作,在构建集合时,可以使用条件判断 (if) 和循环 (for)。
排序
list.sort((a, b) => a.compareTo(b));
set
var halogens = {‘fluorine’, ‘chlorine’, ‘bromine’, ‘iodine’, ‘astatine’};
var names = {};
列表里面的if不加花括号
children: [
picWidget, // 占满整个空间,不加定位
if (is_show_dir)
Positioned(
left: 48,
child: dirWidget,
)
]

map
set就是map的简化版
使用 add() 方法或 addAll() 方法向已存在的 Set 中添加项目:
Set 可以像 List 一样支持使用扩展操作符(… 和 …?)以及 Collection if 和 for 操作
注意!map类型固定,不同于python
如果你向Map 对象中添加不正确的类型值,将导致运行时异常。

Runes
这个类专门用来操作用户感知的字符,比如表情符号,相当于dart给客户端特有的支持

Symbol 字面量是编译时常量。

空安全相关 初始化

类似于Kotlin
空安全,为了防止空指针问题,dart引入空安全机制
启用空安全之后,所有变量必须有初始值,除非指定变量可为空。
int? lineCount;

初始值必须声明变量的时候赋值吗?不是必须的
空安全不必在声明它的地方初始化局部变量,但是您需要在使用它之前为它赋值,编译器会检查使用前是否赋值。

如果编译器无法检查的呢,那么dart又提供了延迟初始化机制 和 惰性初始化
延迟初始化
使用前手动初始化
late String description;
当然,这个是有风险的。
惰性初始化
使用前编译器才调用初始化语句,适用于初始化代价比较大的情形
late String temperature = readThermometer(); // Lazily initialized.

运算符

目前基本上的运算符号都是支持的,没有其它语言的死板

要判断两个对象 x 和 y 是否表示相同的事物使用 == 即可。(在极少数情况下,可能需要使用 identical() 函数来确定两个对象是否完全相同)。下面是 == 运算符的一些规则:
三元运算符特别的点:
如果赋值是根据判定是否为 null 则考虑使用 ??。
a ??= 1;
等价于 if (a ==0) a = 1;

级联运算符 …可以让你在同一个对象上连续调用多个对象的变量或方法。
严格来说 … 级联操作并非一个运算符而是 Dart 的特殊语法。
不用手动返回this了
var paint = Paint()
…color = Colors.black
…strokeCap = StrokeCap.round
…strokeWidth = 5.0;
与下面的代码等效:
var paint = Paint();
paint.color = Colors.black;
paint.strokeCap = StrokeCap.round;
paint.strokeWidth = 5.0;

null-short 判空的级联 ?…

级联运算符可以嵌套,例如:

Cascading operators can be nested, for example:

final addressBook = (AddressBookBuilder()
…name = ‘jenny’
…email = ‘[email protected]
…phone = (PhoneNumberBuilder()
…number = ‘415-555-0100’
…label = ‘home’)
.build())
.build();

?[] 判空访问 List

函数 Function

Dart 是一种真正面向对象的语言,函数也是对象,类似于python。

函数可以声明返回值,也可以不声明。

参数

函数可以有两种形式的参数:必选参数 和 可选参数 使用[]包裹的参数。必要参数定义在参数列表前面
顾名思义,可选参数就是调用函数时,可以传递值,也可以不传递,
**使用[ ]包裹的参数是一个位置可选参数。**下面是一个例子:

getHttpUrl(String server, String path, [int port=80]) {
// …
}
在上面的代码中,port是可选的,默认值为80。

您可以getHttpUrl使用或不使用第三个参数进行调用。

getHttpUrl(‘example.com’, ‘/index.html’, 8080); // port == 8080
getHttpUrl(‘example.com’, ‘/index.html’); // port == 80

命名可选参数 使用{ }包裹的参数 就是说调用时需要指定名字,如果是一般的可选参数,就不需要指定名字

下面是一个例子:

getHttpUrl(String server, String path, {int port = 80}) {
// …
}
您可以使用或不使用第三个参数调用getHttpUrl。但你使用第三个参数时,你必须使用参数名称调用函数。

getHttpUrl(‘example.com’, ‘/index.html’, port: 8080); // port == 8080
getHttpUrl(‘example.com’, ‘/index.html’); // port == 80

默认值参数

命名参数
注意调用方法是: 不是= ,和python不一样

函数更高级点的使用

前面说了函数也是对象,也就是说函数可以变成变量,进行赋值传递等,可以赋值给变量,可以在函数之间传递,作为参数或者返回值等
对函数对象进行赋值传递等并没有运行函数,在函数对象后面加上括号和参数就是运行函数了

从这里可以看到,函数式编程的一个优点,相当于把Java等语言里面的接口方法或类方法提出来了,要对一个方法进行操作时,不要再弄很多的外包包裹的模板式代码,更加简洁灵活。

更进一步的,可以在参数列表或者返回值直接定义函数头并使用,方便很多,不用类似Java,要在其它位置先定义好方法接口等才能使用。
例如:
Future的then方法,定义了onValue函数,调用这个函数的函数需要实现方法体,类似于Java实现接口,然后跟简洁,没有包裹的类的相关内容。
Future then(FutureOr onValue(T value), {Function? onError});

匿名函数或者说闭包

([[类型] 参数[, …]]) {
函数体;
};

下面代码定义了只有一个参数 item 且没有参数类型的匿名方法。 List 中的每个元素都会调用这个函数,打印元素位置和值的字符串:
const list = [‘apples’, ‘bananas’, ‘oranges’];
list.forEach((item) {
print(‘${list.indexOf(item)}: $item’);
});

函数体内只包含一个表达式,你可以使用简写语法:
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
=> 有时也称之为 箭头 函数

例子,匿名函数的传递使用
用法
var loudify = (msg) => ‘!!! ${msg.toUpperCase()} !!!’;
assert(loudify(‘hello’) == ‘!!! HELLO !!!’);

词法闭包

这个概念稍微复杂一些,简单的说 就是有状态的函数
也可以简单的理解,类似Java的匿名内部类,函数是对象,我们在外部函数里面再定义内部类或者函数,并创建其对象,此时这个内部类或者函数的对象持有了它的外部函数,就保存了外部函数的状态,并可以访问了

Dart 具有链式作用域,也就是说,子作用域可以访问父(甚至是祖先)作用域中的变量,而反过来不行。

面向对象相关

dart的继承机制和Java基本一致,要稍微的不同

类定义-构造函数

所有实例变量均会隐式地声明一个 Getter 方法。非final 和 late final 声明但未声明初始化的实例变量还会隐式地声明一个 Setter 方法。
构造函数:
独有的形式,
类名.xxx(…){
}
比如
class Person {
String? firstName;

Person.fromJson(Map data) {
print(‘in Person’);
}
}
在构造函数后面可以增加表达式,通过:语法
比如
显示继承父类构造函数
class Employee extends Person {
// Person does not have a default constructor;
// you must call super.fromJson().
Employee.fromJson(super.data) : super.fromJson() {
print(‘in Employee’);
}
}

在构造函数体执行之前初始化实例变量。每个实例变量之间使用逗号分隔。
// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map<String, double> json)
: x = json[‘x’]!,
y = json[‘y’]! {
print(‘In Point.fromJson(): ($x, $y)’);
}

使用 assert 来验证输入数据
等等

构造函数可以没有函数体,直接在参数里面编写表达式,或者:后面编写表达式完成构造 这是很常用的一种方式 eg
class Point {
double x, y;

// The main constructor for this class.
Point(this.x, this.y);
注意这里参数类型和名字都省了,参数名字直接和属性名字一样就行

// Delegates to the main constructor.
Point.alongXAxis(double x) : this(x, 0);
}
一个小的问题点:
构造函数

工厂构造函数 使用 factory 关键字标识类的构造函数并非总是会返回新的实例对象

支持操作符重载

class Vector {
    
    
  final int x, y;

  Vector(this.x, this.y);

  Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
  Vector operator -(Vector v) => Vector(x - v.x, y - v.y);

  // Operator == and hashCode not shown.
  // ···
}

抽象类 和接口 implement

在dart中,使用implement实现一个类就把这个类当成接口使用了,自动隐藏该类的方法实现代码,然后方法和成员变量都会暴露给实现类。
相对于传统的设计,这是一个很妙的设计阿,显得好很多!相当相当的灵活简洁
1、任何一个类都可以当成接口
2、定义接口的同时加添加了一个默认实现,不用先写一遍接口,然后再实现一遍
3、查看代码,或者对接口添加实现时,可以直接看到默认实现

抽象方法不用写关键字,自动判断
// This class is declared abstract and thus
// can’t be instantiated.
abstract class AbstractContainer {
// Define constructors, fields, methods…

void updateChildren(); // Abstract method.
}

mixin、with语法

简单的说,就是多重继承的作用
关于重复,后一个混入的 mixins会覆盖前面一个 mixins的特性

属性 成员相关

get set

set xxx 相当于同时定义了xxx变量,不用再次定义了,极简风格

重写类成员

Overrides a class member
子类可以重写父类的实例方法(包括 操作符)、 Getter 以及 Setter 方法。你可以使用 @override 注解来表示你重写了一个成员

扩展方法-重点

扩展方法,直接在api提供的类上面增加方法,而不用自己写一个工具类
注意,看名字,它只能扩展
例子

extension StringExtension1 on String {
//字符转换成Color对象
toColor() {
var hexColor = this.replaceAll(“#”, “”);
if (hexColor.length == 6) {
hexColor = “FF” + hexColor;
}
if (hexColor.length == 8) {
return Color(int.parse(“0x$hexColor”));
}
}

//字符转int
parseInt() {
return int.parse(this);
}
}

extension DateTimeExtension on DateTime {
toFormatString() {
DateFormat dateFormat = new DateFormat(“yyyy-MM-dd HH:mm:ss”);
return dateFormat.format(this);
}
}

类型相关

判断类型
xx is A
xx is ! A
类型转换
xx as A
获取类型
Type type = xx.runtimeType 或者 type = CLASSXXX
然后用 == 判断

单例的实现,就是使用 常量构造函数。

可调用类

通过实现类的 call() 方法,允许使用类似函数调用的方式来使用该类的实例。
class WannabeFunction {
String call(String a, String b, String c) => ‘$a $b $c!’;
}

var wf = WannabeFunction();
var out = wf(‘Hi’, ‘there,’, ‘gang’);

void main() => print(out);

Typedefs 这个不常用 增加复杂度,收效也不大

类型别名是引用某一类型的简便方法,因为其使用关键字 typedef,因此通常被称作 typedef。下面是一个使用 IntList 来声明和使用类型别名的例子:
typedef ListMapper = Map<X, List>;
Map<String, List> m1 = {}; // Verbose.
ListMapper m2 = {}; // Same thing but shorter and clearer.

异常处理

throw FormatException(‘Expected at least 1 section’);
你也可以抛出任意的对象:throw ‘Out of llamas!’;
优秀的代码通常会抛出 Error 或 Exception 类型的异常。

捕获异常
语法也是
try {
} catch (e) {
}
由于类型不明确?需要用on 来指定异常类型,具体用法
try {
breedMoreLlamas();
} on OutOfLlamasException {
// A specific exception
buyMoreLlamas();
} on Exception catch (e) {
// Anything else that is an exception
print(‘Unknown exception: $e’);
} catch (e) {
// No specified type, handles all
print(‘Something really unknown: $e’);
}

你可以为 catch 方法指定两个参数,第一个参数为抛出的异常对象,第二个参数为栈信息 StackTrace 对象

try {
// ···
} on Exception catch (e) {
print(‘Exception details:\n $e’);
} catch (e, s) {
print(‘Exception details:\n $e’);
print(‘Stack trace:\n $s’);
}

泛型

因为动态语言的特性,实际上dart中泛型的作用主要是用来限制类型实现类型安全,而不是表达多种类型,是相反的功能
比如var names = []; 限定string的列表
泛型一样支持extend
class Foo {
// Implementation goes here…
String toString() => “Instance of ‘Foo<$T>’”;
}

依赖 库 包 导入 和可见性

添加依赖库

dart的包不像Java需要打包成jar,直接导入文件夹就行,所以更加简单,灵活,可以从多种位置添加依赖
如包仓库 git 本地等

包仓库

dependencies:
transmogrify: ^1.4.0
**如果希望使用自己的包存储库,**可以使用托管服务器指定其 URL。下面的 YAML 代码使用托管源创建对 transmogrify 包的依赖项:
dependencies:
transmogrify:
hosted: https://some-package-server.com
version: ^1.4.0
dart中,任意一个文件都会被认为是一个库,尽管其中可能并没有library标签
以下划线(_)开头的成员仅在代码库中可见。
^号是版本约束,表示兼容的几个版本,具体是哪些版本兼容。
通常的依赖
For example, ^1.2.3 is equivalent to ‘>=1.2.3 <2.0.0’, and ^0.1.2 is equivalent to ‘>=0.1.2 <0.2.0’. The following is an example of caret syntax:

例如,^ 1.2.3等于’> = 1.2.3 < 2.0.0’,^ 0.1.2等于’> = 0.1.2.0.2.0’。下面是插入符号语法的一个示例:

Git 软件包

dependencies:
kittens:
git: https://github.com/munificent/kittens.git
git 还可以指定分支

本地包

dependencies:
kittens:
path: xxxx 根目录即可

依赖重写

如果出现依赖冲突,就是依赖树中两个节点依赖另一个节点,但是要求的版本不同
这时候使用依赖重写指定使用同一个版本
dependency_overrides:
json_serializable: ^6.2.0

import 的唯一参数是用于指定代码库的 URI
引入dart语言的内置库:
import ‘dart:math’;
引入pub包管理器提供库:
import ‘package:flutter/material.dart’;
在引用包管理器提供的库时,uri中以package开头

引入本地文件:
import ‘./tools/network.dart’;
引用本地文件时,uri字符串中直接填写文件的相对路径。

import 支持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();

export关键字的使用

就是将多个库文件一起导入的,类似于python的init文件,

在这个入口文件中,使用export关键字将这些dart文件集中导出。然后其他地方使用的使用只需要import这个入口文件就可以了。

延迟加载库

为了减少应用的初始化时间。

处理 A/B 测试,比如测试各种算法的不同实现。

加载很少会使用到的功能,比如可选的屏幕和对话框。

使用 deferred as 关键字来标识需要延时加载的代码库:
import ‘package:greetings/hello.dart’ deferred as hello;
// 当实际需要使用到库中 API 时先调用 loadLibrary 函数加载库:
Future greet() async {
await hello.loadLibrary();
hello.printGreeting();
}

loadLibrary 函数可以调用多次也没关系,代码库只会被加载一次。

并发 异步 多线程

Dart 通过 async-await、isolate 以及一些异步类型概念(例如 Future 和 Stream)支持了并发代码编程。

首先,dart的基本线程模型

dart趋向于淡化线程的概念,用隔离区isolate和任务的概念来完成并发功能,让上层开发者不必关心线程的调度,把线程调度交给语言自身,(某种角度的理解,传统的多线程以线程为中心,让线程分配资源,dart反过来,以资源为中心,分配线程)
在这里插入图片描述
系统线程和isolate之间的关系是有一点模糊的,高度取决于DartVM在一个应用中是如何被嵌入的,仅有以下两点可以确保:

同一时间一个系统线程只能进入到一个isolate中,如果要进入到另外一个,那么它必须先离开当前的isolate
同一时间有且仅有一个执行Dart代码的系统线程(Mutator线程)和一个isolate有关联。

简单的说,同一时间点,isolate和系统线程是一一对应的。
默认的Dart 应用只会使用一个 isolate(即 主 isolate),同时你也可以创建更多的 isolate,从而在多个处理器内核上达成并行执行代码的目的。 **isolate 仅针对 原生平台的使用 进行实现。**使用 Dart 构建的网页应用可以 使用 Web Workers 实现相似的功能。
isolate运行事件循环的独立线程,类似于Android的handler loop机制。

创建新的isolate

官网文档地址 https://dart.cn/guides/language/concurrency
isolate大致相当于线程,那么我们需要创建多个线程,同时运行到机器的多核CPU上,怎么操作?
自然的,就像创建新线程一样,创建一个新的isolate,不同的是isolate不共享内存,通过消息管道来传递数据,正如那句话,通过传递消息共享内存,而不是通过共享内存传递消息。
创建isolate并运行和通信的基本流程如下:
Flutter 开发提示: 如果你在非 Web 平台上使用 Flutter 进行开发,那么与其直接使用 Isolate API,可以考虑使用 Flutter 提供的 compute() 方法。 compute() 方法能以简单的方式将一个函数的调用封装至 isolate 工作对象内。
下面的示例将使用到这些与 isolate 相关的 API:
Isolate.spawn() 和 Isolate.exit()
ReceivePort 和 SendPort

Isolate 工作对象会执行一个函数,完成后结束对象,并将函数结果发送至主 isolate。(Flutter 提供的 compute() 方法 也是以类似的方式工作的。)

主 isolate 的代码如下:

void main() async {
// Read some data.
final jsonData = await _parseInBackground();
// Use that data
print(‘Number of JSON keys: ${jsonData.length}’);
}

Future<Map<String, dynamic>> _parseInBackground() async {
final p = ReceivePort(); // 代码创建了一个 ReceivePort,让 isolate 工作对象可以传递信息至主 isolate。
await Isolate.spawn(_readAndParseJson, p.sendPort); // 创建,传入运行函数和通信接口
return await p.first as Map<String, dynamic>;
}
_parseInBackground() 方法包含了 生成 后台 isolate 工作对象的代码,并返回结果:
在生成 isolate 之前,

Isolate 初始化完成后,主 isolate 即开始等待它的结果。由于 ReceivePort 实现了 Stream,你可以很方便地使用 first 属性获得 isolate 工作对象返回的单个消息。

在 isolate 之间发送多次消息内容
如果你想在 isolate 之间建立更多的通信,那么你需要使用 SendPort 的 send() 方法。下图展示了一种常见的场景,主 isolate 会发送请求消息至 isolate 工作对象,然后它们之间会继续进行多次通信,进行请求和回复。

Future和Stream(异步数据流)

Dart 语言和库通过 Future 和 Stream 对象,来提供能在当前调用未来返回某些值的功能
在 Dart 中一个最终会返回 int 类型值的 ,应当声明为 Future;一个会持续返回一系列 int 类型值的应当声明为 Stream。
异步场景包括调用系统 API,例如非阻塞的 I/O 操作、HTTP 请求或与浏览器交互。还有一些场景是利用 Dart 的 isolate 进行计算,或等待一个计时器的触发。这些场景要么是在不同的线程运行,要么是被系统或 Dart 处理,而让 Dart 代码可以同时运行其它代码。

async-await 语法

async 意思就是异步,用来声明来定义异步函数。异步函数不会连续执行返回结果,线程可能执行了其它位置的代码,再执行该函数返回结果,也就是异步的。

await 意思是异步等待,自然的是相对于同步等待。
同步等待就是卡住线程,直到获取结果,异步等待就是不卡住线程,线程执行其它代码,有结果返回之后再通知线程运行这里的代码。
函数里面使用了await之后,函数也就变成了一个异步函数,不再是一般的函数了,线程不会连续执行到这个函数的末尾,此时函数必须添加异步标识符,即函数后添加 async 。这种情况下,对于一些函数就不能使用await关键字,比如继承的函数或者接口。

下面是一段同步代码调用文件 I/O 时阻塞的例子:

void main() {
// Read some data.
final fileData = _readFileSync();
final jsonData = jsonDecode(fileData);

// Use that data.
print(‘Number of JSON keys: ${jsonData.length}’);
}

String _readFileSync() {
final file = File(filename);
final contents = file.readAsStringSync();
return contents.trim();
}
下面是类似的代码,但是变成了 异步调用:

void main() async {
// Read some data.
final fileData = await _readFileAsync();
final jsonData = jsonDecode(fileData);

// Use that data.
print(‘Number of JSON keys: ${jsonData.length}’);
}

Future _readFileAsync() async {
final file = File(filename);
final contents = await file.readAsString();
return contents.trim();
}
main() 函数在调用 _readFileAsync() 前使用了 await 关键字,让原生代码(文件 I/O)执行的同时,其他的 Dart 代码(例如事件处理器)能继续执行。使用 await 后,_readFileAsync() 调用返回的 Future 类型也转换为了 String。从而在将结果 content 赋予变量时,隐式转换为 String 类型。

备注:

asyn await 的执行流程应该是,await之后事件循环终止主线程循环体后面代码的执行,相当于一个continue,然后后面等结果返回了,再从这里开始运行。

元数据 注解

元数据注解以 @ 开头,其后紧跟一个编译时常量(比如 deprecated)或者调用一个常量构造函数,就像调用函数一样,可以往里面传递参数。

library todo;

class Todo {
final String who;
final String what;

const Todo(this.who, this.what);
}

import ‘todo.dart’;

@Todo(‘seth’, ‘make this do something’)
void doSomething() {
print(‘do something’);
}

注释

注释里面可以加图,语法和Markdown一样!!! Java也支持,语法是html还是什么的
目前Android Studio 插件不支持类跳转
文档注释以 /// 或者 /** 开始

dart的运行架构和原理

Dart 的编译器技术有两种方式运行代码:
原生平台: 对于针对移动和桌面设备的应用程序,Dart 包括一个具有即时(JIT)编译的 Dart 虚拟机 和一个用于生成机器码的提前(AOT)编译器。AOT就是在开发者点击编译之前,提前将代码编译成机器码(是真的机器码?能直接在cpu运行的那个机器码?),加快编译速度和程序运行时对机器想性能效果。

Web 平台: 对于面向 Web 的应用程序,Dart 包括开发时编译器(dartdevc)和生产时编译器(dart2js)。两个编译器都将 Dart 翻译成 JavaScript。

在开发过程中,快速的开发周期对于迭代是至关重要的。Dart VM 提供了一个即时编译器(JIT) ,具有增量重新编译(启用热重载)、实时度量收集(支持 DevTools)和丰富的调试支持。
经过 AOT 编译的代码会在高效的 Dart 运行环境中运行,该运行环境拥有健全的 Dart 类型系统,并使用快速对象分配和 分代垃圾收集器 来管理内存。

Dart 的 Web 支持让你可以在 JavaScript 驱动的网页平台上运行 Dart 代码。使用 Web 环境下的 Dart 时,你可以将 Dart 编译为在浏览器中运行的 JavaScript 代码,例如: Chrome 中的 V8。

Dart Web 包含了可以实现快速开发周期的增量开发编译器,同时还有用于针对生产环境优化的编译器 dart2js。后者使用了类似移除不可访问代码的技术,将 Dart 代码编译为快速、紧凑、随时随地可部署的 JavaScript。

Dart 运行时环境

不论你在哪个平台上使用、选择如何构建你的代码,执行代码时都需要一个 Dart 运行时环境。这个运行时环境负责下面的关键任务:

内存管理: Dart 使用一个受管理的内存模型,未被使用的内存会由垃圾收集器 (GC) 回收。

执行 Dart 语言的类型体系: Dart 语言里大多数类型检查都是静态的(编译时),但仍有部分检查是动态的(运行时)。比如,Dart 运行时环境会在遇到 类型判断运算符 时执行动态检查。

管理 isolates: Dart 运行时环境会负责控制主 isolate(代码通常在这里运行)以及其他应用创建的 isolate。

在原生平台上,Dart 运行时环境被自动包含在独立的可执行文件中,是 dart run 命令提供的 Dart VM 的一部分

注意:运行时,虚拟机两个东西差不多,就是进行内存管理,代码执行管理,线程进程管理等管理工作的。

热重载概念

注意dart热重载时会重新构建代码,即使代码没有改动(2022.7 目前是这样)。

Flutter 框架

是一款流行的多平台 UI 工具包,由 Dart 语言强力驱动,提供一套工具和 UI 库,帮助开发者们在 iOS、Android、macOS、Windows、Linux 和 Web 平台构建优秀的 UI 体验。

dart开发时的一些特点

方法参数列表里面不是简单的写几个变量了,已经变成写成块的代码了,不像是方法体,而像是方法头了,里面的代码量和大括号一样多了了。

工具使用相关

dart pub upgrade 命令与 dart pub get 命令一样,都是用于获取依赖项的。
不同的是 dart pub upgrade 命令会忽略掉任何已存在的 lockfile 文件,因此 Pub 可以获取所有依赖项的最新版本。
意思应该是get不会升级版本,upgrade会自动升级版本。

猜你喜欢

转载自blog.csdn.net/hn_lgc/article/details/126088042
今日推荐