Flutter基础 -- Dart 语言 -- 进阶使用

目录

1. 泛型 generics

1.1 泛型使用

1.2 泛型函数

1.3 构造函数泛型

1.4 泛型限制

2. 异步 async

2.1 异步回调 then

2.2 异步等待 await

2.3 异步返回值

3. 生成器 generate (了解)

3.1 同步生成器 sync*

使用 sync* 的场景

总结

3.2 异步生成器 async*

使用 StreamSubscription 的优势

3.3 递归生成器 sync*

4. 类型定义 typedef

4.1 typedef 定义使用

4.2 简化常用类型定义

5. 空安全

5.1 默认不可空

5.2 type? 可空

5.3 value! 值保证不为空,主观上

5.4 value?. 不为空才执行

5.5 value?? 如果空执行

5.6 late 声明

5.7 late 类成员延迟初始

5.8 List、泛型

5.9 Map

6. 扩展 extension

6.1 示例 扩展日期时间

6.2 业务场景


1. 泛型 generics

1.1 泛型使用

main(List<String> args) {
  var l = <String>[];
  l.add('aaa');
  l.add('bbb');
  l.add('ccc');
  print(l);

  var m = <int, String>{};
  m[1] = 'aaaa';
  m[2] = 'bbbb';
  m[3] = 'cccc';
  print(m);
}

[aaa, bbb, ccc]
{1: aaaa, 2: bbbb, 3: cccc}

容器对象,在创建对象时都可以定义泛型类型

1.2 泛型函数

K addCache<K, V>(K key, V val) {
  print('$key -> $val');
  return val;
}

main(List<String> args) {
  var key = addCache('url', 'https://wiki.doucafecat.tech');
  print(key);
}

url -> https://wiki.doucafecat.tech
https://wiki.doucafecat.tech

1.3 构造函数泛型

class Phone<T> {
  final T mobileNumber;
  Phone(this.mobileNumber);
}

main(List<String> args) {
  var p = Phone<String>('ducafecat');
  print(p.mobileNumber);
}

ducafecat

这是大多数情况下使用泛型的场景,在一个类的构造函数中(Phone是一个泛型类)

(就是cpp的template)

1.4 泛型限制

定义

class AndroidPhone {
  void startup() {
    print('Android Phone 开机');
  }
}

class Phone<T extends AndroidPhone> {
  final T mobile;
  Phone(this.mobile);
}

实例

main(List<String> args) {
  var ad = AndroidPhone();
  var p = Phone<AndroidPhone>(ad);
  p.mobile.startup();
}

Android Phone 开机

通过 extends 关键字 可以限定你可以泛型使用的类型

