Dart笔记

Dart介绍

将 Dart 和 JavaScript 做一个对比:

  1. 开发效率高

    Dart 运行时和编译器支持 Flutter 的两个关键特性的组合:

    • 基于 JIT 的快速开发周期:Flutter 在开发阶段采用,采用 JIT 模式,这样就避免了每次改动都要进行编译,极大的节省了开发时间;
    • 基于 AOT 的发布包: Flutter 在发布时可以通过 AOT 生成高效的机器码以保证应用性能。而 JavaScript 则不具有这个能力。
  2. 高性能

    Flutter 旨在提供流畅、高保真的的 UI 体验。为了实现这一点,Flutter 中需要能够在每个动画帧中运行大量的代码。这意味着需要一种既能提供高性能的语言,而不会出现会丢帧的周期性暂停,而 Dart 支持 AOT,在这一点上可以做的比 JavaScript 更好。

  3. 快速内存分配

    Flutter 框架使用函数式流,这使得它在很大程度上依赖于底层的内存分配器。因此,拥有一个能够有效地处理琐碎任务的内存分配器将显得十分重要,在缺乏此功能的语言中,Flutter 将无法有效地工作。当然 Chrome V8 的 JavaScript 引擎在内存分配上也已经做的很好,事实上 Dart 开发团队的很多成员都是来自Chrome 团队的,所以在内存分配上 Dart 并不能作为超越 JavaScript 的优势,而对于Flutter来说,它需要这样的特性,而 Dart 也正好满足而已。

  4. 类型安全和空安全

    由于 Dart 是类型安全的语言,且 2.12 版本后也支持了空安全特性,所以 Dart 支持静态类型检测,可以在编译前发现一些类型的错误,并排除潜在问题,这一点对于前端开发者来说可能会更具有吸引力。与之不同的,JavaScript 是一个弱类型语言,也因此前端社区出现了很多给 JavaScript 代码添加静态类型检测的扩展语言和工具,如:微软的 TypeScript 以及Facebook 的 Flow。相比之下,Dart 本身就支持静态类型,这是它的一个重要优势。

  5. Dart 团队就在你身边

    看似不起眼,实则举足轻重。由于有 Dart 团队的积极投入,Flutter 团队可以获得更多、更方便的支持,正如Flutter 官网所述“我们正与 Dart 社区进行密切合作,以改进 Dart 在 Flutter 中的使用。例如,当我们最初采用 Dart 时,该语言并没有提供生成原生二进制文件的工具链(这对于实现可预测的高性能具有很大的帮助),但是现在它实现了,因为 Dart 团队专门为 Flutter 构建了它。同样,Dart VM 之前已经针对吞吐量进行了优化,但团队现在正在优化 VM 的延迟时间,这对于 Flutter 的工作负载更为重要。”

软件安装

在这之前,我们知道”工欲善其事必先利其器“,所以需要安装相关工具。

Android studio安装

java安装

win7电脑看这篇博客。

https://blog.csdn.net/weixin_30979229/article/details/114453053?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164921053916782246441003%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=164921053916782246441003&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-4-114453053.142v5pc_search_result_control_group,157v4control&utm_term=win7%E5%AE%89%E8%A3%85java&spm=1018.2226.3001.4187

Android studio中安装flutter与创建项目

首先,现在官网下载flutterSDK包,在此包里面包含了DartSDK文件,下载路径如下:

在 Windows 操作系统上安装和配置 Flutter 开发环境 | Flutter 中文文档 - Flutter 中文开发者网站

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Owlizf64-1638454824682)(F:\flutter笔记\Dart_imges\image-20211117233302460.png)]

点击蓝色按钮即可下载。

下载完成之后,将该文件包解压到你想安装目录的下面,我的安装目录是在D:\SoftwareFolder\路径下,将其解压。解压完成之后就是运行,flutter提供了两种运行方式:

  • 通过运行flutter目录下的flutter_console.bat文件,就可以在命令行中输入flutter命令了。

配置flutter环境

  • 配置环境变量,进入flutter\bin目录,将地址复制,然后打开环境变量窗口进行配置环境变量。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-USVM5Q22-1638454824684)(F:\flutter笔记\Dart_imges\image-20211117234900888.png)]

检测是否可用:

按快捷键win+R输入cmd命令,在命令行中输入flutter doctor

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gfo7uGW5-1638454824685)(F:\flutter笔记\Dart_imges\image-20211117235243803.png)]

在Android studio中添加 Command-line Tools

打开 Android studio,在stting->appearance &Behavior->System Settings->Android SDK中进行配置。
在这里插入图片描述

但是出现了错误,没关系,先解决第一个框里面的问题。

添加许可证

它提示Android toolchain - develop for Android devices

说明是Android的SDK协议没有添加许可证,

找到自己android—sdk的路径,比如:G:\studioAnZhuang\AndroidStudioSDK\tools\bin,关键是**\tools\bin**。

在此目录打开 cmd 命令行 输入以下命令:

sdkmanager --licenses

然后一路同意y就可以了,最后在命令行下输入flutter doctor 查看结果。

配置模拟器路径

之后关闭此命令行窗口,再次运行flutter doctor发现还是这两个问题,于是看见它找出的问题是Unable to locate Android SKD,这就要涉及到配置环境变量的问题,找到你的Android sdk安装路径,比如我的安装路径如下图。

由于我不确定是不是这个路径,去环境变量里添加了这个之后,发现可行,把我激动坏了,环境变量配置如下所示。

关闭命令行窗口之后,再次运行flutter doctor如下图所示。

不知道为啥,就是特别兴奋,感觉满满的成就感,哈哈哈,但是一想到还有一个问题没有解决,脑壳又大了。

于是分析了一下,发现是没有安装谷歌的原因,于是我通过下面链接去下载了google浏览器。

Google Chrome 网络浏览器

它给我自动安装在了C盘,由于不知道怎么更改路径,就挺无语的,再次运行flutter doctor就没有报错了,如下图所示。

配置完这些东西之后,我们还需要去环境变量中添加2个路径如下图所示。

盘符名:安装文件夹\flutter_windows_2.5.3-stable\flutter\bin\cache\dart-sdk\bin

  • dart的环境变量,在flutter2.0之后,官网的安装包里面就自带了dart的sdk文件,所以,我们只需要找到它的文件位置,将它的环境变量设置好就可以开始肝代码了。将下面的路径添加到系统环境变量中。

盘符名:安装文件夹\flutter_windows_2.5.3-stable\flutter.pub-cache\bin

  • Pub下载的公共模块的执行路径,如果,没有 bin文件夹,创建一个即可。

有了上面这些操作,我们就可以配置编译器,开始在编译器中肝代码了!!激动的心,颤抖的手,终于要开始了。

创建项目

Android studio中创建项目

在Android stduio中安装插件:
在这里插入图片描述
重启软件,
在这里插入图片描述

创建Dart项目

首先,打开Android studio软件,点击File > New >New Flutter Project...

选择Dart添加Dart sdk文件位置,如下如图所示。

点击Next,创建项目名和项目地址,之后点击Finish,即可创建项目。

创建Dart文件

首先,找到Project面板,在空白区域右击,选择new>Dart File>输入你的文件名,即可创建你的dart文件。


写一段代码测试是否可以运行

写完之后,点击mian方法旁边的[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yybDmqgr-1638454824687)(F:\flutter笔记\Dart_imges\image-20211118172025554.png)]
按钮运行,并查看结果如下图。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tGvmLFPV-1638454824688)(F:\flutter笔记\Dart_imges\image-20211118172207260.png)]

如果发现无法运行,请点击下面图中的第一个框,进行选择模拟器,再点击运行。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j9bri8Vy-1638454824688)(F:\flutter笔记\Dart_imges\image-20211118172712132.png)]

VScode安装

首次,在VScode官网进行下载,链接如下。

Download Visual Studio Code - Mac, Linux, Windows

安装特别简单,若有不会,请访问下面链接。

百度一下,你就知道 (baidu.com)

