一、小技巧
1、不占空间的空组件
SizedBox.shrink()
2、组件是否显示 - if
if (provider.pageState == DataStatus.completed) BottomMenuWidget(),
3、私有变量以下划线开头
Dart没有public
,protected
,private
关键字,私有变量以下划线开头
未初始化的变量的初始值为
null
,即使数字类型的初始值也是null
,因为在Dart中, everything is object!
dart的类实现call()方法可以像函数一样被调用
class WannabeFunction {
call(String a, String b, String c) => '$a $b $c!';
}
main() {
var wf = new WannabeFunction();
var out = wf("Hi","there,","gang");
print('$out'); // Hi there, gang!
print(wf.runtimeType); // WannabeFunction
print(out.runtimeType); // String
print(wf is Function); // false
}
4、Flutter页面重叠写法
1、叠加布局
Stack(
children: [
Positioned(
top: -10,
child: Container(
// Container的其他属性和子组件
),
),
],
)
2、Transform.translate组件来进行位移调整
Transform.translate(
offset: Offset(0, -10),
child: Container(
// Container的其他属性和子组件
),
)
3、Transform组件来应用变换矩阵来实现位移调整
Transform(
transform: Matrix4.translationValues(0.0, -10.0, 0.0),
child: Container(
// Container的其他属性和子组件
),
)
二、核心语法
1、闭包语法
main(){
List ls = ['aa','bb',3];
List ls2 = ['dongnao simon',true,123];
//闭包
Function weGame(List ls,func(a)){
for(int i=0;i<ls.length;i++){
ls[i] = func(ls[i]);
}
return (ls2)=>ls2+ls;
}
var weGame2 = weGame(ls, (a) =>a*2);
print(weGame2(ls2));
}
weGame2(ls2)相当于 weGame(ls, (a) =>a*2)(ls2)
闭包好处防止变量污染
2、函数别名:类似java的抽象类
参考
typedef MyOperator(int a, int b);
// 1.List转String
var list=<String>['a','b'];
String str=JsonEncoder().convert(list);
print(str);
// 2.String转回List
List<String>list1=<String>[];
for(var value in JsonDecoder().convert(str)){
print(value);
list1.add(value);
};
list1.forEach(print);
3、mixins实现多继承
class D extends Person with A, B {
D(String name, num age) : super(name, age);
}
在 Dart 中本不可以实现多继承,利用 mixins 可实现类似多继承的功能。
- 作为 mixins 的类只能继承自 Object,不能继承其他类。
- 作为 mixins 的类不能有构造函数。
- 一个类可以 mixins 多个 mixins 类。
- mixins 绝不是继承,也不是接口,而是一种全新的特性。
示例:
class A {
String info = "this is A";
void printA() {
print("A");
}
void run() {
print("A Run");
}
}
class B {
void printB() {
print("B");
}
void run() {
print("B Run");
}
}
class Person {
String name;
num age;
Person(this.name, this.age);
printInfo() {
print('${this.name}----${this.age}');
}
void run() {
print("Person Run");
}
}
// 使用 with 关键字实现 mixins
class C with A, B {}
// 既继承自 Person 又 mixins A 和 B,with 后跟的类有顺序之分,后类的方法会覆盖前类的方法
class D extends Person with A, B {
D(String name, num age) : super(name, age);
}
void main() {
var c = new C();
c.printA(); // A
c.printB(); // B
print(c.info); // this is A
var d = new D('张三', 20);
d.printInfo(); // 张三----20
d.run(); // B Run
}
4、final 和 const
如果不希望变量被修改,可以使用 final 和 const 定义变量。
final 和 const 的区别就是在编译的时候 const 变量的值就是已经确定的,final不一定,可能需要运行的时候才能确定值。
final name = 'Bob';
const bar = 1000000;
5、算数除法( / 与 ~/)
print(5 / 2 == 2.5); // 结果是双浮点型
print(5 ~/ 2 == 2); // 结果是整型
6、库
1、自定义库
第1步:声明库
library library_name
第2步:关联库
import 'library_name'
2、库前缀(区分同名库)
如果导入两个存在冲突标识符的库, 则可以为这两个库,或者其中一个指定前缀。
首先,定义一个库:loggerlib.dart,代码如下所示:
library loggerlib;
void log(msg){
print("Log method called in loggerlib msg:$msg");
}
接下来,将定义另一个库:webloggerlib.dart,代码如下所示:
library webloggerlib;
void log(msg){
print("Log method called in webloggerlib msg:$msg");
}
最后,导入带有前缀的库。
import 'loggerlib.dart';
import 'webloggerlib.dart' as web;
// prefix avoids function name clashes
void main(){
log("hello from loggerlib");
web.log("hello from webloggerlib");
}
3、延迟加载库
Deferred loading (也称之为 lazy loading) 可以让应用在需要的时候再加载库。 下面是一些使用延迟加载库的场景:
- 减少 APP 的启动时间。
- 执行 A/B 测试,例如 尝试各种算法的 不同实现。
- 加载很少使用的功能,例如可选的屏幕和对话框。
要延迟加载一个库,需要先使用 deferred as 来导入:
import 'package:greetings/hello.dart' deferred as hello;
当需要使用的时候,使用库标识符调用 loadLibrary() 函数来加载库:
Future greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
在一个库上你可以多次调用 loadLibrary() 函数。但是该库只是载入一次。
7、异步Future
1、基础用法(两种then、await)
类似js的Promise,可以使用then、await两种方式获取结果
- 使用then获取结果
值得注意的是,这里捕获异常是catchError(区别于js的catch),完成是whenComplete(区别于js的finally)
Future.delayed(Duration(milliseconds: 1000)).then((value) {
print("结果:$value");
}).catchError((error) {
print('$error');
}).whenComplete(() {
print('代码执行完成');
});
- 使用await获取结果
try{
var result=await Future.delayed(Duration(milliseconds: 1000));
}catch(e){
}finally{
}
2、延迟加载
Future封装了delayed延迟任务
Future.delayed(Duration(milliseconds: 1), () {
});
3、async、await异步等待
异步等待用法,与js的promise如出一辙
void _onRefresh() async {
await Future.delayed(Duration(milliseconds: 1000));
refreshInfo();
}
4、自定义Future
Future的return:相当于Promise的resolve
Future的throw Exception:相当于Promise的reject
// 模拟一个网络请求
Future<String> getNetworkData() {
return Future<String>(() {
sleep(Duration(seconds: 3));
// throw Exception("我是错误信息");
return "我是请求到的数据";
});
}
5、链式调用
可以使用then多次获取异步结果,也可以使用catchError多次捕获异常
main(List<String> args) {
print('start');
Future(() {
sleep(Duration(seconds: 3));
// throw Exception("第一次异常");
return '第1次网络请求的结果';
}).then((result) {
print('$result');
sleep(Duration(seconds: 3));
return '第2次网络请求的结果';
}).then((result) {
print('$result');
sleep(Duration(seconds: 3));
return '第3次网络请求的结果';
}).then((result) {
print('$result');
}).catchError((error) {
print(error);
});
print('end');
}
8、泛型
1、类的泛型
在类型安全上通常需要泛型支持, 它的好处不仅仅是保证代码的正常运行:
- 正确指定泛型类型可以提高代码质量。
- 使用泛型可以减少重复的代码。
class BusinessBaseClass<T> {
void printStr(T str) {
print(str);
}
}
void main() {
var temp = BusinessBaseClass<double>();
temp.printStr(1); //1.0
}
2、抽象类的泛型
封装上使用广泛
abstract class Cache<T> {
T getByKey(String key);
void setByKey(String key, T value);
}
3、限制泛型类型
通过extends关键字可以限制泛型类型
class SomeBaseClass {}
class BusinessBaseClass<T extends SomeBaseClass> {}
使用时
class Extender extends SomeBaseClass {}
void main() {
var temp = BusinessBaseClass<Extender>();
}
4、泛型函数
泛型也可使用在函数上,下面示例取列表中的第一个元素
T first<T>(List<T> ts) {
T tmp = ts[0];
return tmp;
}
三、工具类封装
1、节流、防抖
yaml文件的dependencies添加依赖
async: 2.8.2
import 'package:async/async.dart';
/**
* @author zhanglei
* @version 1.0
* @created 2022/10/22
* @title 防抖
* @description
* @changeRecord 2022/10/18 modify by zhanglei
*/
class ActionUtil {
static Map<String, CancelableOperation> timeout = {};
// 防抖 所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
// 应用场景: 用户输入间隔多久后没有输入时进行检查
/**
* @desc 函数防抖
* @param func 函数
* @param wait 延迟执行毫秒数
* @param immediate true 表立即执行,false 表非立即执行
*/
static debounce(
Function func, {
int wait = 500,
bool immediate = true,
String key = 'default',
}) async {
if (timeout[key] != null) {
timeout[key]?.cancel();
}
if (immediate) {
bool callNow = timeout[key] == null;
timeout[key] = CancelableOperation.fromFuture(
Future.delayed(Duration(milliseconds: wait)),
onCancel: () => {},
);
if (callNow) func();
await timeout[key]?.value;
timeout.remove(key);
} else {
timeout[key] = CancelableOperation.fromFuture(
Future.delayed(Duration(milliseconds: wait)),
onCancel: () => print(''),
);
await timeout[key]?.value;
func();
}
}
// 节流 所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率。
// 应用场景: 需要稀释执行效率的地方使用,例如懒加载的时候监听滚动条。
/**
* @desc 函数节流
* @param func 函数
* @param wait 延迟执行毫秒数
* @param immediate true 表示立即执行,false 表示非立即执行
*/
static int previous = 0;
static throttle(
Function func, {
int wait = 100,
immediate = false,
String key = 'default',
}) async {
if (immediate) {
int now = DateTime.now().millisecondsSinceEpoch;
if (now - previous > wait) {
func();
previous = now;
}
} else {
if (timeout[key] == null) {
timeout[key] = CancelableOperation.fromFuture(
Future.delayed(Duration(milliseconds: wait)),
onCancel: () => print(''),
);
await timeout[key]?.value;
timeout.remove(key);
func();
}
}
}
}
使用
ActionUtil.debounce((){
})
2、深浅拷贝
在Flutter中,List<Map<String, dynamic>>存在深浅拷贝问题,可以通过以下方式处理:
1、使用json.decode()和json.encode()进行深拷贝:
import 'dart:convert';
List<Map<String, dynamic>> originalList = [...];
List<Map<String, dynamic>> copiedList = json.decode(json.encode(originalList));
2、使用List.generate()进行深拷贝:
序列化与发序列化这种是最经典的深拷贝方式,但是如果Map中dynamic存在数字、字符串混在一起,可能会导致decode出来的都是字符串,此时可以考虑这种
List<Map<String, dynamic>> originalList = [...];
List<Map<String, dynamic>> copiedList = List.generate(originalList.length, (index) => {...originalList[index]});