通过 class Phone<T extends AndroidPhone> {

就可以让传入的实参必须是 AndroidPhone类型 或者 是 AndroidPhone类型的子类

2. 异步 async

2.1 异步回调 then

import 'package:dio/dio.dart';

void main() {
  Dio dio = Dio();
  dio.get("https://wpapi.ducafecat.tech/products/categories").then((response) {
    print(response.data);
  });
}

[{id: 34, name: Bag, slug: bag, parent: 0, description: ...

then 的方式异步回调

在 Dart 中,then 方法用于处理返回了 Future 的异步操作。当 Future 完成时,then 方法所传入的回调函数会被执行

2.2 异步等待 await

import 'package:dio/dio.dart';

void main() async {
  Dio dio = Dio();
  Response<String> response =
      await dio.get("https://wpapi.ducafecat.tech/products/categories");
  print(response.data);
}

[{id: 34, name: Bag, slug: bag, parent: 0, description: ...

async 写在函数定义 await 写在异步请求头

在 Dart 中,async 和 await 关键字用于简化异步编程,使得代码逻辑和控制流更像是同步代码。 它们主要用于处理 Future 对象,从而让异步代码更加易读

  1. async 关键字
    • 当你在函数声明时使用 async 关键字时,表示这个函数是异步函数
    • async 函数总是返回一个 Future,即使你直接返回一个非 Future 类型的值(这种情况下这个值将会被自动包装成一个 Future
  2. await 关键字
    • await 关键字只能在 async 函数中使用,它用于等待一个返回 Future 的表达式完成,并获取其结果
    • 在 await 关键字之后的代码会暂停执行,直到 Future 完成,并将其结果赋值给左侧的变量
   Response<String> response =
      await dio.get("https://wpapi.ducafecat.tech/products/categories");

在这里,await 暂停了 main 函数的执行,直到 dio.get 异步操作完成,并将请求成功返回的响应赋值给 response 变量

2.3 异步返回值

import 'package:dio/dio.dart';

Future<String?> getUrl(String url) async {
  Dio dio = Dio();
  Response<String> response = await dio.get(url);
  return response.data;
}

void main() async {
  var content =
      await getUrl('https://wpapi.ducafecat.tech/products/categories');
  print(content);
}

[{id: 34, name: Bag, slug: bag, parent: 0, description: ...

定义 Future<T> 返回对象

代码展示了如何使用 Dart 中的 async 和 await 关键字来处理异步 HTTP 请求,并把请求操作提取到一个单独的函数里。这种方式不仅使代码更具有模块化,还能提高可读性和维护性

3. 生成器 generate (了解)

3.1 同步生成器 sync*

Iterable<int> naturalsTo(int n) sync* {
  print('start');
  int k = 0;
  while (k < n) {
    yield k++;
  }
  print('end');
}

main(List<String> args) {
  var it = naturalsTo(5).iterator;
  while (it.moveNext()) {
    print(it.current);
  }
}

start
0
1
2
3
4
end

yield 会等待 moveNext 指令

使用 sync* 的场景

延迟计算: 生成器可以在需要时生成元素,而不是一次性生成所有元素

内存效率: 适用于处理大量或无限数据流,因为它们逐个生成元素,而不需要一次性加载到内存中

流控制: 可以在生成元素之间执行其他逻辑,如打印日志、计算等

总结

sync* 关键字用于定义一个同步生成器函数,yield 则用于逐个生成元素。生成器函数非常适合处理需要延迟计算、大量数据或无限数据流的场景。它们提高了内存效率,同时保持代码简洁和易读

3.2 异步生成器 async*

import 'dart:async';

Stream<int> asynchronousNaturalsTo(int n) async* {
  print('start');
  int k = 0;
  while (k < n) {
    yield k++;
  }
  print('end');
}

main(List<String> args) {
  // 流监听
  // asynchronousNaturalsTo(5).listen((onData) {
  //   print(onData);
  // });

  // 流监听 StreamSubscription 对象
  StreamSubscription subscription = asynchronousNaturalsTo(5).listen(null);
  subscription.onData((value) {
    print(value);
    // subscription.pause();
  });
}

start
0
1
2
3
4
end

以流的方式一次性推送

StreamSubscription 对象进行流监听控制

使用 StreamSubscription 的优势

流控制StreamSubscription 对象允许你控制流的行为,比如暂停 (pause) 和恢复 (resume) 流

取消订阅: 你可以通过 subscription.cancel() 方法取消订阅流,避免资源浪费

监听错误和完成: 可以使用 subscription.onError 和 subscription.onDone 方法分别处理错误和流完成事件

3.3 递归生成器 sync*

Iterable<int> naturalsDownFrom(int n) sync* {
  if (n > 0) {
    yield n;
    yield* naturalsDownFrom(n - 1);
  }
}

main(List<String> args) {
  var it = naturalsDownFrom(5).iterator;
  while (it.moveNext()) {
    print(it.current);
  }
}

5
4
3
2
1

yield* 以指针的方式传递递归对象,而不是整个同步对象

4. 类型定义 typedef

4.1 typedef 定义使用

采用 typedef

typedef MyPrint = void Function(String val);

class PrintClass {
  MyPrint print;
  PrintClass(this.print);
}

main() {
  PrintClass coll = PrintClass((String val) => print(val));
  coll.print('hello world');
}

hello world

未采用 typedef

class PrintClass {
  void Function(String val) print;
  PrintClass(this.print);
}

main() {
  PrintClass coll = PrintClass((String val) => print(val));
  coll.print('hello world');
}

hello world

4.2 简化常用类型定义

定义

typedef MapStringAny = Map<String, dynamic>;
typedef MapAnyString = Map<dynamic, String>;
typedef MapStringString = Map<String, String>;
typedef MapStringDouble = Map<String, double>;
typedef MapDoubleString = Map<double, String>;
typedef MapDoubleDouble = Map<double, double>;
typedef MapIntInt = Map<int, int>;
typedef MapIntDouble = Map<int, double>;

typedef ListString = List<String>;
typedef ListInt = List<int>;
typedef ListDouble = List<double>;
typedef ListAny = List<dynamic>;

使用

main() {
  ListString p = [];
  p.add('a');
  p.add('b');
  p.add('c');

  MapStringAny m = {};
  m['a'] = 1;
  m['b'] = 2;
  m['c'] = 3;
}

5. 空安全

5.1 默认不可空

String title = 'ducafecat';

5.2 type? 可空

String? title = null;

5.3 value! 值保证不为空,主观上

String? title = 'ducafecat';
String newTitle = title!;

就是骗一骗编译器

5.4 value?. 不为空才执行

String? title = 'ducafecat';
bool isEmpty = title?.isEmpty();

5.5 value?? 如果空执行

String? title = 'ducafecat';
String newTitle = title ?? 'cat';

像构里面就挺好用的

5.6 late 声明

延迟加载修饰符

声明一个不可空的变量,并在声明后初始化

late String description;

void main() {
  description = 'Feijoada!';
  print(description);
}

本来要在声明的时候初始化的,加上 late 就告诉编译器我们后面再初始化

5.7 late 类成员延迟初始

class WPHttpService extends GetxService {
  late final Dio _dio;

  @override
  void onInit() {
    ...

    _dio = Dio(options);

    ...
  }

加上 late 后就可以不用构造函数的时候初始化了

5.8 List、泛型

  List<String?>? l;
  l = [];
  l.add(null);
  l.add('a');
  print(l);

5.9 Map

  Map<String, String?>? m;
  m = {};
  m['a'] = 'b';
  m['b'] = null;
  print(m);

* 可能返回空

6. 扩展 extension

extension 本质上还是和继承一样扩展了方法

但这是一种简洁优雅的方式,你可以想想之前继承的繁琐

6.1 示例 扩展日期时间

加入依赖包 pubspec.yaml

dependencies:
  intl: ^0.17.0

编写扩展类 ExDateTime

import 'package:intl/intl.dart';

extension ExDateTime on DateTime {
  /// 方法,格式化日期 yyyy-MM-dd
  String toDateString({String format = 'yyyy-MM-dd'}) =>
      DateFormat(format).format(this);

  // 属性
  int get nowTicket => this.microsecondsSinceEpoch;
}

main() {
  var now = DateTime.now();

  print(now.toDateString());
  print(now.nowTicket);
}

我们给可以扩展类加个前缀 Ex 这样一看就知道是扩展

6.2 业务场景

功能函数

视图组件

创作不易,希望读者三连支持 💖
赠人玫瑰,手有余香 💖

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/674212.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CRM系统主要是干什么?CRM系统主要功能和作用

什么是CRM 系统&#xff1f;CRM系统到底是干什么的&#xff1f;不同的企业人员该如何利用CRM去解决他们的问题等等&#xff0c;问题太多了&#xff0c;今天来为大家详细介绍。 干货满满&#xff0c;建议收藏&#xff01;&#xff01; 首先第一个问题&#xff0c;什么是CRM系统…

Tween.js在Three.js中的应用:为3D动画添加流畅过渡

前言 在Web开发领域&#xff0c;Three.js已经成为构建精彩3D内容的首选库之一。它让开发者能够轻松地在浏览器中创建和展示复杂的3D场景。然而&#xff0c;要让这些场景栩栩如生&#xff0c;平滑的动画效果是必不可少的。这就引入了Tween.js——一个轻量级但功能强大的JavaScr…

MyBatis核心对象

MyBatis核心类对象主要有俩个&#xff1a; 1&#xff1a;对相关配置文件信息进行封装的Configuration对象 2&#xff1a;用来执行数据库操作的Executor对象。 核心对象----存储类对象Configuration Configuration对象主要有三个作用&#xff1a; 1&#xff1a;封装MyBatis…

linux进程加载和启动过程分析

我们的源代码通过预处理,编译,汇编,链接后形成可执行文件,那么当我们在终端敲下指令$ ./a.out argv1 argv2 后,操作系统是怎么将我们的可执行文件加载并运行的呢? 首先知道,计算机的操作系统的启动程序是写死在硬件上的,每次计算机上电时,都将自动加载启动程序,之后…

R语言数据分析-针对芬兰污染指数的分析与考察

1. 研究背景及意义 近年来&#xff0c;随着我国科技和经济高速发展&#xff0c;人们生活质量也随之显著提高。但是&#xff0c; 环境污染问题也日趋严重&#xff0c;给人们的生活质量和社会生产的各个方面都造成了许多不 利的影响。空气污染作为环境污染主要方面&#xff0c;更…

重生之我要精通JAVA--第七周笔记

文章目录 IO流字符流字符流原理解析flush和close方法 文件拷贝代码文件加密解密修改文件中的数据 缓冲流字节缓冲流字符缓冲流例题 转换流序列化流序列化流/对象操作输出流 反序列化流序列化流/反序列化流的细节汇总打印流字节打印流字符打印流 解压缩流压缩流Commons-io常见方…

代码随想录--哈希表--两数之和

题目 给定一个整数数组 nums 和一个目标值 target&#xff0c;请你在该数组中找出和为目标值的那 两个 整数&#xff0c;并返回他们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素不能使用两遍。 示例: 给定 nums [2, 7, 11, 15], t…

【RuoYi】如何解决Postman无法访问RuoYi中的接口数据

一、前言 最近&#xff0c;写项目要求需要将数据返回&#xff0c;指定的接口&#xff0c;并且需要使用Postman来测试接口数据&#xff0c;看是否能够请求到数据。然后项目用的是RuoYi的框架&#xff0c;RuoYi使用了SpringSecurity来做的安全框架&#xff0c;所以在访问的时候&a…

【C语言】编译与链接:深入理解程序构建过程

&#x1f525;引言 本篇将深入理解程序构建过程&#xff0c;以便于我们在编写程序的过程同时&#xff0c;理解底层是如何从程序的创建到生成可执行程序的。 &#x1f308;个人主页&#xff1a;是店小二呀 &#x1f308;C语言笔记专栏&#xff1a;C语言笔记 &#x1f308;C笔记专…

django使用fetch上传文件

在上一篇文章中&#xff0c;我包装了fetch方法&#xff0c;使其携带cookie。但是之前fetch传递的是json数据&#xff0c;现在有了一个上传文件的需求&#xff0c;因此需要进行修改&#xff1a; const sendRequest (url, method, data) > {const csrftoken Cookies.get(cs…

【Effective Python教程】(90个有效方法)笔记——第1章:培养pythonic思维——7:尽量用enumerate取代range

文章目录 第1章&#xff1a;培养pythonic思维第7条 尽量用enumerate取代range&#xff08;移位操作、位掩码&#xff09;要点enumerate函数可以用简洁的代码选代iterator&#xff0c;而且可以指出当前这轮循环的序号。不要先通过range指定下标的取值范围&#xff0c;然后用下标…

Linux eBPF:网络、系统监控和安全领域的创新

扩展 Berkeley Packet Filter&#xff08;eBPF&#xff09;是Linux内核中的一项强大技术&#xff0c;最初用于网络数据包过滤。随着时间的推移&#xff0c;eBPF的功能和应用场景不断扩展&#xff0c;如今已成为网络、系统监控和安全等领域的重要工具。eBPF可以在Linux内核中安全…

Halcon 双相机标定与拼图(一)

二、算子解释 get_calib_data camera-pose 获得基于第一个相机的第二个相机的Pose get_calib_data (CalibDataID, camera, 1, pose, RelPose2) *relative 相对 * To get the absolute pose of the second camera, its relative pose needs * to be inverted and combined…

2024 cicsn magicvm

文章目录 参考检查逆向vm::runvm::vmvm_alu::set_inputvm_mem::set_inputvm_id::runvm_alu::runvm_mem::run 漏洞思路参考的exp 参考 https://forum.butian.net/share/3048 https://akaieurus.github.io/2024/05/20/2024%E5%9B%BD%E8%B5%9B%E5%88%9D%E8%B5%9Bpwn-wp/#SuperHea…

【Nacos_bugs】java.lang.IllegalStateException: Failed to load ApplicationContext

报错原因 找不到配置文件。 Bug 排查 如果使用 Nacos 管理配置文件&#xff0c;需要检查本地 bootstrap.yml 配置是否出现问题&#xff1a; 检查点&#xff1a; 检查 Nacos 服务的地址有没有配置错误&#xff0c;如上图 ①&#xff0c;格式严格为 IP:端口号" 检查 D…

Mongodb的数据库简介、docker部署、操作语句以及java应用

Mongodb的数据库简介、docker部署、操作语句以及java应用 本文主要介绍了mongodb的基础概念和特点&#xff0c;以及基于docker的mongodb部署方法&#xff0c;最后介绍了mongodb的常用数据库操作语句&#xff08;增删改查等&#xff09;以及java下的常用语句。 一、基础概念 …

WebPack插件实现:打包之后自动混淆加密JS文件

在WebPack中调用JShaman&#xff0c;实现对编译打包生成的JS文件混淆加密 一、插件实现 1、插件JShamanObfuscatorPlugin.js&#xff0c;代码&#xff1a; class JShamanObfuscatorPlugin { apply(compiler) { compiler.hooks.emit.tapAsync(JShamanObfuscatorPlugin, (comp…

【Python网络爬虫】详解python爬虫中正则表达式、BeautifulSoup和lxml数据解析

&#x1f517; 运行环境&#xff1a;PYTHON &#x1f6a9; 撰写作者&#xff1a;左手の明天 &#x1f947; 精选专栏&#xff1a;《python》 &#x1f525; 推荐专栏&#xff1a;《算法研究》 #### 防伪水印——左手の明天 #### &#x1f497; 大家好&#x1f917;&#x1f91…

SpringMVC日期格式处理 分页条件查询

实现日期格式处理 实现分页条件查询&#xff1a; 分页条件查询 和 查询所有 是两个不同的方法&#xff0c;使用同一个mapper的查询功能&#xff0c;但是两个不同的业务方法 ​​​​​​​

2024年5月2日 Go生态洞察:Go 1.22中的安全随机性

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a;…