安装之后,我们需要在软件中安装flutter插件和run插件,如下图所示。


  • flutter插件安装好之后,重启一下vscode就可以开始创建文件,编写代码。
  • run插件可以直接点击右上角的[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cwzTPvmS-1638454824689)(F:\flutter笔记\Dart_imges\image-20211118200136702.png)]
    ,前提是你需要在文档中写了Dart程序代码。

VScode中创建项目

  • 首先,你需要在你所需要创建项目的目录下使用cmd命令,进入命令行,使用flutter create flutter_app

  • flutter_app是你需要创建的项目名。

  • 在vscode打开项目,找到android目录下的build.gradle文件并且打开。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lv045wQj-1638454824690)(F:\flutter笔记\Dart_imges\image-20211122141455474.png)]

在打开的文件中,将repositories{...}下的google()mavenCentral()注释,因为我们要换源,使用国内的镜像源。将下面3个源替换原来的两个源。

maven { url ‘https://maven.aliyun.com/repository/google’}

maven { url ‘https://maven.aliyun.com/repository/jcenter’ }

maven { url ‘https://maven.aliyun.com/repository/public’ }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zmQMWYSA-1638454824691)(F:\flutter笔记\Dart_imges\image-20211122141955329.png)]

之后打开你的flutter安装目录,找到目录下的packages > flutter_tools > gradle >flutter.gradle> 的文件,双击在vscode中打开,将repositories{...}下面的代码替换。保存,重新打开vscode。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pgd0FoHe-1638454824691)(F:\flutter笔记\Dart_imges\image-20211122142408354.png)]

添加第三方包(依赖)

说完了安装方法,现在介绍两种添加Flutter第三方包的两种方法:

  1. flutter项目的pubspec.yaml文件中添加:
    在该文件中找到dependencies:所在的行,并且需要知道它的版本号,所以,打开https://pub.dev/,在这里面你可以搜索到所有开源的第三方包。
    例如添加一个provider的包:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    将版本号复制下来,粘贴到该文件中的dependencies处,点击Pub get,在下面的Messges窗口中可以看到是否添加成功,若成功之后,可以在External Libraries中查看文件的位置。
    在这里插入图片描述
  2. 在命令行中添加:
    在这里插入图片描述添加成功!
    在这里插入图片描述

Dart基础

语法基础

简单介绍

首先,我们需要知道程序是都是从main方法开始执行,于是有了固定的写法,如下所示。

// 定义一个输出方法
void printInteger(int num){
  print("hello world $num");	// 输入语句,将文本输出到控制台
    							// $num 用于将形参的值放进来
}

// 在主函数中输出
void main(){
  var number = 3000;	// 定义一个活动变量,可以是任意类型值
  printInteger(number);	// 调用方法
}

从上面可以看出:

  • 在main方法后面是一对小括号与一对大括号。

  • 大括号里面放置的是需要执行的程序语句。

  • 函数和方法前面都有类型声明,void关键字表示该方法 无返回值int是整形数字。

  • 两根斜杠为注释内容,程序不会执行被注释的内容。

  • 打印使用print( JavaScript中使用console.log() )。

  • 每行代码结束时,必须写结束分号。

  • 字符串通过引号包起来,支持模板字符串。

  • var声明的变量。其数据类型是动态的。

  • 声明函数不需要关键字(JavaScript中使用function关键字声明函数)。

注释

Dart中,注释有两种写法,和javascript的注释方法一样,如下所示。

  • 单行注释,以//两根斜杠为注释。
// 这行文本被注释
  • 多行注释,以/* 这里边是文本内容 */,以两根斜杠为边,里面再放两个星号,在星号中的文本就是注释内容。
/* 
这行文本被注释
这行文本被注释
*/
  • 文档注释,使用三跟斜杠用于文档注释,可以通过dartdoc将注释转换为文档(文档注释支持markdown语法)。
/// 这是文档注释

变量

  • 变量是一个引用,Dart万物皆对象,变量存储的是对象的引用。

  • 声明变量

    • 明确指定类型:int age = 18;

    • 不明确类型:

      • var age = 18;

      • dynamic age = 18;

      • Object age = 18

      • ObjectDart 所有对象的根基类,也就是说在 Dart 中所有类型都是Object的子类(包括Function和Null),所以任何类型的数据都可以赋值给Object声明的对象。 dynamicObject声明的变量都可以赋值任意对象,且后期可以改变赋值的类型,这和 var 是不同的,如:

        var t;
        dynamic t;
        Object x;
        var t = "ha";
        t = "hi world";
        x = 'Hello Object';
        //下面代码没有问题
        t = 1000; // 出错
        //下面代码没有问题
        t = 1000;
        x = 1000;
        
      • dynamicObject不同的是dynamic声明的对象编译器会提供所有可能的组合,而Object声明的对象只能使用 Object 的属性与方法, 否则编译器会报错。

         dynamic a;
         Object b = "";
         main() {
           	a = "";
           	printLengths();
         }   
        
         printLengths() {
           	// 正常
        	print(a.length);
           	// 报错 The getter 'length' is not defined for the class 'Object'
           	print(b.length);
         }
        
      • dynamic 的这个特点使得我们在使用它时需要格外注意,这很容易引入一个运行时错误,比如下面代码在编译时不会报错,而在运行时会报错:

        print(a.xx); // a是字符串,没有"xx"属性,编译时不会报错,运行时会报错
        
  • 变量名大小写敏感(age与Age是两个不同的变量)。

  • Dart变量的值不会进行隐式转换(null不会自动 转换成false)。

void main() {
  //定义变量
  var name1="张三";
  print(name1);    // 输出字符串 张三

  // 指定类型的变量
  String name2 = "李四";
  print(name2);   // 输出String类型的字符串 李四

  // 任意类型的变量
  dynamic name3 = "王五";
  print(name3);   // 输出字符串 王五

  // 任意类型的变量
  Object name4 = "赵六";
  print(name4);   // 输出字符串 赵六
}

常量

  • 常量就是值不可以改变的变量(一旦声明,其值不能改变)。

  • 声明常量

    • 使用const关键字进行声明变量。

      const age = 18;
      
    • 使用final关键字进行声明变量。

      final age = 18;
      
    • constfinal的区别

      • const time=DateTime.now(); //报错-无法将运行时得知分配给const变量。
      • final time = DateTime.now(); //成功-可以将运行时的值分配给final变量。
void main(List<String> args) {

  // const定义常量
  const age = 18;
  // age = 19;   // 报错,常量不能再次赋值
  print(age);   // 输出整形 18

  // final定义常量
  final age1 = 18;
  // age1 = 19;    // 报错,常量不能再次赋值
  print(age1);

  // 报错-无法将运行时得知分配给`const`变量
  // Cannot invoke a non-'const' constructor where a const expression is expected.
  // const time1=DateTime.now();

  // 成功-可以将运行时的值分配给`final`变量
  final time2=DateTime.now();
  print(time2); 
}

数据类型

Number类型

  • Dart中的数字由三个关键字描述
    • num数字类型(既可以是整数,也可以是小数)
      • int表示整数(必须是整数)
      • double表示浮点数(既可以是整数,也可以是小数)
  • 常用API
    • https://api.dart.cn/stable/dart-core/num-class.html
    • https://api.dart.cn/stable/dart-core/int-class.html
    • https://api.dart.cn/stable/dart-core/double-class.html
void main(List<String> args) {
  // 定义一个整型变量
  int num1 = 123;
  print(num1);  // 123

  // 定义一个浮点数变量
  double num2 = 123;
  print(num2);  // 123.0

  // 定义一个数值变量,它包含了int类型和double类型
  num num3 = 123;
  print(num3);  // 123
    
  num num4 = 123.0;
  print(num4);  // 123.0

}

常用API

String类型

字符串定义

首先,我们需要知道怎么定义字符串,字符串定义的引号都是成对出现,字符串的定义方法如下:

  • 使用var的方式进行定义。

    var str1 = 'this is str1';  // 单引号形式
    var str2 = "this is str2";  // 双引号新式
    
    // 输出结果如下所示
    this is str1
    this is str2
    
  • 使用String定义字符串,3个引号包括换行符。

    String str1 = 'this is str1';   // 单引号形式
    String str2 = "this is str2";   // 双引号新式
    String str3 = '''
    	this is str3
    	
    	this is str3''';			// 实现多行显示,原样显示
    
    // 输出结果如下所示
    this is str1
    this is str2
          this is str3
    
          this is str3
    
  • 正则表达式。

    • RegExp(r’正则表达式’)
    • RegExp(r’\d+')
字符串拼接

首先,我们需要定义两个字符串变量,然后将他们拼接起来。

  • 使用+加号将他们连接。
  // 字符串拼接
  String str1 = "张三想:";
  String str2 = "吃饭";
  print(str1+str2);
  //或者
  String str3 = str1 + str2;
  print(str3);
// 效果如下
张三想:吃饭
张三想:吃饭
  • 使用$方式进行拼接操作。
  String str1 = "张三想:";
  String str2 = "吃饭";
  print("$str1      $str2");
张三想:    吃饭
字符串的常见操作
  • 使用字符分割,结果是一个数组类型的值。

    • .split("字符") – 根据字符进行分割。
      // 字符串分割
      String str1 = "hello world";
      print(str1.split(""));;	// 按照空值进行分割
    
    [h, e, l, l, o,  , w, o, r, l, d]
    
  • 字符串裁切

    • .trim() – 将字符串前后空格裁切掉。
    • .trimLeft() – 将字符串的左边空格裁切。
    • .trimLeft() – 将字符串的右边空格裁切。
    // 字符串裁切
      String str1 = "   hello  world   ";
      print(str1.trim());   // 删除前后空格
      print(str1.trimLeft());   // 删除左边空格
      print(str1.trimRight());  // 删除右边空格
    
    hello  world
    hello  world   
       hello  world
    
    
  • 判断字符串是否为空

    • .isEmpty – 判断字符串是否为空,返回值是 true或 false。
    • .isNotEmpty – 判断字符串是否不为空,返回值是 true或 false。
// 判断字符串是否为空
  String str1 = "";		// 定义空字符串
  print(str1.isEmpty);
  print(str1.isNotEmpty);
true
false
  • 字符串替换

    • .replaceAll(旧字符,新字符),而.replaceFirst()只查找第一个并替换。
      // 字符串替换
      String str1 = "hello dart,hello world";
      print(str1.replaceAll("hello", "你好"));
    
  • 查找字符

    • .contains('字符串') – 在字符串中查找字符,返回值为true 或 false。
  // 字符查找
  String str1 = "hello dart,hello world";
  print(str1.contains("l"));
true
  • 定位字符
    • .indexOf(字符) – 用于在找到字符的索引位置,返回索引下标,从左开始查找。
    • .lastIndexOf(字符) – 找到字符的索引位置,返回索引下标,从右往左查找。
  // 定位字符
  String str1 = "hello dart,hello world";
  print(str1.indexOf('ll'));
  print(str1.lastIndexOf("or"));
2
18

bool类型

  • 布尔类型只有truefalse

  • Dart通过bool关键字来表示布尔类型。

  • 对变量进行判断时,要显式的检查布尔值

    • if (varname){ … }
    • if (varname == 0){ … }
    • if (varname == null){ … }
  // 定义一个true值
  bool b1 = true;
  print(b1);
  // 定义一个false值
  bool b2 = false;
  print(b2);
true
false

显式的判断

  • if语句,将条件true赋值给一个变量保存,将其放入if的判断中进行判断,使用如下:

      bool b3 = true;
      if (b3) {
        print("条件成立!");
      }else{
        print("条件不成立");
      }
    
    条件成立!
    
  • 判断字符是否为 NaN。

      var b1 =0/0;
      print(b1.isNaN);
    
    true
    
  • 判断是否为空值。

      // 判断是否为空值
       var flag;
       if (flag == Null) {
         print("是空值");
       } else {
         print("不是空值");
       }
    

List数据类型

List用法

List 就是Dart中的数组,由List对象表示。List有两种声明方式:

  • 字面量方式

    • 创建列表。

    • List list = [1,2,"张三","不法分子"];  // 不限定元素的数据类型
      
    • 泛型方法限定列表类型。

    • List list = <int>[1,2,3];  // 泛型,限定元素的类型是int
      
  • 构造函数方式

    • 创建一个不限定长度的列表。

      List list = new List.empty(growable:true);  // 不限制长度的空列表
      
      []
      
    • 填充指定长度的列表。

      List list = new List.filled(3,6);  // 声明指定长度的填充列表
      
      [6, 6, 6]
      
  • 扩展操作符(…)

    • 将第一个列表插入到另一个列表中。

      var list = [1,2,3];
      var list2 = [0,...list]; 
      
      [0, 1, 2, 3]
      
    • 使用?先进行判断是否可以扩展,在下面列子中用空值进行试验。

        // 判断是否可以扩展
        var list;
        // var list1 = [123,...list];  //报错:type 'Null' is not a subtype of type 'Iterable<dynamic>'
        var list2 = [234,...?list];
        print(list2);
      
List常用API
  • 添加元素

    • .add(数据) – 向列表中添加元素,可以是字符串、数组、各种数据类型。

        // 添加元素
        List list = [12,23,34];
        list.add(1.3);
        list.add("字符串");
        list.add([true,false]);
        print(list);
      
      [12, 23, 34, 1.3, 字符串, [true, false]]
      
    • .addAll(iterable) – iterable是一个可迭代的对象,可以在里面放置多个元素。

        // 添加元素
        List list = [12,23,34];
        list.addAll(["name",123,["张三",18]]);
        print(list);
      
      [12, 23, 34, name, 123, [张三, 18]]
      
  • 在指定位置添加元素。

    • .insert(index,element) – index为元素下标,element为元素

        // 在指定位置添加元素
        List list = [12,23,34,45,56];
        list.insert(3, "张三");		// 在下标为3的地方添加“张三”
        print(list);
      
      [12, 23, 34, 张三, 45, 56]
      
  • 清空列表

    • .clear() – 清空列表。

        // 清空列表
        List list = [12,23,34,45,56];
        list.clear();
        print(list);
      
      []
      
  • 连接元素

    • .join('-') - -将列表中的元素使用 - 连接。
  • 删除元素

    • .remove(元素名) – 参数为需要删除的元素名。

        List list = [12,23,34,45,56];
        list.remove(23);
        print(list);
      
      [12, 34, 45, 56]
      
    • .removeAt(下标) – 根据下标删除元素。

        // 根据下标删除元素
        List list = [12,23,34,45,56];
        list.removeAt(list.length-1);
        print(list);
      
      [12, 23, 34, 45]
      
  • 返回数组长度

    • .length – 可以查看数组有少个元素。

        // 查看数组长度
        List list = [12,23,34,45,56];
        print(list.length);
      
      5
      
  • 创建一个固定长度的列表

    • .filled(元素个数,填充的字符) --所接收的元素类型一定是List类型,创建的列表长度是固定的,不可以修改他的长度。

        List list = List.filled(3,6);  // 声明指定长度的填充列表
        print(list);
      
      [6, 6, 6]
      
    • 指定创建指定类型与长度的列表。

        // 创建指定类型的快速填充
        List list = List<String>.filled(3,"");  // 所创建的列表中只能添加int类型的元素。
        list[0]="张三";
        print(list);
      
      [张三, , ]
      
  • 列表反转

    • .reversed – 将列表中的元素翻转,得到的结果是一个元组,可以使用在后面加上.toList()方法.

        // 列表的反转
        List list = [12,23,34,45,56];
        print(list.reversed);
        // 变为列表
        print(list.reversed.toList());
      
      (56, 45, 34, 23, 12)
      [56, 45, 34, 23, 12]
      
遍历列表
  • forEach()

    • 遍历列表
  • map()

    • 遍历并处理元素,然后生成新的列表
  • where()

    • 返回满足条件的数据
  • any()

    • 只有一项满足条件,即返回true
  • every()

    • 判断是否每一项都满足条件,都满足条件才返回true
  • 使用for循环进行遍历。

    // for 循环进行遍历
      var list = [1,2,3];
      for (var i = 0; i < list.length; i++) {
        print(list[i]);
      }
    
    1
    2
    3
    
  • for … in 循环。

    // for in 循环进行遍历
      var list = [1,2,3];
      for (var item in list) {
        print(item);
      }
    
    1
    2
    3
    
  • forEach 使用匿名函数进行遍历,其中element是一个形参,使用print可将他打印出来。

    // forEach
      var list = [1,2,3];
      list.forEach((element) {
        print(element);
      });
    
    1
    2
    3
    
  • 使用map()将列表中的每个元素进行相乘,得到一个处理之后的列表,注意:=>函数只能右一行,可以省略大括号。

    // map
      var list = [1,2,3];
      list.map((e)=> e*e);	// 也可以写成这样list.map((e)=> {e*e});
      print(list);
    }
    
    [1, 2, 3]
    
  • where()返回符合条件的元素

      //where()返回符合条件的元素
      // 判断数字是否为奇数
      var list = [1,2,3,4,5,6,7,8];
      bool isOdd(n) => n%2==1;  // 箭头函数,判断数字是否为奇数
      var oddNum = list.where((element) => isOdd(element));   // 返回结果是一个元组类型
      print(oddNum.toList());
    
    [1, 3, 5, 7]
    
    • 第五行代码中,调用isOdd()函数进行判断,将判断的结果返回给list,并重复判断,直到不满足条件为止。而element则是列表中的每一个元素。
  • 使用any()检测是否有奇数,返回值为true或false。有一个也是true。

    // 使用any() 检测是否奇数
      var list = [1,2,3,4,5,6,7,8];
      bool isOdd(n) => n%2==1;  // 箭头函数,判断数字是否为奇数
      print(list.any(isOdd));
    
    true
    
  • 使用every()来判断是否都是奇数。

    // 使用every()来判断是否都是奇数
      var list = [1,2,3,4,5,6,7,8];
      bool isOdd(n) => n%2==1;  // 箭头函数,判断数字是否为奇数
      print(list.every(isOdd));
    
    false
    
  • 使用扩展方式,通过.expend()方法进行降维处理。

      // 扩展,使用expend进行降维
      var list = [[1,2,3],[4,5,6]];
      var flattened = list.expand((element) => element).toList();
      print(flattened);
    
  • 折叠-- .fold(initialValue, (previousValue, element) => null)initialValue

为初始值,对列表中的每一个元素,做一个累计的操作。

// 折叠
  var list = [1,2,3,4,5,6];
  int result = list.fold(2, (previousValue, element) => previousValue+element);
  print(result);

Set类型

  • Set是一个无序的,元素唯一的集合,不能有重复值。

  • Set有字面量和构造函数两种声明方式(字面量中用大括号)。

  • 无法通过下标取值。

  • 具有集合特有的操作。比如:求交集、并集、差集等。

  • 字面量

    // 字面量
      var nums = <int>{1,2,3};
      print(nums);
    
    {1, 2, 3}
    
  • 构造函数的形式创建集合

    // 构造函数创建集合
      var fruits = new Set();   // 使用构造函数的形式创建集合
      fruits.add("张三");  // 向集合中添加添加元素
      fruits.add("李四");
      fruits.add("王五");
    
      print(fruits);  // 打印集合
      print(fruits.toList());  // 集合转换为表格形式
    
    {张三, 李四, 王五}
    [张三, 李四, 王五]
    
  • 列表转换为集合

    // 表格转换为集合,重复的元素会被过滤掉
      List list = [1,2,2,3,4,4];  // 定义列表
      print(list.toSet());  // 列表转换为集合
    
    {1, 2, 3, 4}
    
  • 集合特有的操作,交集、差集、并集。

    // 集合特有操作
      var name1 = new Set();
      name1.addAll(['张山','李四',"王五"]);
    
      var name2 = new Set();
      name2.addAll(["王五","赵六","马七"]);
      
      // 求交集,共同有的元素会被显示出来了。
      print(name1.intersection(name2));
      
      // 求并集,将他们合并,共同的元素只留一个。
      print(name1.union(name2));
      
      // 求差集,第一个中有的,而第二个中没有的元素。
      print(name1.difference(name2));
      
      // 返回第一个元素
      print(name1.first);
      
      // 返回最后一个元素
      print(name1.last);
    

Map类型

  • Map是一个无序的键值对(key-value)映射。通常被作为哈希字典。]

  • Map的键值对是成对出现的。

  • 声明方式:

    • var map = {key1:value1,key2:value2};
      
    • var map = new Map();
      
    • map['key'] = value;
      
  • 字面量形式

    // 字面量形式
      var person = {
        'name':'张三',
        'age':18,
        'information':'法外狂徒',
      };
      print(person);
    
    {name: 张三, age: 18, information: 法外狂徒}
    
  • 构造函数

    // 构造函数
      var p = new Map();
      p['name'] = "张三";	// 向map中添加元素
      p['age'] = 18;
      p['info'] = '法外狂徒';
      print(p);
      
      // 访问属性
      print(p['name']);   // 访问name的值
    
    {name: 张三, age: 18, information: 法外狂徒}
    {name: 张三, age: 18, info: 法外狂徒}
    张三
    
  • 判断Map中的Key和Value是否存在,返回值为true或false。

    var p = new Map();
      p['name'] = "张三";
      p['age'] = 18;
      p['info'] = '法外狂徒';
      print(p);
      
    // 判断Map中的Key是否存在
      print(p.containsKey('name')); 
      
      // 判断Map中的Value是否存在
      print(p.containsValue('张山'));
    
    true
    false
    
  • .putIfAbsent()赋值,如果 Key 不存在,我们才赋值(如果key已存在,则不赋值)。

    var p = new Map();
      p['name'] = "张三";
      p['age'] = 18;
      p['info'] = '法外狂徒';
      print(p);
      
      //如果 Key 不存在,向Key赋值
      p.putIfAbsent('score', () => 100);
      print(p);
    
    {name: 张三, age: 18, info: 法外狂徒, score: 100}
    
  • .keys.values获取map中所有的key 和 value。

    // 获取所有的key 和 value
      var person = {'name':'张三','age':18,'information':'法外狂徒'};
      print(person.keys);
      print(person.values);
    
    (name, age, information)
    (张三, 18, 法外狂徒)
    
  • 删除元素

    • .remove(key) – 根据key 进行删除。

      // 删除元素
        var person = {'name':'张三','age':18,'information':'法外狂徒'};
        person.remove('name');
        print(person);
      
      {age: 18, information: 法外狂徒}
      
    • .removeWhere() – 根据条件进行删除。

      // 根据条件进行删除
        var person = {'name':'张三','age':18,'information':'法外狂徒'};
        person.removeWhere((key, value) => key=='name');
        print(person);
      
      {age: 18, information: 法外狂徒}
      

