@TryLoveCatch
2020-06-04T16:10:11.000000Z
字数 14209
阅读 1529
flutter
dart
不会Dart,还搞什么Flutter?
1.4 Dart语言简介
它们的区别在于,const比final更加严格。
int Func() {
// 代码
}
final int m1 = 60;
final int m2 = Func(); // 正确
const int n1 = 42;
const int n2 = Func(); // 错误
在上面的例子中,虽然m2的值是不变的,但如果不把代码运行起来,你无法知道m2是多少。
assert(条件, 可选信息)
int number = 200;
assert(number > 100);// 正常
assert(number < 100);// 报错
assert(urlString.startsWith('https'),
'URL ($urlString) should start with "https".');
Assert不会影响生产环境中代码的执行,仅仅在Flutter的调试模式时生效。(如果你是用 Android Studio 的 bug 图标运行,或者在命令行执行 flutter run,则默认会使用 debug 模式。)
定义函数时,使用{param1, param2, …}的形式来指定命名参数:
coffeeFlavor ({bool sugar , bool sugar}) {
}
调用方法的时候,可以使用 paramName: value 的形式来指定参数的名称,这样就可以根据paramName得知参数的含义,提高代码的可读性。
coffeeFlavor(sugar :true ,sugar :false );
把函数的参数放到 [] 中就变成可选位置参数了:
String go(String to, [String who]) {
var result = 'go to the $to';
if (who != null) {
result = '$result with $who';
}
return result;
}
go('beijing');
go('beijing', 'me');
可以使用 = 来定义可选参数的默认值, 默认值必须是编译时常量。如果没有提供默认值,则默认值为 null。
String go(String to, [String who= 'me']) {
var result = 'go to the $to';
if (who != null) {
result = '$result with $who';
}
return result;
}
String result= go ('beijing');// beijing with me
没有名字的匿名方法,格式如下所示:
([[Type] param1[, …]]) {
codeBlock;
};
定义了一个参数为i(该参数没有指定类型)的匿名函数。list中的每个元素都会调用这个函数打印出来:
var list = ['张无忌', '风清扬', '张三丰', '独孤求败', '萧峰'];
list.forEach((i) {
print(list.indexOf(i).toString() + ': ' + i);
});
这个在集合操作里面比较常见:
E firstWhere(bool test(E element), {E orElse()}) {
for (E element in this) {
if (test(element)) return element;
}
if (orElse != null) return orElse();
throw IterableElementError.noElement();
}
void forEach(void f(E element)) {
for (E element in this) f(element);
}
上面两个例子的bool test(E element)
就都是函数作为参数
调用可以有两种方式:
var list = ['张无忌', '风清扬', '张三丰', '独孤求败', '萧峰'];
void print(var test) {
print(test);
}
/// 注意print,没有括号,不是调用
list.forEach(print);
/// 匿名函数
list.forEach((i) => print(i));
标准的 for 循环:
var message = new StringBuffer("张无忌");
for (var i = 0; i < 3; i++) {
message.write('!');
}
List和Set等实现了Iterable接口的类还支持for-in形式的遍历:
var hero = ['张无忌', '风清扬', '张三丰', '独孤求败', '萧峰'];
for (var h in hero) {
print(h);
}
try {
//...
} on Exception1 {
//...
} on Exception2 catch (e) {
print('exception: $e');
} catch (e) {
print('Something really unknown: $e');
} finally {
//...
}
Dart是一个面向对象编程语言,支持基于Mixin的继承机制。Mixin可以理解为多继承,在with关键字的后面为一个或者多个类。
class Person{
run(){
print('跑');
}
}
class Wushu{
use(){
print('乾坤大挪移');
}
}
class Zhangwuji extends Person with Wushu{
int age;
Zhangwuji(int age){
this.age=age;
}
}
void main() {
var zhangwuji=new Zhangwuji(30);
zhangwuji.run();
zhangwuji.use();
}
import 'dart:io';
import 'package:mylib/mylib.dart';
import 'package:utils/utils.dart';
import '../lib/my.dart';
如果导入的两个库具有冲突的名字, 可以使用库的前缀来进行区分。例如,如果library1和library2 都有一个名字为Element的类,可以这样使用:
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// ...
Element element1 = new Element(); //使用lib1中的Element
lib2.Element element2 = new lib2.Element(); //使用lib2中的Element
如果只使用库的一部分功能,则可以选择需要导入的部分内容。其中show代表只导入指定的部分,hide代表除了指定的部分都导入。
// 只导入foo
import 'package:lib1/lib1.dart' show foo;
// 除了foo,其他部分都导入
import 'package:lib2/lib2.dart' hide foo;
延迟加载意味着应用程序可以在需要的时候再加载库,使用延迟加载库的场景主要有以下几点:
要延迟加载一个库,需要先使用 deferred as来导入:
import 'package:deferred/hello.dart' deferred as hello;
当需要使用的时候,调用loadLibrary() 函数来加载库:
greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
简单说下,Dart的事件循环机制:
- 循环中有两个队列。一个是微任务队列(MicroTask queue),一个是事件队列(Event queue)。
- 事件队列包含外部事件,例如I/O, Timer,绘制事件等等。
- 微任务队列则包含有Dart内部的微任务,主要是通过scheduleMicrotask来调度。
Dart的事件循环的运行遵循以下规则:
- 首先处理所有微任务队列里的微任务。
- 处理完所有微任务以后。从事件队列里取1个事件进行处理。
- 回到微任务队列继续循环。
也就是说:
注意第一步里的所有,也就是说在处理事件队列之前,Dart要先把所有的微任务处理完。如果某一时刻微任务队列里有8个微任务,事件队列有2个事件,Dart也会先把这8个微任务全部处理完再从事件队列中取出1个事件处理,之后又会回到微任务队列去看有没有未执行的微任务。
调用scheduleMicrotask来让代码以微任务的方式异步执行
scheduleMicrotask((){
print('a microtask');
});
调用Timer.run来让代码以Event的方式异步执行
Timer.run((){
print('a event');
});
仅仅使用回调函数来做异步的话很容易陷入“回调地狱(Callback hell)”,为了避免这样的问题,JS引入了Promise。同样的, Dart引入了Future。
要使用Future的话需要引入dart.async
import 'dart:async';
Future(() => print('立刻在Event queue中运行的Future'));
Future.delayed(const Duration(seconds:1), () => print('1秒后在Event queue中运行的Future'));
Future.microtask(() => print('在Microtask queue里运行的Future'));
Future.sync(() => print('同步运行的Future'));
这里要注意一下:
- Future.sync()的同步运行指的是构造Future的时候传入的函数是同步运行的
- Future.sync()通过then串进来的回调函数是调度到微任务队列异步执行的。
Future(()=> throw 'we have a problem')
.then((_)=> print('callback1'))
.then((_)=> print('callback2'))
.catchError((error)=>print('$error'))
.whenComplete(()=> print('whenComplete'));
这里也需要注意一下:
- then串起来的回调函数会按照链接的顺序依次执行
- then串起来的回调函数有可能同步也有可能异步
- catchError可以捕获task和then的异常
- whenComplete一定会被执行,无论Future是正常执行完毕还是抛出异常
针对上买呢的第二条,我们再来说一点:
- 你通过then串起来的那些回调函数在Future完成的时候会被立即执 行,也就是说它们是同步执行,而不是被调度异步执行。
- 如果Future在调用then串起回调函数之前已经完成,
那么这些回调函数会被调度到微任务队列异步执行。这个就是Future.sync()- 通过Future()和Future.delayed()实例化的Future不会同步执行,它们会被调度到事件队列异步执行。
- 通过Future.value()实例化的Future会被调度到微任务队列异步完成,类似于第2条。
- 通过Future.sync()实例化的Future会同步执行其入参函数,然后(除非这个入参函数返回一个Future)调度到微任务队列来完成自己,类似于第2条。
Future相对于调度回调函数来说,缓减了回调地狱的问题。但是如果Future要串起来的的东西比较多的话,代码还是会可读性比较差。特别是各种Future嵌套起来,是比较烧脑的。
async和await是什么?它们是Dart语言的关键字,可以让你用同步代码的形式写出异步代码。
看一个例子:
foo() async {
print('foo E');
String value = await bar();
print('foo X $value');
}
bar() async {
print("bar E");
return "hello";
}
main() {
print('main E');
foo();
print("main X");
}
执行结果:
main E
foo E
bar E
main X
foo X hello
我们主要来看这个:
foo() async {
print('foo E');
String value = await bar();
print('foo X $value');
}
这个会被分成两部分:
foo() async {
print('foo E');
Future future = bar()
---------------------------
String value = await future;
print('foo X $value');
}
分割线上面的会先执行,await后面的必须是一个Future,然后,分割线下面的就会以then的方式链入这个Future被异步调度执行。
相当于:
foo() {
print('foo E');
return Future.sync(bar).then((value) => print('foo X $value'));
}
- async标注的函数其返回值类型是Future
- await后面必须是Future对象
- await并不像字面意义上程序运行到这里就停下来啥也不干等待Future完成。而是立刻结束当前函数的执行并返回一个Future。函数内剩余代码通过调度异步执行。
- await只能在async函数中出现。
- async函数中可以出现多个await,每遇见一个就返回一个Future, 实际结果类似于用then串起来的回调。
- async函数也可以没有await, 在函数体同步执行完毕以后返回一个Future,相当于第一条
另外,使用async和await还有一个好处是我们可以用和同步代码相同的try/catch机制来做异常处理。
foo() async {
try {
print('foo E');
var value = await bar();
print('foo X $value');
} catch (e) {
// 同步执行代码中的异常和异步执行代码的异常都会被捕获
} finally {
}
}
如果Dart中的类实现了call()函数,那么这个类可以当做方法来调用。
class JointFunction {
call(String a, String b, String c, String d) => '$a $b $c $d';
}
main() {
var jf = new JointFunction();
var out = jf("放","手","去","做");
print('$out');
}
在Java中创建实例可以用new,在Dart中你可以选择用new,也可以选择不用:
Element element = Element();
在 Dart 语言中使用下划线前缀标识符,会 强制其变成私有。
Dart构造函数介绍
Dart学习笔记(五):类
通过类的构造函数给实例添加变量:
main(List<String> args) {
Dog d = new Dog('Duffy', 2);
print(d.name);
}
class Dog {
String name;
int age;
Dog(String name, Stirng age) {
this.name = name;
this.age = age;
}
}
main(List<String> args) {
Dog d = new Dog('Duffy', 2);
print(d.name);
}
class Dog {
String name;
int age;
// 这里~~~~~~~~
Dog(this.name, this.age);
}
第一个参数,对应到name
第二个参数对应age
main(List<String> args) {
Dog d = new Dog.newBorn();
print(d.name);
}
class Dog {
String name;
int age;
Dog(this.name, this.age);
// 这里~~~~~~~
Dog.newBorn() {
name = 'Doggy';
age = 0;
}
}
我们在flutter的plugin里面会发现很多类似的用法:
class GoogleMapController {
GoogleMapController._(
this.channel,
CameraPosition initialCameraPosition,
this._googleMapState,
) : assert(channel != null) {
channel.setMethodCallHandler(_handleMethodCall);
}
...
}
类似于这种._
,这个私有的命名构造函数。
_
既表示是私有,又表示命名,是命名构造函数。
class ImmutablePoint {
final num x, y;
const ImmutablePoint(this.x, this.y);
}
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);
identical(a, b); // true
var a = ImmutablePoint(1, 1);
var b = ImmutablePoint(1, 1);
identical(a, b); // false
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 2);
identical(a, b); // false
identical,测试两个对象是否是同一个对象(indentity test)
而==
是我们自己实现的一个方法,是否==取决于你的实现方式
@override
bool operator ==(Object other) {
if (identical(this, other)) {
return true;
}
if (runtimeType != other.runtimeType) {
return false;
}
return other is ImmutablePoint
&& other.a == a
&& other.b == b;
}
runtimeType可以获得一个示例的类型,该属性返回一个Type对象
main() {
int a = 1;
print('type of a is ${a.runtimeType}');// 输出:type of a is int
}
一般来说:
Pug类继承了Dog类,并且在Pug的构造函数里用SUPER关键字调用了Dog类的构造函数
main(List<String> args) {
Pug p = new Pug('Duffy', 5);
print(p.name);
}
class Dog {
String name;
int age;
Dog(this.name, this.age);
Dog.newBorn() {
name = 'Doggy';
age = 0;
}
}
// 这里~~~~~~~~~
class Pug extends Dog {
Pug(String name, int age): super(name, age);
}
在一个类里还可以调用其他的构造函数,通过在(:)冒号后调用this关键字
main(List<String> args) {
Pug p = new Pug.small('Duffy');
print(p.name);
}
class Dog {
String name;
int age;
Dog(this.name, this.age);
Dog.newBorn() {
name = 'Doggy';
age = 0;
}
}
// 这里~~~~~~~~~
class Pug extends Dog {
Pug(String name, int age): super(name, age);
Pug.small(Stirng name): this(name, 1);
Pug.large(Stirng name): this(name, 3);
}
方法重写
main(List<String> args) {
Pug p = new Pug.small('Duffy');
p.bark();
}
class Dog {
String name;
int age;
Dog(this.name, this.age);
Dog.newBorn() {
name = 'Doggy';
age = 0;
}
bark() {
print('Bow Wow');
}
}
class Pug extends Dog {
Pug(String name, int age): super(name, age);
Pug.small(Stirng name): this(name, 1);
Pug.large(Stirng name): this(name, 3);
// 这里~~~~~~~~~
@override
bark() {
print('Meow!');
}
}
默认的情况下类里定义的任何变量都是可以访问的,如dog.name,变量的值也可以直接读写。但是有时候不希望直接读写而是通过getter setter。dart里 是通过get set 关键字实现的:
main(List<String> args) {
Dog d = new Dog('Duffy', 5);
d.respectedName = 'Mr.Duffy';
print(d.respectedName);
}
class Dog {
String name;
int age;
Dog(this.name, this.age);
// 这里~~~~~~~~~
String get respectedName {
return 'Mr.$name';
}
// 这里~~~~~~~~~
set respectedName(String newName) {
name = newName;
}
Dog.newBorn() {
name = 'Doggy';
age = 0;
}
bark() {
print('Bow Wow');
}
}
上面的代码 name 属性依然是可读写的
默认类里的任何属性都是public的,也是是公开的,可以直接访问,如果想保护变量,使变量变成私有的private,只要在变量名称前加"_"即可
类前增加了 abstract 关键字
abstract class AbstractDog {
void bark();
void _hiddenMethod();
}
在字段或方法前增加static关键字就变成了静态
main(List<String> args) {
Dog.bark();
}
class Dog {
String name;
int age;
Dog(this.name, this.age);
// 这里~~~~~~~~~
static bark() {
print('Bow Wow');
}
}
main(List<String> args) {
Dog d = new Dog('Duffy', 12, CurrentState.sleeping);
print(d.state == CurrentState.sleeping); //Prints 'true'
}
// 这里~~~~~~~~~
enum CurrentState {
sleeping,
barking,
eating,
walking
}
class Dog {
String name;
int age;
CurrentState state;
Dog(this.name, this.age, this.state);
static bark() {
print('Bow Wow');
}
}
dart支持泛型,如有一个类,管理一个数据,希望这个数据是任何类型。
main(List<String> args) {
DataHolder<String> dataHolder = new DataHolder('Some data');
print(dataHolder.getData());
dataHolder.setData('New Data');
print(dataHolder.getData());
}
// 这里~~~~~~~~~
class DataHolder<T> {
T data;
DataHolder(this.data);
getData() {
return data;
}
setData(data) {
this.data = data;
}
}
判断两个引用是否指向同一个对象,是否是同一个内存地址
class Person {
String ssn;
String name;
Person(this.ssn, this.name);
// Define that two persons are equal if their SSNs are equal
bool operator ==(other) {
return (other is Person && other.ssn == ssn);
}
}
main() {
var bob = new Person('111', 'Bob');
var robert = new Person('111', 'Robert');
print(bob == robert); // true
print(identical(bob, robert)); // false, because these are two different instances
}
另外一个例子,工厂构造函数:
class Logger {
final String name;
bool mute = false;
// 缓存实例
static final Map<String, Logger> _cache = <String, Logger>{};
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final logger = new Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) {
print(msg);
}
}
}
main() {
var logger1 = new Logger('UI');
var logger2 = new Logger('UI');
identical(logger1, logger2); // true
}
logger1和logger2,指向的是同一个对象
Object类的runtimeType属性,来判断对象的类型,返回一个Type对象:
main() {
int a = 1;
print('type of a is ${a.runtimeType}');
}
// 输出:type of a is int
dart Iterable、Stream
Dart中Iterable、Stream的糖语法
dart里面没有提供类似java中Double的isNan的方法,所以需要我们自己处理,不能直接使用== double.nan
这种方式,方法有两种:
使用compareTo()
if (mDouble.compareTo(double.nan) == 0) {
}
Examples:
* ```
* print(1.compareTo(2)); // => -1
* print(2.compareTo(1)); // => 1
* print(1.compareTo(1)); // => 0
*
* // The following comparisons yield different results than the
* // corresponding comparison operators.
* print((-0.0).compareTo(0.0)); // => -1
* print(double.nan.compareTo(double.nan)); // => 0
* print(double.infinity.compareTo(double.nan)); // => -1
*
* // -0.0, and NaN comparison operators have rules imposed by the IEEE
* // standard.
* print(-0.0 == 0.0); // => true
* print(double.nan == double.nan); // => false
* print(double.infinity < double.nan); // => false
* print(double.nan < double.infinity); // => false
* print(double.nan == double.infinity); // => false
/// 判断double 是否为nan
static bool isNan(double value) {
return (value != value);
}
/// 判断double 是否是infinity
static bool isInfinite(double value) {
return (value == double.infinity) || (value == double.negativeInfinity);
}
条件成员访问,左边的操作数如果为null,就返回null;否则,就跟.操作符一致
var bar = foo?.bar; // 如果 foo 为 null 则返回 null,否则返回 bar 成员
expr1 ?? expr2
如果expr1为非空,则返回其值;否则,计算并返回expr2的值。
b ??= value; // 如果b为null将value赋给b,否则b值不变
// 等价于
if (b == null) {
b = value;
}
级联操作符,允许您对同一对象执行一系列操作
querySelector('#button') // Get an object.
..text = 'Confirm' // Use its members.
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
// 等价于
var button = querySelector('#button');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));
as 类型转换
(emp as Person).firstName = 'Bob';
is 当对象是相应类型时返回 true
if (emp is Person) { // 类型检查
emp.firstName = 'Bob';
}
这种写法只适用函数体中仅有一个表达式的函数,并且返回值为这个表达式的计算结果
=> expr
等同于{ return expr; }
void printNumber(num number) => print(number);
等价于
void printNumber(num number){
print(number);
}
匿名函数也可以这样使用,比如我们上面说到匿名函数的例子:
var list = ['张无忌', '风清扬', '张三丰', '独孤求败', '萧峰'];
list.forEach((i) {
print(list.indexOf(i).toString() + ': ' + i);
});
可以改写为:
var list = ['张无忌', '风清扬', '张三丰', '独孤求败', '萧峰'];
list.forEach((i) => print(list.indexOf(i).toString() + ': ' + i));
native传递json给dart侧,如下:
Map tMap = new HashMap(1);
JSONObject tJsonObject = xxxx;
if (tJsonObject != null) {
tMap.put("info", tJsonObject.toString());
mChannelMethod.invokeMethod("update", tMap);
}
dart接收如下:
dynamic info = call.arguments['info'];
if (info != null) {
try {
Info tInfo = Info.fromJson(info);
} catch(e) {
Logger.getInstance().d(_TAG, '$e', 'info');
}
}
在Info.fromJson(tJson);这一行报错,报错如下:
type 'String' is not a subtype of type 'int' of 'index'
解决:
import 'dart:convert';
dynamic info = call.arguments['info'];
if (info != null) {
try {
var tJson = json.decode(info);
Info tInfo = Info.fromJson(tJson);
} catch(e) {
Logger.getInstance().d(_TAG, '$e', 'info');
}
}
这个是因为传过来的info是String类型的,所以需要解析一下才能使用
native传递Bitmap给dart侧,然后通过JSON传递List
接收的代码:
Uint8List.fromList(json['directionIcon'])
然后报错,如下:
type 'List<dynamic>' is not a subtype of type 'List<int>'
修改
Uint8List.fromList(json['directionIcon'].cast<int>())