其他数据类型

  • Runes(符文)

    • Runes对象是一个32位字符对象。它可以把文字转换成符号表情特定的文字
    • print(‘\u{1f44d}’) => 👍
    • https://copychar.cc/
  • Symbol

    • 在Dart 中符号用#开头来表示的标识符。
  • dynamic

    • 动态数据类型

运算符(operator)

运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。Dart语言内置了丰富的运算符,并提供了以下类型的运算符:算术运算符、关系运算符、类型判断运算符、赋值运算符、逻辑运算符、按位和移位运算符、条件表达式、级联运算符以及其他运算符

  • 地板除(~/)-- 相除之后,向下取整。

  • 类型判断运算符(is |is!)-- 判断某种变量是否属于某种类型。

  • 避空运算符(?? | ??=) – 如果变量为空,才会对变量赋值,变量不为空,不对其做任何操作。

  • 条件属性访问(?.) – 先判断属性是否存在,存在的话再去访问。

  • 级联运算符(

    • myObject.myMethod();	// 返回myMethod的返回值
      
    • myObject..myMethod();	// 返回myMethod()对象的引用
      
  • ~/地板除,相除之后,向下取整

    // 地板除
    	print(5/3);   // 除法
    	print(5~/3);  // 地板除
    
    1.6666666666666667
    1
    
    • isis!类型判断运算符
      // 类型判断运算符
      List list = [];	// 定义一个列表
      // is运算符
      if (list is List) {	// 判断是否是列表
        print("是List");
      } else {
        print("不是List");
      }
      // is! 运算符
      if (list is! List) {
        print("不是列表");
      } else {
        print("是列表");
      }
      
    
    是List
    是列表
    
  • ????=避空运算符

    // 避空运算符
      print(1 ?? 3);  //返回1,因为1不为空
      print(null ?? 11);  // 返回11,因为第一个参数为null
    
      var name;   //name没有赋值,为空。
      print(name ?? "name为空");
    
      // 赋值避空运算符
      name ??= "张三";  //进行判断,如果为空,将张三赋值给name
      print(name);
    
    1
    11
    name为空
    张三
    
  • 条件属性运算符

    // 条件属性运算符(判断可能为空的属性)
      var m = new Map();
      print(m.length);  // 返回0
    
      var obj;
      // print(obj.length); //The getter 'length' was called on null.
      print(obj?.length);	// 返回null
    
    0
    null
    
  • 级联运算符

    // 级联运算符
      // 普通添加删除数据
      Set s =new Set();
      s.add("张三");
      s.add("李四");
      s.add([1,2,3]);
      s.removeWhere((element) => element=="张三");
      print(s);
      // 级联添加删除数据
      Set set = new Set();
      set..add("张山")
      ..add("赵陆")
      ..add([1,2,3])
      ..removeWhere((element) => element=="张三");
      print(set);
    
算数运算符(Arithmetic Operators)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cpQLAH98-1638454824693)(F:\flutter笔记\Dart_imges\image-20211120170520611.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j755TOBt-1638454824693)(F:\flutter笔记\Dart_imges\image-20211120170555298.png)]

// 算数运算符
    print(2 + 3 == 5);
    print(2 - 3 == -1);
    print(2 * 3 == 6);
    print(5 / 2 == 2.5); // 结果是一个浮点数
    print(5 ~/ 2 == 2); // 结果是一个整数
    print(5 % 2 == 1); // 取余
    print('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1');
// 自增自减运算符
    var a, b;
    a = 0;
    b = ++a; // 在 b 赋值前将 a 增加 1。
    print(a == b); // 1 == 1

    a = 0;
    b = a++; // 在 b 赋值后将 a 增加 1。
    print(a != b); // 1 != 0

    a = 0;
    b = --a; // 在 b 赋值前将 a 减少 1。
    print(a == b); // -1 == -1

    a = 0;
    b = a--; // 在 b 赋值后将 a 减少 1。
    print(a != b); // -1 != 0
关系运算符(Relational operators)

用法基本上和其他语言相同。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BUVseTGm-1638454824694)(F:\flutter笔记\Dart_imges\image-20211120172117279.png)]

类型判断运算符

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nS8oL1WJ-1638454824695)(F:\flutter笔记\Dart_imges\image-20211120172817600.png)]

赋值运算符

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1MB7XT0p-1638454824695)(F:\flutter笔记\Dart_imges\image-20211120172913856.png)]

逻辑运算符

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xJUlUV0F-1638454824696)(F:\flutter笔记\Dart_imges\image-20211120172941265.png)]

按位和移位运算符

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-czXC23LA-1638454824697)(F:\flutter笔记\Dart_imges\image-20211120173013759.png)]

其他运算符

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PtOKcIAb-1638454824697)(F:\flutter笔记\Dart_imges\image-20211120173042224.png)]

三个点 和 两个点

使用...可以进行拼接操作,两个..表示级联操作。

三个点

  List a = ["张三","李四","王五"];
  List b = ["赵六","马奇","黄八"];
  List c = [...a,...b];
  print(c);
  // 结果为:[张三, 李四, 王五, 赵六, 马奇, 黄八]  
  
  Map map1 = {'张三':'1','李四':'2','王五':'3',};
  Map map2 = {'赵六':'4','马奇':'5','黄八':'6',};
  Map map3 = {...map2,...map1};
  print(map3);
  // 结果为:{赵六: 4, 马奇: 5, 黄八: 6, 张三: 1, 李四: 2, 王五: 3}

两个点

class Test {
  //正常写法
  A printfA() {
    var a = A();
    a.a();
    a.b();
    a.c();
    return a;
  }

  //级联写法
  A printfA() {
    return A()..a()..b()..c();
  }
}

class A {
  a() {}

  b() {}

  c() {}
}

函数

声明函数
  • 直接声明
    • Dart中声明函数不需要function关键字
  • 箭头函数
    • Dart中的箭头中,函数体只能写一行且不能带有结束的分号。
    • Dart中的箭头函数,只是函数的一种简写形式。
  • 匿名函数
  • 立即执行函数
声明函数和调用函数
// 定义函数
void printInfo(){	// 无参函数,无返回值
  print("hello world!");
}

int getNum(){	// 有返回值,无参函数
  // return "张三";  // 不能返回String类型值
  return 18;  // int类型声明的函数只能返回int类型的值
}

void main(List<String> args) {
  printInfo();  // 调用函数
  int a = getNum();	// 接收函数返回值
  print(a);
}
hello world!
18
  • void 表示无返回值。
  • return表示返回,但需要根据函数类型进行返回。也就是说函数是什么类型,返回值就是什么类型,而用于接收它的变量类型等级必须高于int类型。
  • 调用函数,如果函数是有参函数,需要为函数传参,才能调用。
匿名函数
void main(List<String> args) {
// 匿名函数
  var myPrint = (value){	// 定义一个匿名函数
    print(value);
  };
  
  List fruits = ["苹果","香蕉","西瓜"];// 定义一个列表、
  // fruits.forEach((element) {element});
  fruits.forEach(myPrint);
}
苹果
香蕉
西瓜
  • 第三行定义了一个匿名函数,并且用myPrint变量接收。
  • 第七行定义了一个列表。
  • 在第9行中使用myPrint替换了(element) {element},原因是在.forEach()方法中传入的也是一个匿名函数。
箭头函数

只能一行显示的简单函数,并且没有分号。

// 箭头函数
  List fruits = ["苹果","香蕉","西瓜"];
  fruits.forEach((element) =>{
     print(element)	// 箭头函数中不能有分号,并且只能有一行
     });
  fruits.forEach((element) =>print(element));	// 只能有一行
苹果
香蕉
西瓜
苹果
香蕉
西瓜
立即执行函数

立即执行函数就是立即执行的函数。

// 立即执行函数
  (
    (int n){
    print(n);
  }
  )(1223556);
1223556
  • 使用一对小括号扩起来的匿名函数。
  • 在小括号后面可以传递参数。
函数参数
  • 必填参数
    • 参数类型 参数名称
  • 可选参数
    • 放在必选参数后面
    • 通过中括号包起来
    • 带默认值的可选参数
  • 命名参数
    • 用大括号包起来
    • 调用函数时,命名参数的名称与声明函数中的名称保持一致。
  • 函数参数
    • 把一个函数的实例当做参数传递给另一个参数
必填参数
// 定义一个String类型的必选参数
  String userInfo(String name){
    return '你好:$name';
  }

void main(List<String> args) {
// 必填参数
  // String res = userInfo(123); // 传入的值必须和定义值类型一样
  String res = userInfo("张三");
  print(res);
}
你好:张三
  • 传入的值必须和定义值类型一样。
  • 定义多个函数时,函数名不能相同,没有Java中的重载机制。
可选参数
// 可选参数
// // 必须给age定一个默认值,如果为null会和int类型冲突。或者使用动态类型 dynamic 和 Object类型
	// String userInfo(String name,[int age=0])		// int类型
  	// String userInfo(String name,[dynamic age])	// Object类型
   String userInfo(String name,[Object age=0]){
       return '你好:$name,年龄:$age';
  }

void main(List<String> args) {
  // 必填参数
  // String res = userInfo(123); // 传入的值必须和定义值类型一样
  String res =userInfo("张三",18);
  print(res);
}
  • 形参类型必须与实参类型一致。

  • 可选参数用中括号包裹起来。

命名参数
// 命名参数
   String userInfo(String name,{int age=0}){	// 使用花括符进行定义,int类型必须赋值
       return '你好:$name,年龄:$age';
  }
  
void main(List<String> args) {
  // 命名参数调用时,必须与声明的形参一致
  String res =userInfo("张三",age:18);	// 使用键值对的形式进行传递参数
  print(res);
你好:张三,年龄:18
  • 命名参数使用花括号包裹起来。
  • 调用时使用 属性名:属性值的形式进行传参。
函数参数
// 函数参数
  var myPrint = (value){	// 定义一个匿名函数
    print(value);
  };
  
  List fruits = ["苹果","香蕉","西瓜"];// 定义一个列表、
  // 将匿名函数传递给forEach函数
  fruits.forEach(myPrint);
苹果
香蕉
西瓜
  • 将匿名函数用值得形式作为forEach函数的值进行传递。
作用域与闭包
函数作用域
  • 内层可以访问外层所定义的内容。
  • 外层不可以访问内层所定义的内容。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8CM0lOs5-1638454824698)(F:\flutter笔记\Dart_imges\image-20211121152031141.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0F3ZnDqs-1638454824699)(F:\flutter笔记\Dart_imges\image-20211121152136184.png)]

闭包
  • Dart中闭包的实现方式与JavaScript中完全一致。
  • 使用时机:既能重用变量,又保护变量不被污染。
  • 实现原理:外层函数被调用后,外层函数的作用域对象(AO)被内层函数引用着,导致外层函数的作用域对象无法释放,从而形成闭包。

  parent(){
    var money = 1000;
    return (){
      money-=100;
      print(money);
    };
  }

  var p = parent();   // 将parent函数保存为p函数
  p();    // 每次调用完之后只释放return里面的匿名函数的地址,并没有parent函数的地址。
  p();    // 所以可以一直调用一直减
  p();
900
800
700
异步函数
  • JavaScript中,异步调用通过Promise来实现
    • async(异步)函数返回一个Promise(承诺)。await(等待)用于等待Promise。
  • Dart中,异步调用通过Future(未来)来实现。
    • async函数返回一个Future,await用于等待Fature。

类与对象

类-简介
  • 类是通过class声明的代码段,包含属性和方法。
    • 属性:用来描述类的变量。
    • 方法:类中的函数。
  • 对象是类的实例化结果(var obj=new MyClass())
  • 在>=Dart 2.12的版本中添加了late关键字,在类中定义的空属性需要使用late关键字
  • 类名首字母大写,其余字母单词首字母也必须大写。
  • 相当于一个模板,对象就是根据模板做出来的东西。
  • 类只是抽象的说说,并不会去实现,而对象需要具体干出这些活。
  • 就像你女朋友就是一个类,只知道指挥,而你就是她的对象,通过她发出的指令去做,啥都要干。
  • 如果类特别多的话,可以将类单独放入一个文件中,用调用方式进行使用。
  • 编程方式
    • 面向对象编程(OOP)
    • 面向过程编程(POP)
      • JavaScript就是 面向过程编程,它的class只是语法糖,语法糖让程序更加简洁,有更高的可读性。
      • JavaScript中没有类。
  • Dart中所有的内容都是对象。
声明类
  • 使用class关键字进行定义类,类名后面是一对花括符,如:class 类名{};

  • 实例化类使用new关键字。

  • new对象的时候类名后面需要写一对小括号,比如:类型 对象名 = new 类名();

// 声明类
class Person{
  // 类的属性
  String name = "张三";

  // 类的方法
  void getInfo(){
    print("我是$name");
  }
}

void main(List<String> args) {
  // 实例化类,得到一个对象
  Person p = new Person();
  // 访问类中的属性
  print(p.name);
  // 访问类中的方法
  p.getInfo();
}
张三
我是张三

dart中所有的内容都是对象。

为什么这么说呢?看下面例子。

// new一个Map对象,Map是Dart中内置的一个类,不需要自己创建
Map m = new Map();	

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cFrApKDO-1638454824699)(F:\flutter笔记\Dart_imges\image-20211121170336802.png)]

  • 第一个大框中,在vscode中,类的属性是通过[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-utIzZydm-1638454824700)(F:\flutter笔记\Dart_imges\image-20211121170527252.png)]来定义的。
  • 第二个大框中,在vscode里,类的方法是通过[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zPUCmBD3-1638454824701)(F:\flutter笔记\Dart_imges\image-20211121170922049.png)]来定义的。
  • 基本上每个东西自己的属性和方法。
将类放入单独的文件中

在当前目录中创建一个lib目录,在该目录中创建一个叫lei.dart文件,在文件中写入一个类,如下所示:

// lei.dart
// 创建一个人类
class Person{
  int a =10;
  late int b;	// 创建一个空值b
  // 有参构造函数
  Person(this.a,this.b){
    print('${a}  $b');
  }
  // 命名构造函数
  Person.info(){
    print("这是命名构造函数");
  }
}
// 创建一个动物类
class Animal{
  late String name;
  late String info;

  Animal(this.name,this.info){
    print("传入的动物是:$name ,种类是:$info");
  }
}

在上面类的上一级目录创建一个名为Factory_Constuctor.dart的文件,并写入下面代码,主要是实现类的调用。

import 'lib/lei.dart';

void main(List<String> args) {
  // Person p = new Person(123,456);
  Person p = new Person.info();
  int a1 = p.a;
  print("a为:$a1");

  Animal animal = new Animal("小狗", "泰迪");
  print(animal.name);
}
这是命名构造函数
a为:10
传入的动物是:小狗 ,种类是:泰迪
小狗
  • 使用`import ''方式导入模块。
构造器(构造函数)
默认构造函数
  • 与类同名的函数称为构造函数,在实例化时,自动被调用。
  • 构造函数中设置形参称为有参构造函数,反之为无参构造函数。
  • this指向的是类中的属性,目的是为了避免混淆。
// 构造函数
class Point{
  // 定义类属性
  num x=0,y=0;
  // 声明普通构造函数,在创建对象时,就会自动执行构造函数
  Point(num x, num y){	// 构造函数与类同名,并设置为有参构造函数
    print("这是构造函数");
    // 打印
    print("参数值$x $y");
    // 为了避免有歧义,使用this指向类中属性
    this.x=10;	//指向第4行的x,并改变x的值为10
    this.y=20;	//指向第4行的y,并改变y的值为20
  }
}

void main(List<String> args) {
  Point p = new Point(40,30);	// 创建对象,并给构造函数传递参数
  print("属性值:");
  print(p.x);	// 打印属性x的值
  print(p.y);
}
这是构造函数
参数值40 30
属性值:
10
20
  • 构造函数简写
class Point{
  // 定义类属性
  num x=0,y=0;
  // 简写构造函数
  Point(this.x, this.y);  // 简写构造函数,需要加分号结尾。
  }

void main(List<String> args) {
  Point p = new Point(40,30);	// 创建对象,并给构造函数传递参数
  print("属性值:");
  print(p.x);	// 打印属性x的值
  print(p.y);
}
属性值:
40
30
命名构造函数
  • 默认构造函数
    • 与类名同名的函数,在实例化时,自动被调用。
  • 命名构造函数
    • 在类中使用命名构造函器(类名.函数名)实现多个构造器,可以提供额外的清晰度。
class Point{
  // 定义类属性
  num x=0, y=0;
  // 简写构造函数
  Point(this.x,this.y);
  // 命名构造函数
  Point.origin(){   // origin-原始的
    x = 0;
    y = 0;
  }

  Point.fromJson({x:0,y:0}){
      this.x = x;
      this.y = y;
  }
}

void main(List<String> args) {
  // 默认坐标
  Point p1 = new Point.origin();
  print(p1.x);

  // 手动设置坐标
  Point p2 = new Point.fromJson(x: 20,y: 30);
  print(p2.x);
}
0
20
常量构造函数
  • 如果类生产的对象不会改变,可以通过常量构造函数使这些对象成为编译时常量。
  • 常量编译快,因为常量不改变。
  • 创建对象时可以不使用new关键字。
class Point{
  num x=0;
  num y=0;
    
  // 构造函数简写
  Point(this.x,this.y);
}

class ImmutablePoint{
  // 属性必须通过 final 声明
  final num x;
  final num y; 

  // 常量构造函数,必须通过 const 声明,并且常量声明的函数不能有函数体
  const ImmutablePoint(this.x,this.y);
}

void main(List<String> args) {
  var p1 = new Point(1, 3);
  var p2 = new Point(1, 3);
  print(p1 == p2);
    
  // 常量构造函数,可以当做普通构造函数使用
  var p3 = new ImmutablePoint(3,4);
  var p4 = new ImmutablePoint(3,4);
  print(p3 ==p4);
    
  // 声明不可变对象,必须通过 const 关键字,而new出来的对象是指向不同的地址,所以不相等
  var p5 = const ImmutablePoint(3,4);
  var p6 = const ImmutablePoint(3,4);
  print(p5 ==p6);   // true
    
  // 创建对象可以不用 new 关键字进行实现
  var p7 = ImmutablePoint(3,4);
  var p8 = ImmutablePoint(3,4);
  print(p3 ==p4); // false
}
私有与公有

分为私有属性和私有方法,就是在属性名或者方法名前面加一个短下划线_,例如:_name_Animal()方法就是被定义为私有的属性和方法。

  • 私有属性只能在当前类中访问。

lib目录下创建一个名为private.dart的文件,里面写一下代码:

// 创建一个动物类
class Animal{
  late String _name;    // 定义私有属性
  late String info;

  // ignore: unused_element
  _Animal(_name,info){
    print("传入的动物是:$this._name ,种类是:$info");
  }

  int _aaa(){   // 定义私有方法
    return 100;
  }
}

// 创建一个人类
// 创建一个人类
class Person{
  late String name;
  late String info;
  
  Person(this.name,this.info){
    print(this.name);
    print(this.info);
  }
}

privaDemo.dart文件中调用。

import 'lib/private.dart';

void main(List<String> args) {
  Animal animal = new Animal("晓白", "牧羊犬");
  // var aa = animal._aaa();  // 不能调用私有方法
  // String name = animal._name;   // 不能访问私有属性
  print(animal.info);

  Person p = new Person("张三","法外狂徒");
  print("name为:${p.name}");

}
传入的动物是:晓白 ,种类是:牧羊犬
牧羊犬
张三
法外狂徒
name为:张三
工厂构造函数

通过 factory 声明,工厂函数不会自动生成实例,而是通过代码来决定返回的实例。


getter 与 setter
  • get方法,注意方法名之后不加小括号。
  • set方法,注意set方法需要加小括号。
class Rect{
  late num _height;
  late num _width;

  Rect(this._height,this._width);
// get方法,注意方法名之后不加小括号
  get getarea{
    return this._height* this._height;
  }

// set方法,注意set方法需要加小括号
  set setHeight(value){
    this._height = value;
  }
}

void main(List<String> args) {
  Rect rect = new Rect(10, 20);
  // 得到getarea值
  print("矩形面积为:${rect.getarea}");
  // 调用set方法为height设置值
  rect.setHeight=30;
  print("面积为:${rect.getarea}");
}
矩形面积为:100
面积为:900
类的初始化列表
  • 先赋值,在进行实例化构造函数。
  • 将需要赋值的属性写在构造函数小括号后面。
class Rect{
  late num height;
  late num width;
  // 初始化列表
  Rect():height=3,width=20{
    print("height为:${this.height},width为:${this.width}");
  }
}

void main(List<String> args) {
  // 创建对象
  Rect rect = new Rect();
}
height为:3,width为:20
静态成员
  • 使用static关键字来实现类级别的变量和函数。
  • 静态方法不能访问非静态成员,非静态方法可以访问静态成员。
  • 静态方法和静态属性不能由对象进行访问,只能由类直接访问。
import 'dart:ffi';

class Person{
  static String name = "张三";  // 定义静态属性
  int age = 18;    // 定义非静态方法
  // 定义静态方法
  static void show(){
    print("我的名字叫:${name}");
  }

  void printInfo(){
    print("${name}的信息");
    print("年龄为:${this.age}");
    // 调用静态方法
    show();
  }

  static void printUserInfo(){
    print(name);  // 访问静态属性
    show();   // 调用静态方法

    // print(age);   // 静态方法无法范文非静态属性
    // this.printInfo();  // 静态方法无法访问非静态方法
    // printInfo();       // 静态方法无法访问非静态方法
  }
}

void main(List<String> args) {
  // Person person = new Person();  // 创建对象
  // person.show();  // 被修饰的静态方法不能由创建的对象进行访问
  // 由类名进行访问
  print(Person.name);
  Person.show();

  // 非静态方法与非静态属性的使用
  Person person = new Person();
  person.printInfo();
  print(person.age);
  // 调用静态方法
  Person.printUserInfo();
}
张三
我的名字叫:张三
张三的信息
年龄为:18
我的名字叫:张三
18
张三
我的名字叫:张三
继承
  • 子类使用extends关键字来继承父类。
  • 子类会继承父类里面可见的属性和方法,但是不会继承构造函数。
  • 子类能复写父类的方法 getter 和setter。
class Person{
  String name ="张三";
  num age = 20;

  void printInfo(){
    print("姓名:${this.name},年龄:${this.age}");
  }
}
  // 创建一个男人类,并继承人类
class Man extends Person{
  
}

void main(List<String> args) {
  // 创建一个男人对象
  Man man = new Man();
  print(man.name);  // 调用人类的name
  man.printInfo();  // 调用父类的方法
}
张三
姓名:张三,年龄:20
super关键字的使用
  • 重写父类构造函数,为父类构造函数进行传参。
  • 因为父类里面的属性没有赋值,而子类又需要调用父类的属性,只有通过构造函数进行赋值。
  • 在复写父类的方法时,记得加上@override关键词。
class Person{
  late String name;
  late num age;
  // 构造函数
  Person(this.name,this.age);

  void printInfo(){
    print("Person类");
    print("姓名:${this.name},年龄:${this.age}");
  }
}
  // 创建一个男人类,并继承人类
class Man extends Person{
  // 定义子类的属性
  late String sex;
  // 重写父类构造函数
  Man(String name, num age, String sex) : super(name, age){ // 实例化构造函数的时候,将子类的name与age传递给父类的构造函数
    this.sex =sex;  // 将子类的构造函数里的sex赋值给子类sex属性
  }
  // 复写父类方法
  @override
  void printInfo(){
    print("man类");
    print("姓名:${this.name},年龄:${this.age},性别:${this.sex}");
  }

  // 定义子类的方法
  void run(){
    print("儿子类子跑");
  }
}

void main(List<String> args) {
  // 创建父类对象
  Person person = new Person("张三",18);
  person.printInfo();
  // Person person1 = new Person("李四",56);
  // person1.printInfo();
  // 创建子类对象
  Man man =new Man("张大娃", 32,"男");
  man.printInfo();  // 通过构造函数,找到父类的构造函数,使用父类的方法进行打印。
  man.run();  // 子类调用子类的方法

}
Person类
姓名:张三,年龄:18
man类
姓名:张大娃,年龄:32,性别:男
儿子类子跑

使用super为命名构造函数传参。

class Person{
  late String name;
  late num age;
  // 命名构造函数
  Person.xxx(this.name,this.age);	

  // 创建一个男人类,并继承人类
class Man extends Person{
  // 定义子类的属性
  late String sex;
  // 重写父类构造函数
  Man(String name, num age, String sex) : super.xxx(name, age){ // 实例化命名构造函数的时候,将子类的name与age传递给父类的构造函数
    this.sex =sex;  // 将子类的构造函数里的sex赋值给子类sex属性
  }
  // 复写父类方法
  @override
  void printInfo(){
    print("man类");
    print("姓名:${this.name},年龄:${this.age},性别:${this.sex}");
  }

void main(List<String> args) {

  Person person = new Person.xxx("张三",18);	// 命名构造函数传参
  person.printInfo();

  Man man =new Man("张大娃", 32,"男");
  man.printInfo();  // 通过构造函数,找到父类的构造函数,使用父类的方法进行打印。
  man.run();  // 子类调用子类的方法

}
Person类
姓名:张三,年龄:18
man类
姓名:张大娃,年龄:32,性别:男
  • 子类调用父类方法,在子类的方法名前面加上一个supper关键字,例如:supper.run(),调用父类的run()方法

  • 子类中使用父类的属性时,可以使用this关键词进行调用。

抽象类
  • 抽象类主要用于定义标准。
  • 抽象类通过abstract 关键字来定义。
  • Dart中的抽象方法不能用abstract声明,Dart中没有方法体的方法我们称为抽象方法。
  • 如果子类继承抽象类必须得实现里面的抽象方法。
  • 如果把抽象类当作接口实现的话。必须实现抽象类里面定义的所有属性和方法。
  • 抽象类不能被实例化,只有继承他的子类可以实例化。
  • extends 抽象类和 implements 的区别:
    • 如果复用抽象类里面的方法,并且要用抽象方法约束子类的话,我们要用extends继承抽象类。
    • 如果只是把抽象类当作标准的话,我们需要用implements实现抽象类。
// 定义抽象类
abstract class Animal{
  eat();  // 抽象方法
  run();
  // 在抽象类中定义,该方法可以有方法体
  printInfo(){
    print("我来自抽象类");
  }
}
// 子类继承父类必须实现父类中的所有方法
class Dog extends Animal{
  @override
  eat() {
    print("小狗在啃骨头");
  }

  @override
  run() {
    print("小狗再跑");
  }
}
// 子类继承父类必须实现父类中的所有方法
class Cat extends Animal{
  @override
  eat() {
    print("小猫在吃饭");
  }

  @override
  run() {
    print("小猫在跑");
  }
}

void main(List<String> args) {
  Dog dog = new Dog();
  dog.eat();
  dog.printInfo();  // 调用抽象类的方法

  Cat cat = new Cat();
  cat.eat();
  cat.printInfo();  // 调用抽象类的方法
}
小狗在啃骨头
我来自抽象类
小猫在吃饭
我来自抽象类
多态
  • 多态就是父类定义的一个方法不去实现,让他的子类去实现,每个子类有不同的表现。
  • 子类的实例赋值给父类。
// 定义抽象类
abstract class Animal{
  eat();  // 抽象方法
}
// 子类继承父类必须实现父类中的所有方法
class Dog extends Animal{
  @override
  eat() {
    print("小狗在啃骨头");
  }
  //
  run(){
    print("小狗正在跑");
  }
}
// 子类继承父类必须实现父类中的所有方法
class Cat extends Animal{
  @override
  eat() {
    print("小猫在吃饭");
  }
  //
  run(){
    print("小狗正在跑");
  }
}

void main(List<String> args) {
  // 子类的一个实例赋值给父类的应用,也就是所在下面创建的对象就属于父类,而run方法时子类中定义的,所以父类无法调用子类的run方法。
  Animal dog = new Dog();
  dog.eat();
  // dog.run();  // 无法调用run方法

  Animal cat = new Cat();
  cat.eat();
  // cat.run(); // 无法调用run方法,父类中没有run 方法
}
小狗在啃骨头
小狗正在跑
小猫在吃饭
接口
  • Dart的接口没有interface关键字定义接口,普通类或抽象类都可以作为接口被实现。
  • 使用implement关键字进行实现。
  • 如果实现的类是普通类,会将普通类和抽象类中的属性的方法全部重写一边。
  • 抽象类可以定义抽象方法,普通类不可以,普通类不可以,所以一般如果要实现象Java接口那样的方式,一般会使用抽象类。
  • 建议使用抽象类定义接口。
  • 接口就是约定、规范。
// 定义接口
abstract class Person{
  late String name;
  eat();
  run();
  info(String str);
}
// 继承Person接口
class Man implements Person{
  @override
  late String name;

  @override
  eat() {
    print("男人在吃饭");
  }

  @override
  info(String str) {
    print("${str}的信息");
  }

  @override
  run() {
    print("男人在奔跑");
  }
  Man(String str);
  
}
// 继承Person接口
class Women implements Person{
  @override
  late String name;

  @override
  eat() {
    print("女人在吃饭");
  }

  @override
  info(name) {
    print("${name}的信息");
  }

  @override
  run() {
    print("女人在奔跑");
  }
  // Women(this.name);
}

void main(List<String> args) {
  // 创建man对象
  Man man = new Man("张三");
  man.info("张三");
  // 创建women对象
  Women women = new Women();
  women.info("张如玉");
  women.run();
}

可以将这些接口、类、main进行分离,分别放在不同的Dart文件中,如果要导入这些模块,需要使用improt关键字进行导入。

多接口 mixins新特性
  • mixins的中文意思是混入,就是在类中混入其他功能。
  • 在Dart中可以使用mixins 实现类似多继承的功能。
  • 作为mixins的类只能继承自Object,不能继承其他类。
  • 作为mixins的类不能有构造函数。
  • 一个类可以mixins多个mixins类。
  • mixins绝不是继承,也不是接口,而是一种全新的特性。

小结:

多继承:
	使用with后面的类:
	 被继承的类不能继承其他类  
	 被继承的类中不能有构造函数
	 
		with
			class X with a,b{
				...
			}
		也可以
			class X extends c with a,b{
				...
			}

抽象类:
	抽象类不能实例化,继承必须实现内部抽象方法和属性
	抽象类可以继承extends(复用其中普通方法),可以实现implements(当模板)
	抽象类中的方法不加abstract

	abstract class x{
		int a;
		int b;
		void f(); 抽象方法
		void ff(){...};  普通方法
	}
	继承该抽象类的类,只用实现抽象方法,普通方法和属性不用重写
	

多态:父类引用子类,父类的方法引用子类的方法,只能调用父类中存在的方法,不能调用子类中父类没有的方法
	Animal c=new Dog();   c只能调用Animal中的方法,且引用子类Dog中的相同方法

接口:
	使用abstract定义抽象类为接口,操作和抽象类一样
	抽象类的所有属性和函数都必须重写
	abstract class x{
		int a;
		int b;
		void f(); 抽象方法
		void ff(){...};  普通方法
	}
	
	class c implements x{
		int a;
		int b;
		void f(){}
		void ff(){}
	}
	
多接口:
	class c implements a,b{...}
泛型
  • 泛型就是解决 接口方法的复用性、以及对不特定数据类型的支持(类型校验)。
  • 不指定类型就是放弃了类型校验。想传入什么类型就可以传入什么类型。
  • T getValue<T>(T value){..方法体..return..}
  • 第一个T表示返回类型,第二个T表示函数类型,第三个T表示参数类型。
泛型方法
// 定义泛型方法,一般使用大写 T 表示
T getValue<T>(T value){
  return value;
}

void main(List<String> args) {
  // 传入int类型参数
  var d =  getValue(123);
  print(d);
  // 传入string类型参数并指定类型。
  var str =getValue<String>("张三");
  print(str);
}
123
张三
泛型类
  • 泛型就是一种通用的类型格式,一般用在集合中,用来指定该集合中应该存储的对象格式。
  • 泛型一般使用大写的单个字符来表示,通常来说是E, T, S, K 和 V等。
  • 巧妙的使用泛型还能够减少我们的代码量,因为泛型可以代表一类通用的类型。
class Mylist<T>{  // 定义一个泛型类
  List list = <T>[]; // 定义一个泛型列表
  // 定义一个泛型方法,负责向列表中添加数据
  void add(T value){  
    this.list.add(value);
  }
  // 返回列表
  List getList(){
    return list;
  }
}

void main(List<String> args) {
    
  // 限定为String类型的列表
  Mylist l2 = Mylist<String>();
  l2.add("张三");   // 添加一个string参数
  l2.add("李四"); 
  // l2.add(123);  // 在创建对象时限定为String类型,只能传入String数据
  print(l2.getList());  // 打印列表
    
  // 限定为int类型的列表
  Mylist l3 = Mylist<int>();
  l3.add(123);
  l3.add(456);
  print(l3.getList());
}
[1, 张三, false]

。。。。。。。。
有时间再更新
。。。。。。。。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值