flutter 语法 - CSDN
精华内容
参与话题
  • Flutter学习之Dart语法特性

    千次阅读 2019-03-13 09:25:04
    第一天把Flutter环境搭建了,并简单实现第运行第一个Flutter项目,感觉很不错,一些基本操作和原生体验差不多。用Flutter框架写过App项目的开发者都知道,Flutter是一个使用Dart语言开发的跨平台移动UI框架,通过自...

    一、前言

    第一天把Flutter环境搭建了,并简单实现第运行第一个Flutter项目,感觉很不错,一些基本操作和原生体验差不多。用Flutter框架写过App项目的开发者都知道,Flutter是一个使用Dart语言开发的跨平台移动UI框架,通过自建绘制引擎,能高性能、高保真地进行Android和IOS开发。可能很多开发者都会像我一样,会有个疑问?为什么Flutter会选择Dart呢?我特意查了下网上资料,总结以下下面六点:

    • Dart在release下是AOT(Ahead Of Time, 运行前,比如普通的静态编译)编译的,编译成快速、可预测的本地代码,使Flutter几乎都可以使用Dart编写,在Debug下,Dart是JIT(jUST In Time 运行时编译,边运行边编译,java虚拟机就用到)编译,开发周期快,就好像Flutter亚热级的热重载。
    • Dart可以更轻松地创建以60fps运行的流畅动画和转场,60fps是一个什么概念呢?我相信王者荣耀大家都知道,王者荣耀的游戏时帧率评测报告是在40fps-50fps中,越高的fps会提供更快速的操控流畅度,体验感更舒适。
    • Dart可以在没有锁的情况下进行对象分配和垃圾回收。像JavaScript一样,Dart避免了抢占式调度和共享内存,大多数支持并发执行线程的计算机语言(Java、Kotlin、Swift)都使用抢占式来切换线程,抢占就会产生竞态条件,竞态条件很有可能导致严重错误,如应用程序崩溃导致数据丢失等等,一般解决竞态条件使用来保护,但是锁本身可能导致卡顿,也有可能产生死锁,而Dart针对这个问题,采取了isolate方法来解决,Dart中的线程称为isolate,不共享内存。那么Dart是单线程的,意味根本不允许抢占。单线程有助于开发者确保关键功能(动画和转场)完成而无需抢占。 Flutter应用程序被编译为本地代码,因此它们不需要再领域之间建立缓慢的桥梁,所以启动速度快得多。
    • Dart使Flutter不需要单独的声明式布局语言,如JSX或XML,或者单独的可视化界面构建器。其实针对这点我觉得Android的XML界面构建器是很直观阅读的。
    • Dart容易学习,具有静态和动态语言用户都熟悉的特性。
    • 易于移植,Dart可编译成ARM和X86代码,这样Dart移动应用程序可以在IOS、Android和其他地方运行。

    那知道Flutter为什么选择Dart之后,作为开发Flutter项目来讲,把Dar学好就很有必要了,下面就从基本语法开始对Dart入门。

    二、Dart环境

    1.本地环境

    我这边本地是用IntelliJ IDEA来编写Dart项目,我这边简单说一下环境配置流程:

    1. 下载Flutter sdk 之前下载过,在第一天配置Android Stdio环境有说。
    2. Dart插件的安装,Windows下选择FileSettingPlugins搜索Dart下载,而Mac下选择屏幕左上角IntelliJ IDEAPerferencesPlugins,如下图:

    Dart下载
    下载完重启即可。

    1. 配置Dart SDK位置,Windows下选择FileSettingLanguages & FrameworksDartDart SDK Path配置,而Mac下选择屏幕左上角IntelliJ IDEAPerferencesLanguages & Frameworks,如下图:

    配置Dart sdk
    4.在FileNewProjectDart,选择Dart语言,如下图:

    选择dart语言

    1. 创建项目名字,设置项目路径,如下图:


    6. 最后点击Finish按钮,会看到项目已经被创建了,如下图:

    Dart运行结果示意

    2.网页环境

    除了上面通过IntelliJ IDEA本地配置环境外,如果觉得上面配置Dart环境太麻烦就走网页环境,就是下面这个链接直接在线编写Dart小语法练习项目https://dartpad.dartlang.org/,下面例子是循环输出5个hello,示意图如下:

    线上学习Dart看还是很方便快捷的注意:可能需要科学上网才能运行出结果.

    三、Dart一些概念

    • 在Dart中,一切都是对象,所有的对象都是继承Object,也就是所有能够使用变量引用的都是对象,每个对象都是一个了类的实例。在Dart中甚至数字、方法和null都是对象。
    • 没有赋初值的变量都会有默认值null
    • 标识符可以以字母或者_下划线开头,后面可以是其他字符和数字的组合。
    • Dart支持顶级方法,如main方法,同时还支持在类中定义函数(静态函数和实例函数),还可以在方法中定义方法,Dart支持顶层变量,也支持类变量或对象变量。
    • Dart没有publicprotectedprivate关键字。如果某个变量以下划线_开头,代表这个变量是在库中是私有的。
    • Dart中的类和接口都是统一的,类即是接口,你可以继承一个雷,也可以实现一个类,自然也包含了良好的面向对象和并发编程的支持。
    • final的值只能被设定一次。const是一个编译时的常量,可以通过const来创建常量值,var n = const[],这里n还是一个变量,只是被赋值了一个常量值,它还是可以符其他值。实例变量可以是final,但不能是const。
    • Dart是强类型语言,但可以用var或者dynamic来声明一个变量,Dart会自动推断其数据类型,dynamic类似C#
    • 使用静态类型可以更清晰表面你的意图,并且可以让静态分析工具来分析你的代码。
    • Dart在运行之前会先解析你的代码。你可以通过使用类型或者编译时常量来帮助Dart去捕获异常以及让代码运行的更高效。
    • Dart工具可以指出两种问题:警告和错误。警告只是说你的代码可能有问题,但是并不会阻止你的代码执行。错误可以是编译时错误也可以是运行时错误。遇到编译时错时,代码将无法执行;运行时错误将会在运行代码的时候导致一个异常。

    四、Dart语法

    1.关键字

    下表是Dart语言的关键字

    const null class new this
    as default final continue throw
    assert deferred finally operator true
    async do for part try
    async* dynamic get rethrow typedef
    await else if return var
    break enum implements set void
    case export import static while
    catch external in super with
    false extends is switch yield
    abstract factory library sync* yield*

    2.一个最基本的Dart程序

    //定义打印数字方法
    printNumber(num Number){
      print('The number is $Number');
    }
    
    //执行程序入口
    void main(){
      //定义初始化一个变量
      var number = 6.76;
      //调用打印数字方法
      printNumber(number);
    }
    

    上面是一个简单基本的Dart程序,虽然简单但是用到了Dart很多特性:

    • //这是注释符号,还可以用/*...*/这是很多语言的注释符号
    • num这是数值型,Stringintbool是另外其他几种类型。注意:数值型num包含整形int和浮点型double
    • 6.76是一个数字常量,数字常量是编译时常量。
    • print()打印内容的方法
    • "..."或者是'...'表示字符串常量
    • $variableName或者是${expression}是字符串插值:在字符串常量中引用变量或者表达式
    • var一种不指定类型声明变量的方式
    • main()是Dart程序的入口方法,每个程序都需要一个这样得分方法

    3.Variables变量

    var name = 'knight';
    上面是声明变量并赋值的示例,变量是一个引用,上面的名字为name的变量引用一个内容为"knight"的String对象。

    4.Default value(默认值)

    没有初始化的变量自动获取一个默认值为null。类型为数字的变量如果没有初始化那么默认的值也是null,因为数字类型也是对象,上面直接上代码:

    //定义打印数字方法
    printNumber(num Number){
      print("The number is $Number");
    }
    
    //执行程序入口
    void main(){
      //定义初始化一个变量
      var number;
    
      //调用打印数字方法
      printNumber(number);
    }
    

    上面打印的结果是The number is null

    5.Optional types(可选的类型)

    声明变量的时候,可以选择加上具体类型,如下面:

      //定义初始化一个变量
      double number = 6.666;
    

    添加类型可以更加清晰表达你的意图。IDE编译器等工具有可以使用类型来更好的帮助,提供提示代码补全,提前发现bug等功能。

    6.Final and const

    如果以后不打算修改一个变量,使用final或者const。一个final变量只能赋值一次;一个const变量是编译时常量。注意:const变量同时也是final变量,实例变量可以为final但不能是const。直接上例子:

    //定义初始化一个变量
      final double number = 6.666;
      number = 6.667;
      //调用打印数字方法
      printNumber(number);
    

    上面例子用final修饰number并赋值,但number = 6.67的时候,想重新给number再赋值的时候,编译错报错:number,a final variable,can only be set once.,意思和上面所说的一样就是final变量只能赋值一次!下面改为定义为const来修饰number:

      //定义初始化一个变量
      const double number = 6.666;
      number = 6.667;
      //调用打印数字方法
      printNumber(number);
    

    同样number = 6.667编译器会报错Constant variables can not be assigned a value意思是常量值不能赋值,上面也说了,因为const变量同时也是final变量。如果const变量在类中,请定义为static const。可以直接定义const和旗初始值,也可以定义一个const变量使用其他const变量的值来初始化其值,如下面:

      //定义初始化一个变量
      const double number = 6.66;
      const double number1 = 2 * number;
    

    上面例子的number1就是用了number来将自己初始化值,const关键字不仅仅只用来定义常量。有可以用来创建不变的值,还能定义构造函数为const类型
    ,这中类型的构造函数创建的对象是不可改变的,任何变量都可以有一个不变的值。

    7.Built-in types(内置的类型)

    在Dart有几种内置的数据类型:数值型-Number、布尔型-boolean、键值对-Map、字符串-String、列表-List、其他类型-Runes、Symbols

    7.1数值型-Number

    Dart中提供了两种类型:

    数值类型

    //执行程序入口
    void main(){
    
    
      //整形,其取值通常位于-2的53次方到2的53之间。
      num x = 777;
      //浮点数 64位
      x = 777.7;
    
      int y = 777;
      y = 777.7;       //这一行编译器会报错,因为将int型的数据转为double型
    
      double n = 77,7;
      d = 77;          //这个地方会报错,因为将double
    
      int x1 = 7;
      int x2 = 77;
      int x3 = 777;
      int x4 = 7777;
    
      print('${x1.bitLength}'); //占了3个bit 相当于00000000 00000111
      print('${x2.bitLength}'); //占了7个bit 相当于00000000 01001101
      print('${x3.bitLength}'); //占了10个bit 相当于00000011 00001001
      print('${x4.bitLength}'); //占了13个bit 相当于00011110 01100001
    
    
    }
    

    上面例子可以看到三个点:

    • 使用num声明的变量,可以随意的转换类型,如果使用int或者double明确的声明,那就不能转换了
    • 判断一个int值需要多少位时,可以使用bitLength

    8.数值型的操作

    运算符:+、-、*、/、~/、%
    常用属性:isNaN、isEven、isOdd
    常用方法:abs()、round()、floor()、ceil()、toInt()、toDouble()

    //执行程序入口
    void main(){
    
    
      int i =7;
      double d = 10.1;
    
      print(i / d);               //0.6930693069306931
      print(i ~/ d);              //0   这个操作是取整 就是得出商
    
      print(i.isOdd);             // 判断是奇数
      print(i.isEven);            // 判断是偶数
    
    
      //String -> int
      var x1 = int.parse("7");
      print(x1 == 7);              //输出true
    
      //Sting -> double
      var x2 = double.parse("7.7");
      print(x2 == 7.7);             //输出true
    
      //int -> String
      var x3 = 7.toString();
      print(x3 == '7');             //输出true
    
      //double -> String
      var x4 = 7.1234.toStringAsFixed(2);
      print(x4 == '7.12');          //输出true
    
      //求绝对值
      var x5 = (-7).abs();
      print(x5 == 7);
    
      //四舍五入1
      var x6 = (7.7).round();
      print(x6);                   //输出8
    
      //四舍五入2
      var x7 = (7.3).round();
      print(x7);                   //输出7
    
      //求大小于它的最大整数
      var x8 = (7.7).floor();
      print(x8);                   //输出7
    
      //求大于它的最小整数
      var x9 = (7.7).ceil();
      print(x9);                   //输出8
    
      double num1 = 7.77;
      print(num1);                //结果是7.77
      double num2 = 7;
      print(num2);                //结果是7.0
      int num3 = 7;
      print(num3.toDouble());     //int 转 double 结果是7.0
      double num4 = 7.77;
      print(num4.toInt());        //double 转 int 结果是7
      
    }
    

    上面列出了一些平时遇到最多的操作,如求余,求整,类型转换等。

    9.Strings(字符串)

    Dart字符串是UTF-16编码的字符序列,可以使用单引号或者双引号来创建字符串:

    //执行程序入口
    void main(){
    
      String m_str1 = '单引号字符串';
      String m_str2 = "双引号字符串";
    
      print(m_str1);        //输出:单引号字符串
      print(m_str2);        //输出:双引号字符串
    
    }
    

    String中单、双引号互相嵌套情况

    //执行程序入口
    void main(){
    
      String m_str1 = '单引号中的"双引号"字符串';
      String m_str2 = "双引号中的'单引号'字符串";
    
      print(m_str1);        //输出:单引号中的"双引号"字符串
      print(m_str2);        //输出:双引号中的'单引号'字符串
    
      //单引号里面有单引号,必须在前面加反斜杠
      String m_str3 = '单引号中的\'单引号\'';
      String m_str4 = "双引号里面有双引号,\"双引号\"";
      print(m_str3);        //输出:单引号中的'单引号'
      print(m_str4);        //输出:双引号里面有双引号,"双引号"
    
    }
    

    单引号嵌套单引号之间不允许出现空串(不是空格),双引号嵌套双引号之间不允许出现空串:

     //String m_str5 = '单引号''''单引号'; //报错
      String m_str6 = '单引号'' ''单引号';
      print(m_str6);        //输出: 单引号 单引号
    
      String m_str7 = '单引号''*''单引号';
      print(m_str7);        //输出: 单引号*单引号
    
      //String m_str8 = "双引号""""双引号";   //报错
    
      String m_str9 = "双引号"" ""双引号";
      print(m_str9);        //输出: 双引号 双引号
    
      String m_str10 = "双引号""*""双引号";
      print(m_str10);       //输出: 双引号*双引号
    

    单双引号混合嵌套空串是可以的,如下:

     String m_str11 = '单引号""""单引号';
      print(m_str11);       //输出: 单引号""""单引号
    
      String m_str12 = '单引号"" ""单引号';
      print(m_str12);       //输出: 单引号"" ""单引号
    
      String m_str13 = '单引号""*"""单引号';
      print(m_str13);       //输出: 单引号""*"""单引号
    
      String m_str14 = "双引号''''双引号";
      print(m_str14);       //输出:  双引号''''双引号
    
      String m_str15 = "双引号'' ''双引号";
      print(m_str15);       //输出:  双引号'' ''双引号
    
      String m_str16 = "双引号''*''双引号";
      print(m_str16);       //输出:  双引号''*''双引号
    

    字符串拼接方式,如下:

    //使用空格拼接,多个空格也是可以地
      String m_str1 = '单引号字符串' '拼接'     '---';
      print(m_str1);       //输出:单引号字符串拼接---
    
      //使用换行符和空格
      String m_str2 = '单引号字符串'
        '换行''加空格' '';
      print(m_str2);       //输出: 单引号字符串换行加空格
    
      //单双引号 空格拼接
      String m_str3 = "单双引号字符串加空格" '拼接'      "----";
      print(m_str3);      //输出: 双引号字符串加空格拼接----
    
      //单双引号 换行 空格
      String m_str4 = "单双引号字符串"
        '换行' '加空格' '***';
      print(m_str4);      //输出: 单双引号字符串换行加空格***
    
      //使用三个单引号创建多行字符串
      String m_str5 = '''
         三个单引号+
         拼接
         ''';
      print(m_str5);      /*输出    三个单引号+
                                    拼接
                                    */
    
      //使用三个双引号创建多行字符串
      String m_str6 = """
        三个双引号+
        拼接
        """;
      print(m_str6);      /*输出    三个双引号+
                                    拼接
                                    */
    
      String m_str7 = "正常拼接"+",用加号了来拼接";
      print(m_str7);      //输出: 正常拼接,用加号了来拼接
    

    通过提供一个r前缀可以创建"原始raw"字符串,在字符串加字符,或者在\前面再加一个\,可以避免\的转义作用,如下:

      String m_str1 = r"sdsdsds";
      print(m_str1);  //输出  sdsdsds
    
      print("换行:\n"); //输出:换行
    
      print(r"换行:\n"); //输出:换行:\n
    
      print("换行:\\n"); //输出:换行:\n
    

    ${表达式的使用},类似JS中ES6的表达式使用,使用$可以获取字符串的内容,用${表达式}可以将表达式的值放入字符串中,使用${表达式}也可以使用字符串拼接。下面也是直接上例子:

    bool flag = true;
      String m_str1 = "字符串";
      print("看看这个值:${m_str1} ""看看这个值flag:${flag}"); //输出:字符串:字符串 看看这个值flag:true
    
      //使用$+字符串
      String name = "knight";
      print("$name" + "CTO");     //输出:knightCTO;
    
      //使用字符串拼接,运用了String类中的toUpperCase函数,把字母变成大写 
      String m_str = "Android";
      assert('${m_str.toUpperCase()} is very good' ==       
            'ANDROID is very good');
      
    

    ==操作符判断两个对象的内容是否一样,如果了;两个字符串包含一样的字符编码序列,则他们是相等的。在生产模式 assert() 语句被忽略了。在检查模式assert(condition) 会执行,如果条件不为 true 则会抛出一个异常。

    10.Boolean(布尔值)

    Dart中以bool代表布尔值,只有两个对象是布尔类型的,那就是truefalse所创建的对象,这两个对象都是编译时常量。当Dart需要一个布尔值,只有true对象才被认为是true,所有其他值都是false。看下下面的例子:

      String name ="knight";
      //报错 因为name不是bool类型
      if(name){
        print(name);
    
      }
    

    上面代码会抛出异常,提示name不是布尔值,Dart使用的是先式的检查值,如下图:

      // 检查是否为空字符串
      var fullName = '';
      assert(fullName.isEmpty);
    
      // 检查是否小于等于0
      var hitPoints = 0;
      assert(hitPoints <= 0);
    
      // 检查是否为 null.
      var unicorn;
      assert(unicorn == null);
    
      // 检查是否为 NaN.
      var iMeantToDoThis = 0 / 0;
      assert(iMeantToDoThis.isNaN);
    

    assert是语言内置的断言的函数,仅在检查模式有效,在开发过程中,除非条件为真,否则会引发异常。(断言失败则程序立刻终止)

    11.Lists列表

    array是编程语言中最常见的集合类型,我相信身为开发者,都用过。在Dart中数组就是List对象,所以一般称为lists,下面直接上Dart list的示例:

    //创建一个int类型的list 并赋值为0,1,2,3,4
      List list =  [0,1,2,3,4];
    
      //使用构建的方式创建list
      List list1 = new List();
    
      //创建一个常量的List,不可以改变的List
      List list2 = const[0,1,2,3];
    
      //增加泛型
      List list3 = new List<String>();
    
      //创建固定的长度的数组列表,不能移除或者增加
      List list4 = new List(5);
    
      //创建包含所有以下元素的可改变的长度列表
      List list5 = new List.from([0,1,2,3]);
    
      //创建在固定范围内改变长度的列表
      List list6 = new List()..length = 10;
    
      //创建包含所有元素的固定长度列表
      List list7 = new List.unmodifiable([0,1,2]);
    
      //用生成器给所有元素赋初始值
      List list8 = new List<int>.generate(5, (int i){
        return i + i;
    
      });
    
    

    List常用的一些api:

     //在列表中存放不同类型的对象
      List list = [1,2,3,false,"Kinght"];
      print(list);          //输出:[1, 2, 3, false, Kinght]
    
      //在列表中添加元素
      list.add(7);
      print(list);          //输出:[1, 2, 3, false, Kinght, 7]
    
      //修改列表下标为1的值
      list[1] = "paul";
      print(list);          //输出:[1, paul, 3, false, Kinght, 7]
    
      //移除列表的指定值得的元素
      list.remove("paul");
      print(list);          //输出:[1, 3, false, Kinght, 7]
    
      //移除列表指定下标下的元素
      list.removeAt(0);
      print(list);          //输出:[3, false, Kinght, 7]
    
      //获取列表的长度
      print(list.length);   //输出:4
    
      //向列表中的指定位置添加元素 在第0的位置上插入Android
      list.insert(0, "Android");
      print(list);          //输出:[Android, 3, false, Kinght, 7]
    
      //判断数组中是否有某元素
      print(list.indexOf("Android")); //这里存在,输出对应的下标,如果没有则输出-1
    
      //排序
      List list1 = [3,1,2,6,7];
      // 根据语法提示: List.sort([(int, int) → int compare]) → void
      list1.sort((a,b) => a.compareTo(b));
      print(list1);           //输出:[1, 2, 3, 6, 7]
    

    简单总结:

    1. 下标索引从0开始。
    2. 可以直接打印List的元素,List也是一个对象。

    12.Maps

    通常来讲,Map是一个键值对相关的对象,键和值可以是任何类型的对象。每个键只出现一次,而一个值则可以出现多次。上面直接上Map集合的创建方式:

      //1.通过构建器来创建Map
      Map map1 = new Map();
      //添加值 赋值
      map1["one"] = 'Android';
      map1["two"] = 'IOS';
      map1["three"] = 'Flutter';
      print(map1);              //输出:{one: Android, two: IOS, three: Flutter}
    
      //2.通过复制的形式
      Map map2 = Map.of(map1);
      print(map2);              //输出:{one: Android, two: IOS, three: Flutter}
    
      //3.跟上面形式一样  Object.fromEntries() 函数传入一个键值对的列表,并返回一个带有这些键值对的新对象。
      // 这个迭代参数应该是一个能够实现@iterator方法的的对象,返回一个迭代器对象。它
      // 生成一个具有两个元素的类似数组的对象,第一个元素是将用作属性键的值,第二个元素是与该属性键关联的值。
      Map map3 = Map.fromEntries(map1.entries);
      print(map3);
    
      //4.直接声明,直接赋值key为String类型的map
      Map map4 = {'one':'Android',
        'two':'IOS',
        'three':'Flutter'};
      print(map4);              //输出:{one: Android, two: IOS, three: Flutter}
    
      //5.创建一个空的Map
      Map map5 = Map.identity();
      print(map5);              //输出:{}
    
    
      //6.创建不可变的Map
      Map map6 = const {'one':'Android','two':'IOS','three':'flutter'};
      print(map6);              //输出:{one: Android, two: IOS, three: flutter}
    
      //7.在目标的map6创建(复制)新的不可修改map7
      Map map7 = Map.unmodifiable(map6);
      print(map7);              //输出:{one: Android, two: IOS, three: flutter}
    
      //8.创建key为int值得map
      Map map8 = {1:'Android',
        2:'IOS',
        3:'Flutter'};
      print(map8);              //输出:{1: Android, 2: IOS, 3: Flutter}
    
      //9.根据list所提供的key value来创建map
      List<String> keys = ['one','two'];
      List<String> values = ['Android','IOS'];
      Map map9 = Map.fromIterables(keys, values);
      print(map9);               //输出:{one: Android, two: IOS}
      
       //通过构建器来创建Map
       Map map10 = new Map();
       //添加值 赋值 赋值不同类型的Map
       map10["one"] = 'Android';
       map10["two"] = 'IOS';
       map10["three"] = 'Flutter';
       map10[4] = 'RN';
       print(map10);              //输出:{one: Android, two: IOS, three: Flutter, 4: RN}
    

    Map常用的一些api:

     //创建Map key是int类型,value是String类型
       var  map1 = new Map<int,String>();
    
       //对Map第一个位置赋值,中括号是key
       map1[0] = 'Android';
       //对Map第二个位置赋值
       map1[1] = 'IOS';
       //对Map第三个值赋值
       map1[2] = 'flutter';
       //对Map赋空值
       map1[3] = null;
       //因为Map中的键值是唯一的,当第二次输入的key如果存在,Value会覆盖之前
       map1[2] = 'RN';
       print(map1);                //{0: Android, 1: IOS, 2: RN, 3: null}
    
       //获取Map的长度
       print(map1.length);         //输出:4
    
       //判断Map是否为空
       print(map1.isNotEmpty);     //输出结果:true
    
       //判断Map是否不为空
       print(map1.isEmpty);        //输出结果:false
    
       //检索Map是否含有某个Key
       print(map1.containsKey(1)); //输出:true
    
       //检索Map是否包含某个Value
       print(map1.containsValue('Android'));  //输出:true
    
       //删除某个键值对
       map1.remove(0);
       print(map1);                //输出:{1: IOS, 2: RN, 3: null}
    
       //获取所有的key
       print(map1.keys);           //输出:(1, 2, 3)
    
       //获取所有的values
       print(map1.values);         //输出:(IOS, RN, null)
    
       //循环打印
       /*
         key:1, value:IOS
         key:2, value:RN
         key:3, value:null
    
        */
         map1.forEach((key,value) {
         print("key:${key}, value:${value}");
       });
    
    

    简单总结:

    1. 当Map的Key没有指定类型时,Key类型不一致也不会报错。
    2. Map里面的key不能相同。但是value可以相同,value可以为空字符串或者为null。
    3. 创建Map有两种方式:通过构造器(new)和直接赋值。

    13.Runes

    在Dart中,runes代表字符串的UTF-32 code points。Unicode为每一个字符、标点符号、表情符号等都定义了一个唯一的数值。什么意思呢?也就是在书写系统中,每一个字母,数字都是有唯一的数值。由于在Dart字符串是UTF-16 code units字符序列,所以在字符串表达32-bit Unicode值就需要新的语法,通常用\uXXXX的方式表示Unicode code point,这里的XXXX是4个16进制的数。如:心形符号?是\u2665。对于非4个数值的情况,把编号放到大括号即可。例如,笑脸(?)是\u{1f600}String类有一些属性可以提取rune信息。codeUnitAtcodeUnit属性返回16-bit code units。使用runes属性来获取字符串的runes信息。下面示例演示了runes16-bit code units、和32-bit code points之间的关系:

    //执行程序入口
    void main(){
    
      var clapp = '\u{1f44f}';
      print(clapp);                  //输出:?
    
      print(clapp.codeUnits);        //输出: [55357, 56399]
      print(clapp.runes.toList());   //输出:  [128079]
    
      //使用String. fromCharCodes方法显示字符图形
      Runes input = new Runes(
          '\u2665  \u{1f605}  \u{1f60e}  \u{1f47b}  \u{1f596}  \u{1f44d}');
      print(new String.fromCharCodes(input));   //输出:♥  ?  ?  ?  ?  ?
    
    }
    

    14.Symbols

    一个Symbolobject代表Dart程序声明的操作符或者标识符。你也许从来不会用到Symbol,但是该功能对于通过名字来引用标识符的情况是非常有价值的,特别是混淆后的代码,标识符的名字被混淆了,但是Symbol的名字不会改变,下面列下例子:

      //使用 Symbol 字面量来获取标识符的 symbol 对象,也就是在标识符 前面添加一个 # 符号:
      //#radix
      //#bar
    
      print(#n == new Symbol('n'));  //输出:true
    
      var name = 'knight';
      Symbol symbol = #name;
      print(symbol);                //输出:Symbol("name")
      print(#name);                 //输出:Symbol("name")
    

    15.function方法

    Dart是一个真正的面向对象语言,方法也是对象并且具有一种类型,Function。这意味着,方法可以赋值给变量,也可以当做其他方法的参数。也可以把Dart类的实例当做方法来调用,下面是定义方法的示例:

    //定义一个方法 判断列表对应下标是否为null
    bool isNoble(int atomicNumber) {
      return list[atomicNumber] != null;
    }
    

    虽然在Effective Dart推荐在公开的APIs上使用静态类型,当然也可以选择忽略类型定义:

    //定义一个方法 判断列表对应下标是否为null 忽略类型定义
    isNoble(int atomicNumber) {
      return list[atomicNumber] != null;
    }
    

    对于只有一个表达式的方法,可以选择使用缩写语法来定义:

    //定义一个方法 判断列表对应下标是否为null 缩写写法
    bool isNoble(int atomicNumber) => list[atomicNumber] != null;
    

    => expr是语法{return expr;}形式的缩写。=>形式也称为胖箭头语法。注意:在箭头(=>)和冒号(;)之间只能使用一个表达式,不能使用语句。方法可以有两种类型的参数:必需和可选的。必需的参数在参数列表前面,后面是可选参数。

    15.1.Optional parameters(可选参数)

    什么是可选参数么?定义一个函数时,形参可以定义为可选参数,当调用这个方法时,可以不传这个可选参数。可选参数可以是命名参数或者基于位置的参数,但是这两种参数不能同时当做可选参数

    15.1.1.Optional named parameters(可选命名参数)

    调用方法的时候,可以使用这种形式paramName:value来指定命名参数。例如:

    enableFlags(bold: true, hidden: false);
    

    定义方法时,使用{param1,param2,...}的形式来指定可选命名参数:

    enableFlags({bool bold, bool hidden}) {
      // ...
    }
    
    15.1.2.Optional positional parameters(可选位置参数)

    把一些方法的参数放到[]中就变成可选位置参数了,例子如下:

    //定义一个方法 [String device]是可选位置参数 也就是调用这个方法可以不传这个参数
    String say(String from, String msg, [String device]) {
      var result = '$from says $msg';
      if (device != null) {
        result = '$result with a $device';
      }
      return result;
    }
    
    //不使用可选参数调用方法
    assert(say('Bob', 'Howdy') == 'Bob says Howdy');
    
    //使用可选参数调用方法
    assert(say('Bob', 'Howdy', 'smoke signal') ==
    'Bob says Howdy with a smoke signal');
    
    15.1.3.Default parameter value(默认参数值)

    在定义方法的时候,可以使用=来定义可选参数的默认值。默认值只能是编译时常量。如果没有提供默认值,则默认值为null。下面是设置可选参数默认值的示例:

    //定义一个返回类型为空的方法 方法中hidden默认值为false
    void enableFlags({bool bold = false,bool hidden = false}){
    
    }
    
    //调用方法 没有传hidden的值,那默认值就是false
    enableFlags(bold:true);
    

    下面的示例显示了如何设置位置参数的默认值:

    //定义一个方法 这个方法位置可选位置参数device的默认参数是carrier pigon
    //也就是当调用这个方法,没有传这个参数时,这个参数会取默认值
    String say(String from, String msg,
        [String device = 'carrier pigeon', String mood]) {
      var result = '$from says $msg';
      if (device != null) {
        result = '$result with a $device';
      }
      if (mood != null) {
        result = '$result (in a $mood mood)';
      }
      return result;
    }
    
    //调用上面的方法
    assert(say('Bob', 'Howdy') ==
    'Bob says Howdy with a carrier pigeon');
    

    还可以使用list或者map作为默认值,下面的示例定义一个方法doStuff(),并分别为listgifts参数指定默认值:

    //执行程序入口
    void main(){
    
      //调用判断对应小标的值是否为空
     // print(isNoble(1));    //输出:true
    
    
      doStuff();    //输出:list:  [1, 2, 3]
                    //输出:gifts: {first: paper, second: cotton, third: leather}
    
    
    }
    
    //List和Map都取了默认值
    void doStuff(
        {List<int> list = const [1, 2, 3],
          Map<String, String> gifts = const {
            'first': 'paper',
            'second': 'cotton',
            'third': 'leather'
          }}) {
      print('list:  $list');
      print('gifts: $gifts');
    }
    

    15.2.The main() function(入口函数)

    每个应用都需要有个顶级的main()入口方法才能执行。main()方法的返回值为void并且有个可选的List<String>参数,下面是一个web应用的main()方法:

    void main() {
      querySelector("#sample_text_id")
        ..text = "Click me!"
        ..onClick.listen(reverseText);
    }
    

    在上面代码中..y语法是级联调用。使用级联调用,可以在一个对象上执行多个操作。下面是一个命令行应用的main()方法,并且使用方法参数作为输入参数:

    // Run the app like this: dart args.dart 1 test
    void main(List<String> arguments) {
      print(arguments);
    
      assert(arguments.length == 2);
      assert(int.parse(arguments[0]) == 1);
      assert(arguments[1] == 'test');
    }
    

    15.3.Functions as first-class objects(一等方法对象)

    可以把方法当做参数调用另外一个方法。如:

    printElement(element) {
      print(element);
    }
    
    var list = [1, 2, 3];
    
    // 遍历集合
    list.forEach(printElement);
    

    方法也可以赋值给一个变量:

    var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
    assert(loudify('hello') == '!!! HELLO !!!');
    

    15.4.Anonymous functions(匿名方法)

    大部分方法都带有名字,例如main()或者printElement()。也可以创建没有名字的方法,称为匿名方法,有时候也被称为lambda或者clourse闭包。可以把匿名方法赋值给一个变量,然后可以使用这个方法,比如添加集合或者从集合中删除。匿名函数和命名函数类似,在括号之间可以定义一些参数·,参数使用逗号分割,也可以是可选参数。后面大括号中的代码为函数体:

    ([[Type] param1[,]]) { 
      codeBlock; 
    }; 
    

    下面的代码定义了一个参数为i的匿名函数。list中的每个元素都会调用这个函数来打印出来,同时来计算了每个元素在list中的索引位置。

    var list = ['apples', 'oranges', 'grapes', 'bananas', 'plums'];
    list.forEach((i) {
      print(list.indexOf(i).toString() + ': ' + i);
    });
    //输出:
    
    0: apples
    1: oranges
    2: grapes
    3: bananas
    4: plums
    

    上面说到如果方法只包含一个语句,可以使用胖箭头语法缩写,把上面的代码改成下面同样的意思:

    list.forEach((i) => print(list.indexOf(i).toString() + ': ' + i));
    

    15.5.静态作用域

    Dart是静态作用域语言,变量的作用域在写代码的时候就缺的过了。基本上大括号里面定义的变量就只能在大括号里面访问,和java作用域类似。

    var topLevel = true;
    
    main() {
      var insideMain = true;
    
      myFunction() {
        var insideFunction = true;
    
        nestedFunction() {
          var insideNestedFunction = true;
    
          assert(topLevel);
          assert(insideMain);
          assert(insideFunction);
          assert(insideNestedFunction);
        }
      }
    }
    

    注意nestedFunction可以访问所有的变量,包含顶级变量。

    15.6.Lexical closures(词法闭包)

    一个闭包是一个方法对象,不管该对象在何处被调用,该对象都可以访问其作用域内的变量,方法可以封闭定义到其作用域内的变量。方法可以封闭定义到其作用域内的变量。下面示例中,makeAdder()捕获到了变量addBy。不管在哪里执行makeAdder()所返回的函数,都可以使用addBy参数:

    Function makeAdder(num addBy) {
      return (num i) => addBy + i;
    }
    
    main() {
      // Create a function that adds 2.
      var add2 = makeAdder(2);
    
      // Create a function that adds 4.
      var add4 = makeAdder(4);
    
      assert(add2(3) == 5);
      assert(add4(3) == 7);
    }
    

    15.7.Testing functions for equality(测试函数是否相等)

    下面是测试顶级方法、静态函数和实例函数相等的示例:

    foo() {}               // 顶级方法
    
    class A {
      static void bar() {} // 静态方法
      void baz() {}        // 实例方法
    }
    
    void main() {
      var x;
    
      // 比较顶级函数
      x = foo;
      print(foo == x);    //输出:true
    
      // 比较静态方法
      x = A.bar;
      print(A.bar == x);  //输出:true
    
      // 比较实例方法
      var v = new A(); // 实例1
      var w = new A(); // 实例2
      var y = w;
      x = w.baz;
    
      //这些闭包引用相同的实例
      //所有相等
      print(y.baz == x);   //输出:true
    
      // 闭包引用不同的实例,所以不相等`
      print(v.baz != w.baz);   //输出:true
    }
    

    15.8.Return values (返回值)

    所有的函数都返回一个值。如果没有指定返回值,则默认把语句return null;作为函数的最后☝一个语句执行。

    16.Operators 操作符

    操作符
    上面列了操作符和一些例子,在操作符表格所列的操作符都是按照优先级顺序从左到右,从上倒下的方式来排列,上面和左边的操作符优先级要高于下面和右边的。例如%操作符优先高于==,而等号高于&&,下面的代码结果是一样的:

    void main() {
       var n = 2;
       var i = 2;
       var d = 7;
       if((n % i == 0) && (d % i == 0)){
         print('符合条件');
       }else{
         print('不符合条件');       //进入这里
       }
    
       if(n % i == 0 && d % i == 0){
         print('符合条件');
       }else{
         print('不符合条件');       //进入这里
       }
    }
    

    16.1.算术操作符

    算术操作符

    16.2.相等相关的操作符

    相等相关的操作符

    16.3.类型判定操作符

    类型判定操作符
    只有obj实现了T的接口,obj is T才是true。例如obj is Object总是true。使用as操作符吧对象转换为特定的类型。一般情况下,可以把它当做is判定类型然后调用所判定对象的函数缩写形式,如下面的示例:

    if (emp is Person) { // Type check
      emp.firstName = 'Bob';
    }
    

    使用as操作符可以简化上面的代码:

    (emp as Person).firstName = 'Bob';
    

    注意:上面的这两个代码效果是有区别的。如果emp是null或者不是person类型,则第一个示例使用is则不会执行条件里面的代码,而第二个情况使用as则会抛出一个异常。

    16.4.赋值操作符

    使用=操作符赋值。但是还有一个??=操作符来指定值为null的变量值。

    a = value;   // 给 a 变量赋值
    b ??= value; // 如果 b 是 null,则赋值给 b;
                 // 如果不是 null,则 b 的值保持不变
    

    还有复合赋值符+=等可以赋值:

    复合赋值操作符
    下面的代码使用赋值操作符符合复合赋值操作符:

    var a = 2;           // Assign using =
    a *= 3;              // Assign and multiply: a = a * 3
    assert(a == 6);
    

    下面是复合赋值操作符工作原理解释:
    复合赋值操作符解释

    16.5.逻辑操作符

    逻辑操作符

    16.6.位和移位操作符

    在Dart中可以单独操作数字的某一位,下面操作符同样应用于整数:

    位与移位操作符

    16.7.条件表达式

    Dart有两个特殊的操作符可以用来替代if-else语句:
    condition ? expr1 : expr2
    如果condition是true,执行expr1(并返回执行的结果);否则执行expr2并返回结果。
    expr1 ?? expr2如果expr1是non-null,返回其值;否则执行expr2并返回其结果。如果是基于布尔表达式的值来赋值,考虑使用?:

    var finalStatus = m.isFinal ? 'final' : 'not final';
    

    如果是基于布尔表达式是测试值是否为null,考虑使用??

    String toString() => msg ?? super.toString();
    
    //上面代码可以用下面代码来表示,意思效果是一样的,代码易懂但是不简洁
    String toString() => msg == null ? super.toString() : msg;
    
    String toString() {
      if (msg == null) {
        return super.toString();
      } else {
        return msg;
      }
    }
    

    16.8.级联操作符

    级联操作符(…)可以在同一对象上连续调用多个函数以及访问成员变量。使用级联操作符可以避免创建临时变量,并且写出来的代码看起来更加流畅,如:

    querySelector('#button') // Get an object.
      ..text = 'Confirm'   // Use its members.
      ..classes.add('important')
      ..onClick.listen((e) => window.alert('Confirmed!'));
    

    第一个方法quertSelector返回一个selector对象。后面的级联操作符都是调用这个对象的成员,并忽略每个操作所返回的值。上面代码和下面的代码功能一样:

    var button = querySelector('#button');
    button.text = 'Confirm';
    button.classes.add('important');
    button.onClick.listen((e) => window.alert('Confirmed!'));
    

    级联调用也可以嵌套:

    final addressBook = (new AddressBookBuilder()
          ..name = 'jenny'
          ..email = 'jenny@example.com'
          ..phone = (new PhoneNumberBuilder()
                ..number = '415-555-0100'
                ..label = 'home')
              .build())
        .build();
    

    在方法上使用级联操作符需要非常小心,例如下面代码是不合法的:

    var sb = new StringBuffer();
    sb.write('foo')..write('bar');
    

    sb.write函数返回一个void,无法再void上使用级联操作符。注意级联语法不是操作符,只是语法!

    17.Control flow statements(流程控制语句)

    Dart中的控制流程语句和java语言很像,可以说是差不多的:

    17.1.if else

    if (isRaining()) {//条件语句
      you.bringRainCoat();//内容体
    } else if (isSnowing()) {//条件语句
      you.wearJacket();//内容体
    } else {
      car.putTopDown();//内容体
    }
    

    17.2.for循环

    可以使用标准的for循环:

    var message = new StringBuffer("Dart is fun");
    for (var i = 0; i < 5; i++) {
      message.write('!');
    }
    
    //使用foreach循环 list 和 Set都可以用这种方式
    List numbers = [1,2,3,4,5,6,7,8,910];
    numbers.foreach((number)=> print(number));
    
    //使用for in循环,一般List和Set都是用这种方式
    List numbers = [1,2,3,4,5,6,7,8,910];
    for(var number in numbers){
         print(number);
    }
    

    17.3.While and do-while

    while循环在执行循环之前先判断条件是否满足:

    //判断条件
    while (!isDone()) {
      //内容
      doSomething();
    }
    
    //例子
    var i = 0;
    while(i > 5){
        i++;
    }
    

    do-while循环是先执行循环代码再判断条件:

    do {
      printLine();//内容体
    } while (!atEndOfPage());//条件判断
    //例子
    var i = 0;
    do{
        i++;
    }while(i > 7);
    

    17.4.Break and continue

    使用break来终止循环:

    while (true) {
      if (shutDownRequested()) break;
      processIncomingRequests();
    }
    

    使用continue来开始下一次循环:

    for (int i = 0; i < candidates.length; i++) {
      var candidate = candidates[i];
      if (candidate.yearsExperience < 5) {
        continue;
      }
      candidate.interview();
    }
    

    上面代码在实现Iterable接口对象(List和Map)可以使用下面写法:

    candidates.where((c) => c.yearsExperience >= 5)
              .forEach((c) => c.interview());
    

    17.5.Switch and case

    Dart中的Switch语句使用==比较integer、String、或者编译时常量。比较的兑现必须都是同一个类的实例(并且不是其之类),calss必须没有覆写==操作符,每个非空的case语句都必须有一个break语句。另外还可以通过continuethrowreturn来介绍非空case语句。当没有case语句匹配时,可以使用default语句来匹配这种默认情况。

    var command = 'OPEN';
    switch (command) {
      case 'CLOSED':
        executeClosed();
        break;
      case 'PENDING':
        executePending();
        break;
      case 'APPROVED':
        executeApproved();
        break;
      case 'DENIED':
        executeDenied();
        break;
      case 'OPEN':
        executeOpen();
        break;
      default:
        executeUnknown();
    }
    

    下面的示例代码再case省略了break语句,编译的时候会出现一个错误:

    void main() {
    
      var number = 1;
      var i = 0;
      switch(number){
        case 1;
        i++;
        case 2:
          i--;
          break;
    
      }
    
    }
    

    在Dart中的空case语句中可以不要break语句:

    void main() {
    
      var number = 1;
      var i = 0;
      switch(number){
        case 1;
        i++;
        case 2:
          i--;
          break;
    
      }
    
    }
    

    如果需要实现这种继续到下一个case语句中继续执行,则可以使用continue语句跳转对应的标签处继续执行:

    void main() {
    
      var number = 1;
      var i = 0;
      switch(number){
        case 1;
        i++;
        case 2:
          i--;
          break;
    
      }
    
    }
    

    每个case语句可以有局部变量,局部变量只有在这个语句内可见。

    17.6.Assert(断言)

    如果条件表达式结果不满足需要,则可以使用assert语句俩打断代码的执行。示例如下:

    // 确保变量是非空值 
    assert(text != null);
    // 确保值是小于100
    assert(number < 100);
    // 确保这是一个 https 地址
    assert(urlString.startsWith('https'));
    

    assert上面也说过了,assert方法参数可以为任何布尔值的表达式或者方法。如果返回的值为true,断言执行通过,执行结束。如果返回值为false,断言执行失败,会抛出异常,断言只有在检查模式运行有效,如果生产模式运行,则断言不会执行。

    18.Exceptions(异常)

    代码中可以出现异常和捕获异常。异常表示一些未知的错误情况。如果异常没有捕获,则异常会抛出,导致抛出异常的代码终止执行。和java不同的是,所有的Dart异常是非检查异常。方法不一定声明·来看他们所抛出的异常,并且你不要求捕获异常,Dart提供了ExceptionError类型,以及一些子类型。还可以自己定义异常类型。但是,Dart代码可以抛出任何非null对象为异常,不仅仅是实现了ExceptionError对象。

    18.1.Throw

    下面是抛出或者扔出一个异常的示例:

    thow new FormatException('Expected at least 1 section');
    

    还可以抛出任意对象:

    throw 'Out of llamas!';
    

    由于抛出异常时一个表达式,所以可以在=>语句中使用,也可以在其他能使用表达式的地方抛出异常。

    distanceTo(Point other) =>
        throw new UnimplementedError();
    

    18.2.Catch

    捕获异常可以避免异常继续传递(重新抛出rethrow)异常除外。捕获异常给你一个处理该异常的机会:

    try {
      breedMoreLlamas();
    } on OutOfLlamasException {
      buyMoreLlamas();
    }
    

    对于可以抛出多种类型异常的代码,你可以指定多个捕获语句。每个语句分别对应一个异常类型,如果捕获语句没有指定异常类型,则该可以捕获任何异常类型:

    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');
    }
    

    如之前代码所示,可以使用on或者catch来声明捕获语句,也可以同时使用。使用on来指定异常类型,使用catch来捕获异常对象。函数catch()可以带有一个或者两个参数,第一个参数为抛出的异常对象,第二个为堆栈信息。

      ...
    } on Exception catch (e) {
      print('Exception details:\n $e');
    } catch (e, s) {
      print('Exception details:\n $e');
      print('Stack trace:\n $s');
    }
    

    使用rethrow关键字可以把捕获的异常重新抛出:

    final foo = '';
    
    void misbehave() {
      try {
        foo = "You can't change a final variable's value.";
      } catch (e) {
        print('misbehave() partially handled ${e.runtimeType}.');
        rethrow; // Allow callers to see the exception.
      }
    }
    
    void main() {
      try {
        misbehave();
      } catch (e) {
        print('main() finished handling ${e.runtimeType}.');
      }
    }
    

    18.3.Finally

    无论是否抛出异常,要确保某些代码都要执行,可以使用finally语句来实现。如果没有catch语句来捕获异常,则在执行完finally语句后,异常被抛出了:

    try {
      breedMoreLlamas();
    } finally {
      // 即使抛出异常也会执行
      cleanLlamaStalls();
    }
    

    定义的finally语句在任何匹配的catch语句之后执行:

    try {
      breedMoreLlamas();
    } catch(e) {
      print('Error: $e');  // 优先处理异常
    } finally {
      cleanLlamaStalls();  // 然后再执行
    }
    

    19.Classes

    Dart是一个面向对象编程语言,同时支持基于mixin的继承机制。每个对象都是一个类的实例,所有的类都继承于object。基于Mixin的继承意味着每个类(Object除外)都只有一个超类,一个类的代码可以在其他多个类继承中重复使用·。使用new关键字和构造函数来创建新的对象。构造函数名字可以为ClassName或者ClassName.identifier。例如:

    var jsonData = JSON.decode('{"x":1, "y":2}');
    
    // 创建Point类型的对象
    var p1 = new Point(2, 2);
    
    // 根据json来创建Point对象
    var p2 = new Point.fromJson(jsonData);
    

    对象的成员包括方法和数据(函数和示例变量)。当你调用一个函数的时候,你是在一个对象上调用:函数需要访问对象的方法和数据,使用(.)来引用对象的变量或者方法:

    var p = new Point(2, 2);
    
    // 对实例对象的变量y赋值
    p.y = 3;
    
    // 从成员变量得到值
    assert(p.y == 3);
    
    // 从p复制实例对象
    num distance = p.distanceTo(new Point(4, 4));
    

    使用?.来替代,可以避免当左边对象为null时候抛出异常:

    //如果P不是空对象,那么对其变量y赋值
    p?.y = 4;
    

    有些类提供了常量构造函数。使用常量构造函数可以创建编译时常量,要使用常量构造函数只需要用const替代new即可:

    var p = const ImmutablePoint(2, 2);
    

    两个一样的编译时常量其实是同一个对象:

    var a = const ImmutablePoint(1, 1);
    var b = const ImmutablePoint(1, 1);
    
    print(identical(a, b)); // 它们是相同的实例,输出true
    

    可以使用Object的runtimeType属性来判断实例的类型,该属性返回一个Type对象。

    print('The type of a is ${a.runtimeType}');
    

    19.1.Instance variables

    下面是如何定义实例变量的示例:

    
    class Point{
      num x;//声明实例变量x,初始化为空
      num y;//声明实例变量y,舒适化为空
      num z = 0;//声明实例变量z,初始化为0
    }
    

    所有没有初始化时变量值都是null。每个实例变量都会自动生成一个getter方法。Non-final实例变量还会自定生成一个setter方法:

    class Point{
      num x;//声明实例变量x,初始化为空
      num y;//声明实例变量y,舒适化为空
      num z = 0;//声明实例变量z,初始化为0
    }
    void main() {
      var point = new Point();
      point.x = 4;              //使用setter方法对x变量赋值
      print(point.x == 4);      //输出true 使用getter获取x变量的值
      print(point.y == null);   //输出true
    
    }
    

    如果在实例变量定义的时候初始化该变量(不是在构造函数或者其他方法中初始化),改值是在实例对象的时候初始化的,也就是在构造函数和初始化参数列表执行之前。

    19.2.Constructors

    定义一个和类名字一样的方法就定义一个构造函数还可以带有其他可选的标识符。常见的构造函数生一个对象的新实例:

    class Point {
      num x;
      num y;
    
      Point(num x, num y) {
        this.x = x;
        this.y = y;
      }
    }
    

    this关键字指当前的实例。只有当名字冲突的时候才使用this。由于构造函数参数赋值给实例变量的场景太常见了,Dart提供一个语法糖来简化这个操作:

    class Point {
      num x;
      num y;
    
      // 设置x和y的语法糖
      // 在构造函数主体运行之前
      Point(this.x, this.y);
    }
    
    19.2.1.Default copnstructors(默认构造函数)

    如果没有定义构造函数,则会有个默认构造函数。默认构造函数没有参数,并且会调用超类的没有参数的构造函数

    19.2.2.Constructors aren’t inherited(构造函数不会继承)

    子类不会继承超类的构造函数。子类如果没有定义构造函数,则只有一个默认构造函数。

    19.2.3.Named constructors(命名构造函数)

    使用命名构造函数可以为一个类实现多个构造函数,或者使用命名构造函数来更清晰自己的意图:

    class Point {
      num x;
      num y;
    
      Point(this.x, this.y);
    
      // 命名构造函数
      Point.fromJson(Map json) {
        x = json['x'];
        y = json['y'];
      }
    }
    

    构造函数不能继承,所以超类的命名构造函数也不会被继承,如果子类也有超类一样命名构造函数,就必须在子类中自己实现该构造函数。

    19.2.4.Invoking a non-default superclass constructor(调用超类构造函数)

    默认情况下,子类的构造函数会自动调用超类的无名无参数的默认构造函数。超类的构造函数在子类构造函数体开始执行的位置调用。如果提供了一个 initializer list(初始化参数列表) ,则初始化参数列表在超类构造函数执行之前执行。 下面是构造函数执行顺序:

    1. initializer list(初始化参数列表)
    2. superclass’s no-arg constructor(超类的无名构造函数)
    3. main class’s no-arg constructor(主类的无名构造函数)

    如果超类没有无名无参构造函数,则需要手动去调用超类的其他构造函数。在构造函数参数后使用冒号:可以调用超类构造函数,下面中,Employee类的构造函数调用超类Person的命名构造函数:

    //定义Person类
    class Person {
      String firstName;
    
      Person.fromJson(Map data) {
        print('in Person');
      }
    }
    
    class Employee extends Person {
      // Person 没有默认构造函数
      // you must call super.fromJson(data).
      Employee.fromJson(Map data) : super.fromJson(data) {
        print('in Employee');
      }
    }
    
    main() {
      var emp = new Employee.fromJson({});
    
      // 打印输出
      // in Person
      // in Employee
    
    }
    

    由于超类构造函数的参数在构造函数执行之前执行,所以擦拭可以是一个表达式或者一个方法调用:

    class Employee extends Person {
      // ...
      Employee() : super.fromJson(findDefaultData());
    }
    

    如果在构造函数的初始化列表中使用 super(),需要把它放到最后。调用超类构造函数的参数无法访问 this。 例如,参数可以为静态函数但是不能是实例函数。

    19.3.Initializer list(初始化列表)

    在构造函数体执行之前除了可以调用超类构造函数之外,还可以 初始化实例参数。 使用逗号分隔初始化表达式:

    class Point {
      num x;
      num y;
    
      Point(this.x, this.y);
    
      // 初始化列表在构造函数体运行之前设置实例变量。
      Point.fromJson(Map jsonMap)
          : x = jsonMap['x'],
            y = jsonMap['y'] {
        print('In Point.fromJson(): ($x, $y)');
      }
    }
    

    初始化表达式等号右边的部分不能访问 this。初始化列表非常适合用来设置 final 变量的值。 下面示例代码中初始化列表设置了三个 final 变量的值:

    import 'dart:math';
    
    export 'src/DartProject_base.dart';
    
    // TODO: Export any libraries intended for clients of this package.
    
    
    class Point {
     final num x;
     final num y;
     final num distanceFromOrigin;
    
     Point(x, y)
         : x = x,
           y = y,
           distanceFromOrigin = sqrt(x * x + y * y);
    }
    
    main() {
     var p = new Point(2, 3);
     print(p.distanceFromOrigin);//输出:3.605551275463989
    }
    

    19.4.Redirecting constructors(重定向构造函数)

    有时候一个构造函数会调动类中的其他构造函数。一个重定向构造函数是没有代码的,在构造函数声明后,使用冒号调用其他构造函数。

    class Point {
      num x;
      num y;
    
      // 主构造函数
      Point(this.x, this.y);
    
      // 调用主构造函数
      Point.alongXAxis(num x) : this(x, 0);
    }
    

    19.5.Constant constructors(常量构造函数)

    如果你的类提供一个状态不变的对象,你可以把这些对象定义为编译时常量。要实现这个功能,需要定义一个const构造函数, 并且声明所有类的变量为final

    class ImmutablePoint {
      final num x;
      final num y;
      const ImmutablePoint(this.x, this.y);
      static final ImmutablePoint origin =
          const ImmutablePoint(0, 0);
    }
    

    19.6.Factory constructors(工厂方法构造函数)

    如果一个构造函数并不总是返回一个新的对象,则使用factory来定义这个构造函数。例如,一个工厂构造函数可能从缓存中获取一个实例并返回,或者返回一个子类型的实例。例子:

    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);
        }
      }
    }
    

    使用new关键字来调用工厂构造函数

    var logger = new Logger('UI');
    logger.log('Button clicked');
    

    19.7.函数

    函数是类中定义的方法,是类对象的行为。

    19.7.1.Instance methods(实例函数)

    对象的实例函数可以访问this,下面例子中distanceTo函数就是实例函数:

    import 'dart:math';
    
    class Point {
      num x;
      num y;
      Point(this.x, this.y);
    
      num distanceTo(Point other) {
        var dx = x - other.x;
        var dy = y - other.y;
        return sqrt(dx * dx + dy * dy);
      }
    }
    

    Getterssetters是用来设置和访问对象属性的特殊函数。每个实例变量都隐含的具有一个getter, 如果变量不是final的则还有一个setter。可以通过实行gettersetter来创建新的属性, 使用getset关键字定义gettersetter

    class Rectangle {
      num left;
      num top;
      num width;
      num height;
    
      Rectangle(this.left, this.top, this.width, this.height);
    
      // 定义两个计算属性:右和下。
      num get right             => left + width;
          set right(num value)  => left = value - width;
      num get bottom            => top + height;
          set bottom(num value) => top = value - height;
    }
    
    main() {
      var rect = new Rectangle(3, 4, 20, 15);
      assert(rect.left == 3);
      rect.right = 12;
      assert(rect.left == -8);
    }
    

    gettersetter的好处是,可以开始使用实例变量,后来可以把实例变量用函数包裹起来,而调用代码的地方不需要修改。

    19.7.2.Abstract methods(抽象函数)

    实例函数、 getter、和setter函数可以为抽象函数,抽象函数是只定义函数接口但是没有实现的函数,由子类来实现该函数。如果用分号来替代函数体则这个函数就是抽象函数。

    abstract class Doer {
      // ...定义实例变量和方法...
    
      void doSomething(); // 定义一个抽象方法.
    }
    
    class EffectiveDoer extends Doer {
      void doSomething() {
        // ...提供实现,因此此处的方法不是抽象的...
      }
    }
    

    调用一个没实现的抽象函数会导致运行时异常。

    19.8.Overridable operators(可覆写的操作符)

    下表中的操作符可以被覆写。 例如,如果你定义了一个 Vector 类, 你可以定义一个 + 函数来实现两个向量相加。

    可覆写的操作符
    下面举了覆写+-操作符的示例:

    class Vector {
      final int x;
      final int y;
      const Vector(this.x, this.y);
    
      /// 覆写 + (a + b).
      Vector operator +(Vector v) {
        return new Vector(x + v.x, y + v.y);
      }
    
      /// 覆写 - (a - b).
      Vector operator -(Vector v) {
        return new Vector(x - v.x, y - v.y);
      }
    }
    
    main() {
      final v = new Vector(2, 3);
      final w = new Vector(2, 2);
    
      // v == (2, 3)
      assert(v.x == 2 && v.y == 3);
    
      // v + w == (4, 5)
      assert((v + w).x == 4 && (v + w).y == 5);
    
      // v - w == (0, 1)
      assert((v - w).x == 0 && (v - w).y == 1);
    }
    

    19.9.Abstract classer(抽象类)

    使用abstract修饰符定义一个抽象类,一个不能被实例化的类。抽象类通常用来定义接口,以及部分实现,如果抽象类是可实例化的,则定义一个工厂构造函数
    抽象类通常具有抽象函数,下面是定义具有抽象函数的抽象类:

    // 这个类是抽象类,不能实例化
    abstract class AbstractContainer {
      // ...定义构造函数, 变量, 方法...
    
      void updateChildren(); // 抽象方法.
    }
    

    下面的类不是抽象的,但是定义了一个抽象函数,这样的列是可以被实例化:

    class SpecializedContainer extends AbstractContainer {
      // ...定义更多的构造方法, 方法...
    
      void updateChildren() {
        // ...实现 updateChildren()...
      }
    
      //Abstract method causes a warning but doesn't prevent instantiation.
    抽象方法导致警告,但不阻止实例化。
      void doSomething();
    }
    

    19.10.Implicit interfaces(隐式接口)

    每个类都隐式的定义了一个包含所有实例成员的接口,并且这个类实现了这个接口。如果你想创建类A来支持类B的api,而不想继承 B 的实现, 则类A应该实现B的接口:

    // A person. 隐式接口包含greet().
    class Person {
      // 在接口中,但仅在此库中可见
      final _name;
    
      // 不在接口中,因为这是构造函数
      Person(this._name);
    
      // 在接口中
      String greet(who) => 'Hello, $who. I am $_name.';
    }
    
    // 实现Person 接口
    class Imposter implements Person {
      // 我们必须定义这个,但我们不使用它。
      final _name = "";
    
      String greet(who) => 'Hi $who. Do you know who I am?';
    }
    
    greetBob(Person person) => person.greet('bob');
    
    main() {
      print(greetBob(new Person('kathy')));
      print(greetBob(new Imposter()));
    }
    

    下面是实现多个接口的示例:

    class Point implements Comparable, Location {
      // ...
    }
    

    19.11.Extending a class(扩展类)

    使用extends定义子类,supper引用超类:

    class Television {
      void turnOn() {
        _illuminateDisplay();
        _activateIrSensor();
      }
      // ...
    }
    
    class SmartTelevision extends Television {
      void turnOn() {
        super.turnOn();
        _bootNetworkInterface();
        _initializeMemory();
        _upgradeApps();
      }
      // ...
    }
    

    子类可以覆写实例函数,gettersetter。下面是覆写Object类的noSuchMethod()函数的例子, 如果调用了对象上不存在的函数,则就会触发noSuchMethod()函 数。

    class A {
      //除非重写NoSuchMethod,否则使用不存在的函数将导致NoSuchMethodError。
      void noSuchMethod(Invocation mirror) {
        print('You tried to use a non-existent member:' +
              '${mirror.memberName}');
      }
    }
    

    还可以使用@override注解来表明函数是想覆写超类的一个函数:

    class A {
      @override
      void noSuchMethod(Invocation mirror) {
        // ...
      }
    }
    

    如果使用noSuchMethod函数来实现每个可能的getter、setter、以及其他类型的函数,可以使用@proxy注解来避免警告信息:

    @proxy
    class A {
      void noSuchMethod(Invocation mirror) {
        // ...
      }
    }
    

    如果要知道编译时的具体类型,可以实现这些类来避免警告,和使用@proxy效果一样:

    class A implements SomeClass, SomeOtherClass {
      void noSuchMethod(Invocation mirror) {
        // ...
      }
    }
    

    19.12.Enumerated types(枚举类型)

    枚举类型通常称为enumerations或者enums是一种特殊的类,用来表现一个固定数目的常量。使用enum关键字来定义枚举类型:

    enum Color {
      red,
      green,
      blue
    }
    

    枚举类型中的每个值都有一个indexgetter函数,该函数返回该值在枚举类型定义中的位置(从0开始),例如,第一个枚举值的位置为0,第二个为1:

    print(Color.red.index == 0); //输出:true
    print(Color.green.index == 1);//输出:true
    print(Color.blue.index == 2);//输出:true
    

    枚举values常量可以返回所有的枚举值

    List<Color> colors = Color.values;
    print(colors[2] == Color.blue);//输出:true
    

    可以在switch语句中使用枚举。如果在switch(e)中的e的类型为枚举类,如果没有处理所有该枚举类型的值的话,则会抛出一个警告:

    enum Color {
      red,
      green,
      blue
    }
    // ...
    Color aColor = Color.blue;
    switch (aColor) {
      case Color.red:
        print('Red as roses!');
        break;
      case Color.green:
        print('Green as grass!');
        break;
      default: // 如果没有这个,你会看到一个警告
        print(aColor);  // 'Color.blue'
    }
    

    枚举类型具有以下限制:

    1. 无法继承枚举类型,无法使用mixin、无法实现一个枚举类型
    2. 无法显示的初始化一个枚举类型

    19.13.Adding features to a class:mixins(为类添加新的功能)

    Mixins是一种多类继承中重用一个类代码的方法,使用with关键字后面为一个或者多个mixin名字来使用mixin,上例子如何使用mixin:

    class Musician extends Performer with Musical {
      // ...
    }
    
    class Maestro extends Person
        with Musical, Aggressive, Demented {
      Maestro(String maestroName) {
        name = maestroName;
        canConduct = true;
      }
    }
    

    定义一个类继承Object,该类没有构造函数,不能调用super,则该类就是一个mixin。下面例子:

    abstract class Musical {
      bool canPlayPiano = false;
      bool canCompose = false;
      bool canConduct = false;
    
      void entertainMe() {
        if (canPlayPiano) {
          print('Playing piano');
        } else if (canConduct) {
          print('Waving hands');
        } else {
          print('Humming to self');
        }
      }
    }
    

    从Dart1.13开始,Mixins可以继承其他类,不再限制为继承Object,Mixins可以调用super()

    19.14.Class variables and methods(类变量和函数)

    使用static关键字来实现级别的变量和函数。

    19.14.1.Static variables(静态变量)

    静态变量对于类别的状态是非常有用的:

    class Color {
      static const red =
          const Color('red'); // 静态构造变量.
      final String name;      // 静态实例变量.
      const Color(this.name); // 构造方法.
    }
    
    main() {
      print(Color.red.name == 'red'); //输出:true
    }
    

    静态变量在第一次使用的时候才被初始化。

    19.14.2.Static methods(静态函数)

    静态函数不再实例是执行,所以无法访问this:

    import 'dart:math';
    
    class Point {
      num x;
      num y;
      Point(this.x, this.y);
    
      static num distanceBetween(Point a, Point b) {
        var dx = a.x - b.x;
        var dy = a.y - b.y;
        return sqrt(dx * dx + dy * dy);
      }
    }
    
    main() {
      var a = new Point(2, 2);
      var b = new Point(4, 4);
      var distance = Point.distanceBetween(a, b);
      assert(distance < 2.9 && distance > 2.8);
    }
    

    对于通用的或者经常使用的静态函数,考虑使用顶级方法而不是静态函数,静态函数还可以当做编译时常量使用,如:把静态函数当做常量构造函数的参数来使用。

    20.泛型

    在查看List类型的API文档,可以看到实际的类型定义为List<E>。这个<..>声明list是一个泛型(或者参数化)类型。通常情况下,使用一个字母来代表类型参数,例如E,T,S,K和V等。

    20.1.Why use generics?(为何使用泛型)

    在Dart中类型是可选的,你可以选择不用泛型。有很多情况都要使用类型表明自己的意图,不管是使用泛型还是具体类型。如,如果自己希望List只包含字符串对象。则可以定义为List<String>代表(“list of string”)。这样开发工具或者自己的同事可以帮助检查自己的代码是否把非字符串类型对象给放到这个list中,如下面:

    main() {
       List Tech = new List<String>();
       Tech.addAll(['Android','IOS','Flutter']);
       Tech.add(42);//运行时报错
    }
    

    另外使用泛型的原因是减少重复代码。泛型可以在多种类型之间定义同一个实现,同时还可以继续使用检查模式和静态分析工具提供的代码分析功能。例如:创建一个保存缓存对象的接口:

    abstract class ObjectCache {
      Object getByKey(String key);
      setByKey(String key, Object value);
    }
    

    后来发现需要一个用来缓存字符串的实现,那又要定义一个接口:

    abstract class StringCache {
      String getByKey(String key);
      setByKey(String key, String value);
    }
    

    然而,又需要用一个用来缓存数字的实现,在后来,又需要另外一个类型的缓存实现,等等。。。这时候,泛型的另一个作用体现出来了,泛型可以避免这种重复代码,例子上:

    abstract class Cache<T> {
      T getByKey(String key);
      setByKey(String key, T value);
    }
    

    在上面的代码中,T是一个备用类型,这是类型占位符,自己调用该接口的时候会指定具体类型。

    20.2.Using collection literals(使用集合字面量)

    ListMap字面量也是可以参数化的。参数化定义list需要在中括号之间添加<type>,定义map需要在大括号之前添加<KeyType,valueType>。如果你需要更加安全的类型检查,则可以使用参数化定义。例子如下:

    var names = <String>['Seth', 'Kathy', 'Lars'];
    var pages = <String, String>{
      'index.html': 'Homepage',
      'robots.txt': 'Hints for web robots',
      'humans.txt': 'We are people, not machines'
    };
    

    20.3.Using parameterized types with constructors(构造函数中使用泛型)

    在调用构造函数的时候,在类名字后面使用尖括号(<…>)来指定泛型类型。例如:

    var names = new List<String>();
    names.addAll(['Seth', 'Kathy', 'Lars']);
    var nameSet = new Set<String>.from(names);
    

    下面代码创建了一个keyinteger,valueView类型的map:

    var views = new Map<int, View>();
    

    20.4.Generic collections and the types they contain(泛型集合和包含的类型)

    Dart的泛型类型是固化的,在运行时有也可以判断具体的类型,例如在运行时也可以检测集合里面的对象类型:

    var names = new List<String>();
    names.addAll(['Seth', 'Kathy', 'Lars']);
    print(names is List<String>); // 输出:true
    

    is表达式只是判断集合的类型,而不是集合里面具体对象的类型。在成产模式,List<String>变量可以包含非字符串类型对象。对于这种情况,可以选择分包判断每个对象的类型或者处理类型转换异常,java中的泛型信息是编译时的,泛型信息在运行时是不纯在。在java中可以测试一个对象是否为List,但是无法测试一个对象是否为List<String>

    20.5.Restricting the parameterrized type(限制泛型类型)

    当使用泛型类型的时候,可能想限制泛型的具体类型,使用extends可以实现这个功能:

    // T必须是SomebaseClass或其后代之一。
    class Foo<T extends SomeBaseClass> {...}
    
    class Extender extends SomeBaseClass {...}
    
    void main() {
      // 可以在<>中使用somebaseclass或它的任何子类。
      var someBaseClassFoo = new Foo<SomeBaseClass>();
      var extenderFoo = new Foo<Extender>();
    
      //也可以使用no<>
      var foo = new Foo();
    
    
    
       //指定任何非SomeBaseClass类型将导致警告,在Debug检查模式,运行时错误。
      // var objectFoo = new Foo<Object>();
    }
    

    20.6.Using generic method(使用泛型函数)

    一开始,泛型只能在Dart类中使用。新的语法也支持在函数和方法上使用泛型。

    T first<T>(List<T> ts) {
      // ...做一些初始化工作或者错误检查...
      T tmp ?= ts[0];
      // ...做一些额外的检查或者处理...
      return tmp;
    }
    

    这里first(<T>)泛型可以在如下地方使用参数T

    • 函数的返回值类型(T)
    • 参数的类型(List<T>)
    • 局部变量的类型(T tmp)

    在Dart1.21开始使用泛型函数,如果需要使用泛型函数,需要设置SDK版本为1.21或者以上。

    21.Libraries and visibility(库和可见性)

    使用importlibrary指令可以帮助创建模块化的可分享代码。库不仅仅提供API,还是一个私有单元:以下划线(_)开头的标识符只有在库内部可见。每个Dart app都是一个库,即使没有使用library命令也是一个库。这里的库和Android所说的库有相似的地方,简单来讲就是用人家写好库中的API。例如:拍照库,网络请求库等。

    21.1.Using libraries(使用库)

    使用import来指定一个库如何使用另外一个库,例如:Dart web应用通常使用dart:html库,然后可以这样导入库:

    import 'dart:html';
    

    import必须参数为库的URL。对于内置的库,URI使用特殊的dart:scheme。对于其他的库,可以使用文件系统路径或者package:schemepackage:scheme指定的库通过包管理器来提供,如pub工具,如:

    import 'dart:io';
    import 'package:mylib/mylib.dart';
    import 'package:utils/utils.dart';
    

    21.2.Specifying a library prefix(指定库前缀)

    如果导入的库具有冲突的标识符,这个经常遇到,则可以使用库的前缀来区分。例如:如果library1library2都有一个名字为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.
    

    21.3.Importing only part of a library(导入库的一部分)

    如果只使用库的一部分功能,可以选择需要导入的内容,例如:

    // 仅导入foo
    import 'package:lib1/lib1.dart' show foo;
    
    // 导入除foo以外的所有名称。
    import 'package:lib2/lib2.dart' hide foo;
    

    21.4.Lazily loading a library(延迟载入库)

    Deferred loading可以让应用在需要的时候再加载库。这我就想到了App的开启时间,下面是一些使用延迟加载库的场景:

    • 减少APP的启动时间
    • 执行A/B测试,例如尝试各种算法的不同实现
    • 加载很少使用的功能,例如可选的屏幕和对话框

    延迟加载一个库,需要先使用deferred as来导入:

    import 'package:deferred/hello.dart' deferred as hello;
    

    当需要使用的时候,使用库标识符调用loadLibrary()函数来加载库:

    greet() async {
      await hello.loadLibrary();
      hello.printGreeting();
    }
    

    在上面代码,使用await关键字暂停代码执行一直到库加载完成。在一个库上可以多次调用loadLibrary()函数。但是该库只是载入一次。在使用延迟加载库的时候,要注意:

    • 延迟加载库的常量在导入的时候是不可用的。只有当库加载完毕的时候,库中常量才可以使用。
    • 在导入文件的时候无法使用延迟库中的类型。如果需要使用类型,则考虑把接口类型移动到另外一个库中, 让两个库都分别导入这个接口库。
    • Dart隐含的把loadLibrary()函数导入到使用deferred as 命名空间loadLibrary()方法返回一个Future

    22.Asynchrony support(异步支持)

    Dart有一些语言特性来支持异步编程。最常见的特性是async方法和await表达式。Dart库中有很多返回Future或者Stream对象的方法。这些方法是异步:这些函数在设置完基本的操作后就返回了,而无需等待执行完成。例如读取一个文件,在打开文件后就返回了。有两种方式可以使用Future对象中的数据:

    • 使用asyncawait
    • 使用Future API

    同样,从Stream中获取数据也有两种方式:

    • 使用async和一个异步for循环(await for)
    • 使用Stream API

    使用asyncawait的代码是异步的,但是看起来有点像同步代码。如:下面是使用await来等待异步方法返回的示例:

    await lookUpVersion()
    

    要使用await,其方法必须带有async关键字:

    checkVersion() async {
      var version = await lookUpVersion();
      if (version == expectedVersion) {
        // 主体内容.
      } else {
        // 主体内容.
      }
    }
    

    可以使用try,catchfinally来处理使用await的异常:

    try {
      server = await HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, 4044);
    } catch (e) {
      // 对无法绑定到端口作出反应...
    }
    

    22.1.Declaring async functions(声明异步方法)

    一个async方法是函数体被标记为async的方法。虽然异步方法的执行可能需要一定时间,但是异步方法立刻返回-在方法体还没执行之前就返回了。

    checkVersion() async {
      // ...
    }
    
    lookUpVersion() async => /* ... */;
    

    在一个方法上添加async关键字,则这个方法返回值为Future。例如,下面是一个返回字符串的同步方法:

    String lookUpVersionSync() => '1.0.0';
    

    如果使用async关键字,则该方法返回一个Future,并且认为该函数是一个耗时操作。

    Future<String> lookUpVersion() async => '1.0.0';
    

    注意,方法的函数体并并不需要使用FutureAPI。Dart自动在需要的时候创建Future对象。

    22.2.Using await expressions with Futures(使用await表达式)

    await表达式具有如下的形式:

    await expression
    

    在一个异步方法内可以使用多次await表达水。例如,下面的示例使用三次await表达式来执行相关的功能:

    var entrypoint = await findEntrypoint();
    var exitCode = await runExecutable(entrypoint, args);
    await flushThenExit(exitCode);
    

    await expression中,expression的返回值通常是一个Future;如果返回的值不是Future,则Dart会自动把该值放到Future中返回。Future对象代表返回一个对象的承诺。await expression执行的结果为这个返回的对象。await expression会阻塞主,直到需要的对象返回为止。如果await无法正常使用,请确保是在一个async方法中。例如要在main()方法中使用await,则main()方法的函数体必须标记为async:

    main() async {
      checkVersion();
      print('In main: version is ${await lookUpVersion()}');
    }
    

    22.3.Using asynchronous for loops with Stream(在循环中使用异步)

    异步for循环具有以下形式:

    await for (variable declaration in expression) {
      // 每次流发出时会执行.
    }
    

    上面的expression返回的值必须是Stream类型。执行流程如下:

    1. 等待直到stream返回一个数据
    2. 使用stream返回的参数执行for循环代码
    3. 重复执行1和2直到stream数据返回完毕

    使用break或者return语句可以停止接收stream数据,这样就挑出了for循环并且从stream上取消注册了。如果**异步for循环不能正常工作,确保是在一个async方法中使用。**如:要想在main()方法中使用异步for循环,则需要把main()方法的函数体标记为async

    main() async {
      ...
      await for (var request in requestServer) {
        handleRequest(request);
      }
      ...
    }
    

    23.Callable classes(可调用的类)

    如果Dart类实现call()函数则可以当做方法来调用,下面例子中,wannabeFunction类定义了一个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!
    }
    

    24.Isolates

    现代的浏览器和移动浏览器运行在多核CPU系统上。要充分利用这些CPU,开发者一般使用共享内存数据来爆炸多线程的正确运行。然而,多线程共享数据通常会导致很多潜在的问题,并导致代码运行出错,结果不是预期。所有的Dart代码在isolates中运行而不是线程,每个isolate都有自己的堆内存,并且确保每个isolate的状态都不能被其他isolate访问。

    25.Typedefs

    在Dart语言中,方法也是对象。使用typedef或者function-type-alias来为方法类型命名,然后可使用命名的方法,当把方法类型赋值给一个变量的时候,typedef保留类型信息。下面代码没有使用typedef:

    class SortedCollection {
      Function compare;
    
      SortedCollection(int f(Object a, Object b)) {
        compare = f;
      }
    }
    
     // Initial, broken implementation.
     int sort(Object a, Object b) => 0;
    
    main() {
      SortedCollection coll = new SortedCollection(sort);
    
      // 我们只知道 compare 是一个 Function 类型,
      // 但是不知道具体是何种 Function 类型?
      assert(coll.compare is Function);
    }
    

    当把f赋值给compare的时候,类型信息丢失了,f的类型是(object,object)->int当然该类型是一个Function。如果使用显式的名字并保留类型信息,开发者和工具可以使用这些信息:

    typedef int Compare(Object a, Object b);
    
    class SortedCollection {
      Compare compare;
    
      SortedCollection(this.compare);
    }
    
     // Initial, broken implementation.
     int sort(Object a, Object b) => 0;
    
    main() {
      SortedCollection coll = new SortedCollection(sort);
      assert(coll.compare is Function);
      assert(coll.compare is Compare);
    }
    

    26.Metadata(元数据)

    使用元数据给代码添加额外信息,元数据注解是以@字符开头,后面是一个编译时常量或者调用一个常量构造函数。有三个注解所有的Dart代码都可使用:@deprecated@override@proxy,下面直接上@deprecated的示例:

    class Television {
      /// 已经弃用,请改用[打开]
      @deprecated
      void activate() {
        turnOn();
      }
    
      /// 打开电视.
      void turnOn() {
        print('on!');
      }
    }
    

    可以定义自己的元数据注解。下面的示例定义一个带有两个参数的@todo注解:

    library todo;
    
    class todo {
      final String who;
      final String what;
    
      const todo(this.who, this.what);
    }
    
    

    使用@todo注解的示例:

    import 'todo.dart';
    
    @todo('seth', 'make this do something')
    void doSomething() {
      print('do something');
    }
    

    元数据可以在librarytypedeftype parameterconstructorfactoryfunctionfieldparameter、或者variable声明之前使用,也可以在import或者export指令之前使用,使用反射可以再运行时获取元数据信息。

    五、总结

    Dart语法和Java语法很像,很容易上手,理解很简单,用一天就把语法整理了一遍,我为什么要学习Dart语法呢?一开始解释很清楚了,无非就是把根基打稳。学什么一定要带有目的性去学,下面直接上一张图:

    带有目的性去学

    学习资源:

    1. 起步-Dart
    2. 为java开发人员准备的Dart教程

    六、额外知识

    Flutter有四种运行模式:Debug、Release、Profile和test。

    1. Debug:Debug模式可以在真机和模拟器上同时运行:会打开所有的断言,包括debugging信息、debugger aids(比如observatory)和服务扩展。
    2. Release:Release模式只能在真机上运行,不能在模拟器上运行:会关闭所有断言和debugging信息,关闭所有debugger工具。
    3. Profile:Profile模式只能在真机上运行,不能在模拟器上运行:基本和Release模式一致。
    4. test: headless test模式只能在桌面上运行:基本和Debug模式一致。

    Flutter四种运行方式

    展开全文
  • 前言 本文的原文连接是: ...未经博主允许不得转载。 ...1,关于dart 要开发 flutter 必须得用 dart 进行开发。 这个是个很容易学的语言,比golang 还容易,和java JavaScrip...

    前言


    本文的原文连接是: https://blog.csdn.net/freewebsys/article/details/90488394
    未经博主允许不得转载。
    博主地址是:http://blog.csdn.net/freewebsys

    1,关于dart


    要开发 flutter 必须得用 dart 进行开发。
    这个是个很容易学的语言,比golang 还容易,和java JavaScript 非常相似。
    Flutter 官方网站:
    https://flutter.dev
    https://flutter-io.cn/
    https://codelabs.flutter-io.cn/
    参考官方代码:
    https://codelabs.flutter-io.cn/codelabs/first-flutter-app-pt1-cn/index.html#0
    dart 官方网站:
    https://dart.dev/
    下载 flutter:
    https://flutter.dev/docs/get-started/install
    下载 android studio :
    https://developer.android.com/studio

    但是默认 android 里面没有 flutter 插件。
    打开 android 选择 flutter 插件,安装了 flutter 插件会自动关联安装 dart 插件.

    2,dart 语法


    https://zhuanlan.zhihu.com/p/38847885

    var 关键字声明一个变量,var 关键字声明一个变量

     main(List<String> args) {
      var number = 42;
      var name = 'Gurleen Sethi';
      var salary = 150300.56;
      var isDoorOpen = true;
    }
    

    声明变量:

    int number = 42;
      String name = 'Gurleen Sethi';
      double salary = 150300.56;
      bool isDoorOpen = true;
    final int number = 42;
    const String name = 'Gurleen Sethi';
    //Omit explicitly defining data types
    final salary = 150300.56;
    const isDoorOpen = true;
    

    final 和 const 的不同是,const 是编译时常量,也就是编译时就要有值,如 const
    PI=3.14,而 final 只准许赋值一次,可以在编译时也可以在运行时。 dart 提供基本的数据类型:
    • Numbers 只有 int double 没有 float
    • Strings 带 $xxx 占位解析
    • Booleans
    • Lists
    • Maps

      var list = [1,2,3,4];
    list.length
    print(list[1]); //Outout: 2 list.add(10); list.remove(3);//只删除一个。符合条件的第一个 list.removeAt(0);//下标删除
    var map = {
       'key1': 'value1',
       'key2': 'value2',
       'key3': 'value3'
    };
      print(map['key1']);
      print(map['test']);
    //Output: value1
    //Output: null
     map['key4'] = 'value4';
    //Length
    
    print(map.length);
    //Check if a key is present
    map.containsKey('value1');
    //Get entries , keys and values
    var entries = map.entries;
    var keys = map.keys;
    var values = map.values;
    

    函数,第一个是返回类型,可以忽略,也可以使用箭头函数。

    String fullName(String firstName, String lastName) {
      return "$firstName $lastName";
    }
    fullName(String firstName, String lastName) {
      return "$firstName $lastName";
    }
    fullName(String firstName, String lastName) => "$firstName $lastName";
    

    带默认值 ,函数传递函数

        fullName({String firstName, String lastName = "Lazy"}) {
          return "$firstName $lastName";
        }
        main(List<String> args) {
           out(printOutLoud);
        }
        out(void inner(String message)) {
           inner('Message from inner function');
        }
        printOutLoud(String message) {
           print(message.toUpperCase());
        }
    

    这里有个地方比较特别:
    async await future 语法
    延迟计算 future 对象。加入了 battery lib 后进行异步调用。

    //第 1 种写法。
    void getBattery1() {
    // Instantiate it
    var battery = Battery();
    battery.batteryLevel.then((val) {
    // Access current battery level
    print("get method1 batteryLevel $val ");
    });
    }
    //第 2 种写法。
    void getBattery2() async {
    // Instantiate it
    var battery = Battery();
    var val = await battery.batteryLevel;
    // Access current battery level
    print("get method2 batteryLevel $val ");
    }
    

    async await 延迟计算,针对函数,成对出现。

    3,package包使用


    官方说的是在 dependencies 下面增加,mqtt_client: ^5.5.3 ,然后点击【packages get】。
    非常方便的点击get 按钮就可以下载相关的包了。

    4,总结


    官方学习地址:
    https://codelabs.flutter-io.cn/

    dart 语法非常容易学呢。

    其他开源项目:
    https://github.com/brianegan/flutter_redux
    https://github.com/OpenFlutter/Flutter-Notebook
    https://github.com/flutter/samples
    https://github.com/flutter/samples/blob/master/INDEX.md

    本文的原文连接是:
    https://blog.csdn.net/freewebsys/article/details/90488394

    博主地址是:http://blog.csdn.net/freewebsys

    展开全文
  • 本文出自 AWeiLoveAndroid的博客 Flutter系列博文链接 ↓: 工具安装: Flutter从配置安装到填坑指南详解 ...Flutter基础篇: ... Flutter基础篇(2)-- 老司机用一篇博客带你快速熟悉Dart语法 F...

    转载出处:
    https://www.jianshu.com/p/3d927a7bf020
    本文出自 AWeiLoveAndroid的博客


    Flutter系列博文链接 ↓:

    工具安装:

    Flutter基础篇:

    Flutter进阶篇:

    Dart语法系列博文链接 ↓:


    【前言】Dart语言是使用flutter框架开发时候必备的语言,flutter是一个跨平台的框架,一套代码就可以完美实现安卓和ios两个平台,适配也很不错,Dart语言很友好,和java很类似,学习成本也是很低的。所以这也是我推荐学习Dart语言的一个原因。

    从本篇文章开始讲解Dart语言的基本使用,我将会连续推出好几篇文章详解,希望帮助大家快速掌握Dart语言。


    本文目录:

    一、注释
    二、关键字(56个)
    三、变量和常量
    四、特殊数据类型
    五、运算符
    六、控制流程语句
    七、异常


    本文代码同步发布在Github: https://github.com/AweiLoveAndroid/Flutter-learning/tree/master/projects/dart_demo

    怎么运行代码?

    如果你使用IDEA或者Android Studio:

    打开IDEA或者Android Studio,新建一个Flutter项目,然后在test目录运行我的代码;或者里面去写你自己的dart文件,然后右键run就可以运行了。(注意:需要连接手机或者模拟器)。

    如果你使用vscode:

    打开vscode,执行菜单栏运行,就可以了(确保只有一个dart文件,免得运行的文件不是你想要的,就很尴尬了,vscode也可以设置默认运行的文件是哪个,但是新手不建议去设置,很麻烦。因为你们想最快的运行效果,所有建议只有一个dart文件是最好的)。


    一、注释

    Dart的注释分为3种:单行注释、多行注释、文档注释。

    1、单行注释以//开头。Dart编译器会忽略//和行尾之间的所有内容。

    例如:// todo:待完成

    2、多行注释以/*开头,以*/结尾。介于/**/两者之间的内容会被编译器忽略(除非该注释是一个文档注释)。多行注释可以嵌套。

    例如:/* todo:待完成 */

    3、文档注释以///或者/**开头。可以通过dartdoc命令导出文档。

    文档注释的使用,例如:/// todo:待完成

    文档的导出如图所示:

     

    文档的导出

    导出的结果在我的工程根路径的/doc/api/文件夹里面,如图所示:

    导出的结果

    然后浏览器打开index.html就可以看到文档了。如图所示:

    本地的文档


    二、关键字(56个)

    33个保留字(不能使用保留字作为标识符)

    关键字 - - -
    if superdo switch assert
    else in this enum
    is throw true break
    new try case extends
    null typedef catch var
    class false void const
    final rethrow while continue
    finally return with for
    default - - -

    其中内置标志符有:(17个)

    关键字 - - -
    abstract deferred as dynamic
    covariant export external factory
    get implements import library
    operator part set static
    typedef    

    其中Dart2相对于Dart1新增的,支持异步功能的关键字有:(6个)

    关键字 - - -
    async async* await sync*
    yield yield*  

    跟java相比,Dart特有的关键字有:(25个)

    关键字 - - -
    deferred as assert dynamic
    sync* async async* in
    is await export library
    external typedef factory operator
    var part const rethrow
    covariant set yield get
    yield*    

    三、变量和常量

    (一)变量的声明,可以使用 var、Object 或 dynamic 关键字。

    创建变量并初始化变量实例:

        var name = '张三' ;
    

    变量存储引用。

      1. 使用Object或dynamic关键字
        dynamic name = '张三';
    

    调用的变量name包含对String值为“张三” 的对象的引用。
    name推断变量的类型是String,但可以通过指定它来更改该类型。
    如果对象不限于单一类型(没有明确的类型),请使用Object或dynamic关键字

        Object name = '张三';
        dynamic name = '李四';
    
      1. 显式声明将被推断的类型

    比如String,int等。

        //可以使用String显示声明字符串类型
        String name = '张三' ; //代替var name = '张三';
    

    这个类型有很多,具体在下文有介绍。

    (二)默认值

    未初始化的变量的初始值为null(包括数字),因此数字、字符串都可以调用各种方法。

        //测试 数字类型的初始值是什么?
        int intDefaultValue;
        // assert 是语言内置的断言函数,仅在检查模式下有效
        // 在开发过程中, 除非条件为真,否则会引发异常。(断言失败则程序立刻终止)
        assert(intDefaultValue == null);
        print(intDefaultValue);//打印结果为null,证明数字类型初始化值是null
    

    (三)Final 和 Const的用法

    如果您从未打算更改一个变量,那么使用 final 或 const,不是var,也不是一个类型。
    一个 final 变量只能被设置一次;const 变量是一个编译时常量。(Const变量是隐式的final。)
    final的顶级或类变量在第一次使用时被初始化。

    • 1、被final或者const修饰的变量,变量类型可以省略。
    //可以省略String这个类型声明
    final name1 = "张三";
    //final String name1  = "张三";
        
    const name2 = "李四";
    //const String name2 = "李四";
    
    • 2、被 final 或 const 修饰的变量无法再去修改其值。
      final name1 = "张三";
        // 这样写,编译器提示:a final variable, can only be set once
        // 一个final变量,只能被设置一次。
        //name1 = "zhangsan";
        
        const name2 = "李四";
    
        // 这样写,编译器提示:Constant variables can't be assigned a value
        // const常量不能赋值
        // name2 = "lisi";
    
    • 3、注意:flnal 或者 const 不能和 var 同时使用,
        //这样写都会报错
        //final var name1 = "张三";
        //const var name2 = "李四";
    
    • 4、常量如果是类级别的,请使用 static const
        static const speed = 100;
    
    • 5、常量的运算
        const speed = 100; //速度(km/h)
        const double distance = 2.5 * speed; // 距离 = 速度 * 时间
    
        final speed2 = 100; //速度(km/h)
        final double distance2 = 2.5 * speed2; // 距离 = 速度 * 时间
    
    • 6、const关键字不只是声明常数变量。您也可以使用它来创建常量值,以及声明创建常量值的构造函数。 任何变量都可以有一个常量值。
      // 注意: [] 创建的是一个空的list集合
      // const []创建一个空的、不可变的列表(EIL)。
      var varList = const []; // varList 当前是一个EIL
      final finalList = const []; // finalList一直是EIL
      const constList = const []; // constList 是一个编译时常量的EIL
    
      // 可以更改非final,非const变量的值
      // 即使它曾经具有const值
      varList = ["haha"];
    
      // 不能更改final变量或const变量的值
      // 这样写,编译器提示:a final variable, can only be set once
      // finalList = ["haha"];
      // 这样写,编译器提示:Constant variables can't be assigned a value  
      // constList = ["haha"];
    
    • 7、只要任何插值表达式是一个计算结果为null或数字,字符串或布尔值的编译时常量,那么文字字符串就是编译时常量。(关于$表达式和不同的数据类型后面会讲解。)
    // 这些是常量字符串
    const aConstNum = 0;
    const aConstBool = true;
    const aConstString = 'a constant string';
    
    // 这些不是常量字符串
    
    var aNum = 0;
    var aBool = true;
    var aString = 'a string';
    const aConstList = const [1, 2, 3];
    
    const validConstString = '$aConstNum $aConstBool $aConstString';
    
    //这样用就会报错:Const variables must be initialized with a constant value
    // const常量必须用conat类型的值初始化。
    // const invalidConstString = '$aNum $aBool $aString $aConstList';
    

    四、特殊数据类型

    Dart支持以下特殊类型:

    numbers 数字
    strings 字符串
    booleans 布尔
    lists list集合(也称为数组)
    maps map集合
    runes 字符(用于在字符串中表示Unicode字符)

    (一)num数字类型

    num是数字类型的父类,有两个子类 intdouble
    num类型包括基本的运算符,如+,-,/和*,位运算符,如>>,在int类中定义。如果num和它的子类没有你要找的东西,math库可能会找到。比如你会发现abs(),ceil()和floor()等方法。

    (1)int类型

    int表示整数,int默认是64位二进制补码整数,int的取值不大于64位,具体取决于平台。编译为JavaScript时,整数仅限于valus,可以用双精度浮点值精确表示。可用的整数值包括-253和253之间的所有整数,以及一些幅度较大的整数。这包括一些大于2^63的整数。 因此,在编译为JavaScript的Dart VM和Dart代码之间,int类中的运算符和方法的行为有时会有所不同。例如,当编译为JavaScript时,按位运算符将其操作数截断为32位整数。
    示例如下:

      int intNum1 = 10 ;
      print(intNum1);//结果是10
      int intNum2 = 0xDEADBEEF ;
      print(intNum2);//结果是3735928559
    

    判断一个int值需要多少bit(位),可以使用bitLength,例如:

      // bitLength 返回存储此int整数所需的最小位数
      int a1 = 1; // 占了1个bit     相当于二进制数字 00000000 00000001
      int a2 = 12; // 占了4个bit    相当于二进制数字 00000000 00001100
      int a3 = 123; // 占了7个bit   相当于二进制数字 00000000 01111011
      int a4 = 1234; // 占了11个bit 相当于二进制数字 00000100 11010010
        print('${a1.bitLength}'); //  1
      print('${a2.bitLength}');  // 4
        print('${a3.bitLength}'); // 7
        print('${a4.bitLength}'); // 11
    

    (2)double类型

    Dart的double是IEEE 754标准中规定的64位浮点数。double的最大值是:1.7976931348623157e+308,double类里面有一个常量maxFinite,我们通过语句print(double. maxFinite)可以得到double的最大值
    如果一个数字包含一个小数,那么它就是一个double类型。示例如下:

    double doubleNum1 = 1.1;
      print(doubleNum1); //结果是1.1
    double doubleNum2 = 1.42e5;
    print(doubleNum2); //结果是142000.0
    

    (3)Dart2.1里面新增特性,当double的值为int值时,int自动转成double。

    例如:double test = 12;//打印结果是12.0

    (4)Dart2.1,int也有api转成double。

    例如:

       int test = 10;
       print(test.toDouble()); // 结果是: 10.0
    

    (5)Dart2.1,double也有api转成int,会把小数点后面的全部去掉。

    例如:

       double test2 = 15.1;
       double test3 = 15.1234;
       print(test2.toInt());// 结果是15
    print(test3.toInt());// 结果是15
    

    (二)String字符串类型

    Dart里面的String是一系列 UTF-16代码单元。

    (1)您可以使用单引号或双引号来创建一个字符串。

    String str1 = '单引号基本使用demo.';
    String str2 = "双引号基本使用demo.";
    

    (2)单引号或者双引号里面嵌套使用引号。

    单引号里面嵌套单引号,或者//双引号里面嵌套双引号,必须在前面加反斜杠。

    // 单引号里面有单引号,必须在前面加反斜杠
    String str3 = '单引号里面有单引号it\'s,必须在前面加反斜杠.';
    //双引号里面嵌套单引号(正常使用)
    String str4 = "双引号里面有单引号it's.";
    //单引号里面嵌套双引号(正常使用)
    String str5 = '单引号里面有双引号,"hello world"';
    //双引号里面嵌套双引号,必须在前面加反斜杠
    String str6 = "双引号里面有双引号,\"hello world\"";
    
    print(str3);// 双引号里面有单引号it's,必须在前面加反斜杠
    print(str4);// 双引号里面有单引号it's.
    print(str5);// 单引号里面有双引号,hello world"
    print(str6);//双引号里面有双引号,"hello world"
    

    (3)多个字符串相邻中间的空格问题:

    除了单引号嵌套单引号或者双引号嵌套双引号不允许出现空串之外,其余的几种情况都是可以运行的。
    示例如下:

    这个会报错
    //String blankStr1 = 'hello''''world';
    //这两个运行正常
    String blankStr2 = 'hello'' ''world'; //结果: hello world
    String blankStr3 = 'hello''_''world'; //结果: hello_world
    
    
    // 这个会报错
    //String blankStr4 = "hello""""world";
    这两个运行正常
    String blankStr5 = "hello"" ""world"; //结果: hello world
    String blankStr6 = "hello""_""world"; //结果: hello_world
    

    单引号里面有双引号,混合使用运行正常

    String blankStr7 = 'hello""""world'; //结果: hello""""world
    String blankStr8 = 'hello"" ""world'; //结果: hello"" ""world
    String blankStr9 = 'hello""_""world'; //结果: hello""_""world
    

    双引号里面有单引号,混合使用运行正常

    String blankStr10 = "hello''''world"; //结果: hello''''world
    String blankStr11 = "hello'' ''world"; //结果: hello'' ''world
    String blankStr12 = "hello''_''world"; //结果: hello''_''world
    

    (4)您可以使用相邻字符串文字或+ 运算符连接字符串:

    1. 直接把相邻字符串写在一起,就可以连接字符串了。
      String connectionStr1 =  '字符串连接''甚至可以在''换行的时候进行。';
      print(connectionStr1);// 字符串连接甚至可以在换行的时候进行。
    
    1. 用+把相邻字符串连接起来。
      String connectionStr2 =  '字符串连接'+ '甚至可以在'+ '换行的时候进行。';
      print(connectionStr2);// 字符串连接甚至可以在换行的时候进行。
    
    1. 使用单引号或双引号的三引号:
    String connectionStr3 = ''' 
      这是用单引号创建的
      多行字符串。
      ''' ;
    print(connectionStr3);
    String connectionStr4 = """这是用双引号创建的
      多行字符串。""";
    print(connectionStr4);
    

    print(connectionStr3)输出结果如下:

      这是用单引号创建的
      多行字符串。
    

    print(connectionStr4)的输出结果如下:

    这是用双引号创建的
      多行字符串。
    

    (5)关于转义符号的使用

    声明raw字符串(前缀为r),在字符串前加字符r,或者在\前面再加一个\
    可以避免“\”的转义作用,在正则表达式里特别有用。

    举例如下:

        print(r"换行符:\n"); //这个结果是 换行符:\n
        print("换行符:\\n"); //这个结果是 换行符:\n
        print("换行符:\n");  //这个结果是 换行符:
    

    (6)使用$可以获得字符串中的内容,使用${表达式}也可以将表达式的值放入字符串中。使用${表达式}时可以使用字符串拼接,也可以使用String类或者Object里面的某些方法获得相关字符串属性。

    1、使用$+字符串

    var height = 48.0;
    print('当前标题的高度是$height'); //当前标题的高度是48.0
    

    2、使用$+字符串,以及字符串拼接

    String name = "张三";
    print("$name"+"是我们的部门经理"); // 张三是我们的部门经理
    

    3、这里使用了字符串的拼接,以及使用了String类里面的toUpperCase()函数,把字母全部变成大写。

    String replaceStr = 'Android Studio';
    assert('你知道' +
            '${replaceStr.toUpperCase()}'
              + '最新版本是多少吗?' ==
              '你知道ANDROID STUDIO最新版本是多少吗?'); 
    

    注:==操作符测试两个对象是否相等。assert是断言,如果条件为true,继续进行,否则抛出异常,中端操作。

    (三)bool布尔类型

    Dart表示布尔值的类型叫做bool,它有两个值,分别是:truefalse,它们都是编译时常量。
    Dart使用的是显式的检查值,检查值的类型,如下所示:

      // 检查是否为空字符串
      var emptyStr = '';
      assert(emptyStr.isEmpty);
    
      // 检查是否小于等于0
      var numberStr = 0;
      assert(numberStr <= 0);  
    
      // 检查是否为null
      var nullStr;
      assert(nullStr == null);
    
      // 检查是否为NaN
      var value = 0 / 0;
      assert(value.isNaN);
    

    assert 是Dart语言里的的断言函数,仅在Debug模式下有效
    在开发过程中, 除非条件为真,否则会引发异常。(断言失败则程序立刻终止)。

    (四)list集合,也成为数组

    在Dart中,数组是List对象,因此大多数人只是将它们称为List。
    以下是一个简单的Dart的List:

    创建一个int类型的list

    List list = [10, 7, 23];
    print(list);// 输出结果  [10, 7, 23]
    

    要创建一个编译时常量const的list,示例如下:

    List constantList = const[10,3,15];
    print(constantList);// 输出结果  [10, 3, 15]
    

    注意事项:

    1.可以直接打印list包括list的元素,list也是一个对象。但是java必须遍历才能打印list,java若直接打印list,结果是地址值。
    2.和java一样list里面的元素必须保持类型一致,不一致就会报错。
    3.和java一样list的角标从0开始。

    Dart的list集合给我们提供了很多api,示例如下,api太多就不逐个展示了:

    操作 代码 含义 输出结果
    新增 list.add(1);print(list); 把数字1添加到list中,默认是添加到末尾 [10, 7, 23, 1]
    移除 list.remove(1);print(list); 移除数字1 [10, 7, 23]
    插入 list.insert(0, 5);print(list); 在索引为0的地方插入数字5 [5, 10, 7, 23]
    查找某个索引的值 int value = list.indexOf(10);print(value); 查找10在list中的索引 1
    判断元素是否包含 bool result = list.contains(5);print(result); 查找list中是否包含数字5 true

    (五)map集合

    Dart中的map是将键和值相关联的对象。键和值都可以是任何类型的对象。每个键只出现一次,但您可以多次使用相同的值。

    (1)创建方式:

      1. 直接声明,用{}表示,里面写key和value,每组键值对中间用逗号隔开。
    Map companys = {'first': '阿里巴巴', 'second': '腾讯', 'fifth': '百度'};
    print(companys);//打印结果 {first: 阿里巴巴, second: 腾讯, fifth: 百度}
    
      1. 先声明,再去赋值。
      Map companys1 = new Map();
      companys1['first'] = '阿里巴巴';
      companys1['second'] = '腾讯';
      companys1['fifth'] = '百度';
      print(companys1);
      //打印结果 {first: 阿里巴巴, second: 腾讯, fifth: 百度}
    
      1. 要创建一个编译时常量const的map,请在map文字之前添加const:
    final fruitConstantMap = const {2: 'apple',10: 'orange',18: 'banana'};
    // 打印结果{second: 腾讯, fifth: 百度, 5: 华为}
    

    (2)添加元素。格式: 变量名[key] = value,其中key可以是不同类型。

    //添加一个新的元素,key为“5”,value为“华为”
      companys[5] = '华为';
      print(companys);//打印结果 {first: 阿里巴巴, second: 腾讯, fifth: 百度, 5: 华为}
    

    (3)修改元素。格式:变量名[key] = value

    例如:把key为first的元素对应的value改成 alibaba

      companys['first'] = 'alibaba';
      print(companys);//打印结果 {first: alibaba, second: 腾讯, fifth: 百度, 5: 华为}
    

    (4)查询元素

      bool mapKey = companys.containsKey('second');
      bool mapValue = companys.containsValue('百度');
      print(mapKey); //结果为:true
      print(mapValue); //结果为:true
    

    (5)删除元素.可以使用map的remove或者clear方法。

      companys.remove('first');// 移除key为“first”的元素。
      print(companys);// 打印结果{second: 腾讯, fifth: 百度, 5: 华为}
    
      companys.clear();// 清空map集合的数据。
      print(companys);// 打印结果{}
    

    (6)关于map集合的小结:

    1.创建map有两种方式。
    2.map的key类型不一致也不会报错。
    3.添加元素的时候,会按照你添加元素的顺序逐个加入到map里面,哪怕你的key不连续。
    比如key分别是 1,2,4,看起来有间隔,事实上添加到map的时候{1:value,2:value,4:value} 这种形式。
    4.添加的元素的key如果是map里面某个key的英文,照样可以添加到map里面,
    比如可以为3和key为three可以同时存在。
    5.map里面的key不能相同,但是value可以相同,value可以为空字符串或者为null。
    

    (六)runes 字符(用于在字符串中表示Unicode字符)

    Unicode为世界上所有的书写系统中使用的每个字母,数字和符号定义了唯一的数值。
    Dart字符串是UTF-16代码单元的序列,所以在字符串中表达32位Unicode值需要特殊的语法。
    Unicode代码点的常用方法是\uXXXX,其中XXXX是一个4位十六进制值。

    例如,心形字符()是\u2665。要指定多于或少于4个十六进制数字,请将该值放在大括号中。 例如,笑的表情符号是\u{1f600}

    String类有几个属性可以用来提取符文信息。 codeUnitAt和codeUnit属性返回16位代码单元。
    以下示例说明了符文,16位代码单元和32位代码点之间的关系。

    var clapping = '\u{1f44f}';
    print(clapping);
    print(clapping.codeUnits);
    print(clapping.runes.toList());
    
    //使用String. fromCharCodes显示字符图形 
    Runes input = new Runes(
            '\u2665  \u{1f605}  \u{1f60e}  \u{1f47b}  \u{1f596}  \u{1f44d}');
    print(new String.fromCharCodes(input));
    

    五、运算符

    运算符在每一种语言中都很常见,Dart的运算符如下表所示:

    我这里不详细去讲解每个运算符的用法,我这里主要讲一下Dart里面比较有代表性的以及有特点的一些运算符相关用法。

    (一)?..一样,但最左边的操作数可以为空。

    比如:Test?.funs从表达式Test中选择属性funs,除非Test为空(当Test为空时,Test?.funs的值为空)。

    class Test {
      static int funs = 5;
    
      Test() {
        print('构造函数 Test');
      }
      static fun() {
        print('Test fun函数');
      }
    }
    void main(){
      print(Test?.funs); // 打印5
    }
    

    (二)..级联符号..

    级联符号..允许您在同一个对象上进行一系列操作。 除了函数调用之外,还可以访问同一对象上的字段。其实相当于java的链式调用。
    例如:

    String s = (new StringBuffer()
            ..write('test1 ')
            ..write('test2 ')
            ..write('test3 ')
            ..write('test4 ')
            ..write('test5'))
          .toString();
    print(s); // test1 test2 test3 test4 test5
    

    (三)?? 三目运算符的一种形式。

    expr1 ?? expr2 表示如果expr1非空,则返回其值; 否则返回expr2的值。

    //普通三元运算符
    int a = 10;
    var values = a > 5 ? a : 0;
    //??操作符
    print('a ??=3 : ${a ??= 3}');  // 3
    

    (四)~/ 除法,返回一个整数结果,其实就是取商。

    小学都学过:被除数 ÷ 除数 = 商 ... 余数,在Dart里面A ~/ B = C,这个C就是商,这个语句相当于Java里面的A / B = C。Dart与java不同的是,Dart里面如果使用A / B = D语句,这个结果计算出来的是真实的结果。示例如下:

      var result1 = 15/7;
      print(result1); // 结果是:2.142857...
      var result2 = 15~/7;
      print(result2); // 结果是:2
    

    顺便提一下取模操作,在Dart里面A % B = E,这个E就是余数,%符号表示取模,例如:

     var result3 = 15%7;
      print(result3); // 结果是:1
    

    (五)as、is与is!

    as 判断属于某种类型
    is 如果对象具有指定的类型,则为true
    is! 如果对象具有指定的类型,则为false

    例如:

    class Test {
      static int funs = 5;
    
      Test() {
        print('构造函数 Test');
      }
      static fun() {
        print('Test fun函数');
      }
    }
    
    class Test2 extends Test {
      Test2() {
        print('构造函数 Test2');
      }
      void fun() {
        print('Test2 fun函数');
      }
    }
    
    void main(){
      print(test2 is Test);  // true
      print(test is! Test2);  // true
    
      (test2 as Test2).fun();  // Test2 fun函数
      // 相当于
      // if (test2 is Test) {
      //   test2.fun();
      // }
    

    六、控制流程语句

    控制流程语句和Java语言差不多,有这些语句:

    (一)if else

    if(条件语句){
        内容体
    }else{
    内容体
    }
    

    (二)for循环

    1.简单for循环

    for(初始值;判断条件;循环后的语句){
        内容体
    }
    

    例如:

    for(int i=0;i<10;i++){
        print(i);
    }
    

    也可以通过for循环内部的闭包获取索引的值。

    var array = [];
    for(var i=0;i<10;i++){
        array.add(()=> print(i));
    }
    

    2.使用foreach循环,一般List和Set都可以使用foreach遍历元素。

    如果要迭代的对象是Iterable,或者你不想知道当前的迭代次数,可以使用foreach()方法。

    var numbers = [1,2,3,4,5,6,7,8,9];
    numbers.foreach((number)=> print(number));
    

    3.使用for in循环,一般List和Set使用for-in遍历元素。

    var list = [1,2,3];
    for(var data in list){
        print(data);
    }
    

    4.Dart的for循环里面可以使用标记:(比较有特色的地方)

    Dart的标记:标记是后面跟着冒号的标识符。带标记的陈述是以标记 L为前缀的陈述。带标签的case子句是标签L前缀的switch语句中的case子句。标签的唯一作用是为“break”和“continue”声明提供对象。
    大多数此功能与其他语言类似,因此以下大部分内容可能对读者来说都很熟悉。Dart的switch声明中处理continue是比较独特的,所以这一部分需要一点时间去阅读和熟悉。

    • 1.循环(Loops)

    标签最常用作breakcontinue内部循环。假设你有嵌套的循环,并要跳转到breakcontinue到外部循环。如果没有标记,这不可能(轻松)实现。

    以下示例使用continue 标记名称从内部循环直接跳转到外部循环的下一轮循环:

    // 返回具有最小总和的内部列表(正整数)。
    /// Returns the inner list (of positive integers) with the smallest sum.
    List<int> smallestSumList(List<List<int>> lists) {
      var smallestSum = 0xFFFFFFFF; //已知list的总和较小。
      var smallestList = null;
      outer: // 这就是标记
      for (var innerList in lists) {
        var sum = 0;
        for (var element in innerList) {
          assert(element >= 0);
          sum += element;
          // 无需继续迭代内部列表。它的总和已经太高了。
          if (sum > smallestSum) continue outer; // continue 跳出到标记处(outer)
        }
        smallestSum = sum;
        smallestList = innerList;
      }
      return smallestList;
    }
    

    此函数在所有list中运行,但只要总和过高,就会停止累加变量。

    同理,可以使用break跳出到外部循环:

    // 计算第一个非空list
    List<int> firstListWithNullValueList(List<List<int>> lists) {
      var firstListWithNullValues = null;
      outer:
      for (var innerList in lists) {
        for (var element in innerList) {
          if (element == null) {
            firstListWithNullValues = innerList;
            break outer;  // break 返回到标记处
          }
        }
      }
      // 现在继续正常的工作流程
      if (firstListWithNullValues != null) {
        // ...
      }
      return firstListWithNullValues;
    }
    
    • 2.跳出代码块

    标记也可以用于跳出代码块。假设我们想要统一处理错误条件,但有多个条件(可能是深度嵌套)来揭示(reveal)错误。标签可以帮助构建此代码。

    void doSomethingWithA(A a) {
      errorChecks: {
        if (a.hasEntries) {
          for (var entry in a.entries) {
            if (entry is Bad) break errorChecks;   // 跳出代码块
          }
        }
        if (a.hasB) {
          var b = new A.b();
          if (b.inSomeBadState()) break errorChecks;  // 跳出代码块
        }
        // 一些看起来都正常
        use(a);
        return;
      }
      // 错误的情况,执行这里的代码:
      print("something bad happened");
    }
    
    class A{
      bool hasEntries = false;
      bool hasB = true;
      List<Bad> entries = [new Bad2(),new Bad2()];
      A.b(){
    
      }
    
      bool inSomeBadState(){
        return false;
      }
      
    }
    
    void use(A a){}
    
    abstract class Bad{}
    class Bad1 extends Bad{}
    class Bad2 extends Bad{}
    

    对代码块的使用break指令,使得Dart继续执行块之后的语句。从某个角度来看,它是一个结构化的goto,它只允许跳转到当前指令之后的嵌套较少的位置。

    虽然声明标签在代码块中最有用,但它们可以用在在每个语句中。
    例如,foo: break foo;是一个有效的声明。

    请注意:continue上面的循环可以通过将循环体包装到带标记的代码块中并使用break来实现。
    也就是说,以下两个循环是等效的:

    //以下两种描述是等价的:
    
    // 使用 continue
    for (int i = 0; i < 10; i++) {
      if (i.isEven) continue;
      print(i);
    }
    
    // 使用 break.
    for (int i = 0; i < 10; i++) {
      labels: {
        // isEven 当且仅当该整数为偶数时才返回true
        if (i.isEven) break labels;
        print(i);
      }
    }
    
    • 3.Switch中的标记(label)

    标记也可以用于switch内部。
    Switch中的标记允许continue 使用其它的case 子句。在最简单的形式中,这可以作为一种方式来实现下一个子句:

    void switchExample(int foo) {
      switch (foo) {
        case 0:
          print("foo is 0");
          break;
        case 1:
          print("foo is 1");
          continue shared; // Continue使用在被标记为shared的子句中
        shared:
        case 2:
          print("foo is either 1 or 2");
          break;
      }
    }
    

    有趣的是, Dart没有要求continue的目标子句是当前case子句之后的子句。
    带标记的任何case子句都是有效的目标。这意味着,Dart的switch语句实际上是状态机(state machines)。

    以下示例演示了这种滥用,其中整个switch实际上只是用作状态机(state machines)。

    void main() {
      runDog();
    }
    
    void runDog() {
      int age = 0;
      int hungry = 0;
      int tired = 0;
    
      bool seesSquirrel() => new Random().nextDouble() < 0.1;
      bool seesMailman() => new Random().nextDouble() < 0.1;
    
      switch (1) {
        start:
        case 0:
          print("dog 方法已经开始");
          print('case 0 ==> age: $age');
          print('case 0 ==> hungry: $hungry');
          print('case 0 ==> tired: $tired');
          continue doDogThings;
    
        sleep:
        case 1:
          print("sleeping");
          tired = 0;
          age++;
          if (age > 20) break;
          print('case 1 ==> age: $age');
          print('case 1 ==> hungry: $hungry');
          print('case 1 ==> tired: $tired');
          continue doDogThings;
    
        doDogThings:
        case 2:  
          if (hungry > 2) continue eat;
          if (tired > 3) continue sleep;
          if (seesSquirrel()) continue chase;
          if (seesMailman()) continue bark;
          print('case 2 ==> age: $age');
          print('case 2 ==> hungry: $hungry');
          print('case 2 ==> tired: $tired');
          continue play;
    
        chase:
        case 3:  
          print("chasing");
          hungry++;
          tired++;
          print('case 3 ==> age: $age');
          print('case 3 ==> hungry: $hungry');
          print('case 3 ==> tired: $tired');
          continue doDogThings;
    
        eat:
        case 4:  
          print("eating");
          hungry = 0;
          print('case 4 ==> age: $age');
          print('case 4 ==> hungry: $hungry');
          print('case 4 ==> tired: $tired');
          continue doDogThings;
    
        bark:
        case 5: 
          print("barking");
          tired++;
          print('case 5 ==> age: $age');
          print('case 5 ==> hungry: $hungry');
          print('case 5 ==> tired: $tired');
          continue doDogThings;
    
        play:
        case 6: 
          print("playing");
          tired++;
          hungry++;
          print('case 6 ==> age: $age');
          print('case 6 ==> hungry: $hungry');
          print('case 6 ==> tired: $tired');
          continue doDogThings;
      }
    }
    

    这个函数从一个switch子句跳到另一个子句,模拟了狗的生命。
    在Dart中,标签只允许在case子句中使用,因此我必须添加一些case永远不会到达的行。

    这个功能很酷,但很少使用。由于我们的编译器增加了复杂性,我们经常讨论它的删除。到目前为止,它已经在我们的审查中幸存下来,但我们最终可能会简化我们的规范并让用户自己添加一个while(true)循环(带有标记)。这个dog的示例可以重写如下:

    var state = 0;
    loop:
    while (true)
      switch (state) {
        case 0:
          print("dog has started");
          state = 2; continue;
    
        case 1:  // sleep.
          print("sleeping");
          tired = 0;
          age++;
          // The inevitable... :(
          if (age > 20) break loop;  // 跳出循环
          // Wake up and do dog things.
          state = 2; continue;
        
        case 2:  // doDogThings.
          if (hungry > 2) { state = 4; continue; }
          if (tired > 3) { state = 1; continue; }
          if (seesSquirrel()) { state = 3; continue; }
          ...
    

    如果状态值被命名为常量,那么它将与原始版本一样具有可读性,但不需要switch语句来支持状态机。

    (三)while 和do while

    while(判断条件){
        内容体
    }
    
    do{
    内容体
    } while(判断条件);
    
    while(a>5){
        print(a);
    }
    
    do{
    print(a);
    } while(a>5);
    

    (四)break continue

    break 停止循环

    while(a>5){
      if(b>5){
      print(a);
        break;
      }
    }
    

    continue 跳到下一个循环

    while(a>5){
      if(b<10){
      print(b);
        continue;
      }
    }
    

    如果使用Iterable(list或者set),则可以使用下面这种方式:

    // 第一个是满足条件就进入  第二个是foreach遍历
    arrays
      .when((c)=>c.counts >=5)
      .foreach((c)=>c.next());
    

    (五)switch case

    比较integer, string,编译时常量 使用==。比较对象必须是同一个类的实例(不是其子类的实例),并且该类不能重写==。枚举类型在switch也可以运行。
    每一条非空case字子句以break结束,也可以使用其他的方式结束:continue,throw或者return

    var command = 'OPEN';
    switch (command) {
      case 'CLOSED':
        executeClosed();
        break;
      case 'PENDING':
        executePending();
        break;
      case 'APPROVED':
        executeApproved();
        break;
      case 'DENIED':
        executeDenied();
        break;
      case 'OPEN':
        executeOpen();
        break;
      default:
        executeUnknown();
    }
    

    (六)assert

    如果布尔条件为false,则使用assert语句来中断正常执行。例如:

    // 确保变量具有非空值 
    assert(text != null);
    // 确保值小于100
    assert(number < 100);
    // 确保这是一个 https 网址
    assert(urlString.startsWith('https'));
    

    要将消息附加到断言,请添加一个字符串作为第二个参数。

    assert(urlString.startsWith('https'),'URL ($urlString) should start with "https".');
    

    上例中assert的第一个参数可以是任何解析为布尔值的表达式。如果表达式的值为true,则断言成功并继续执行。如果为false,则断言失败并抛出异常


    七、异常

    Dart代码可以抛出并捕获异常。Exception是指示发生意外事件的错误。如果未捕获异常,则会暂停引发异常的isolate ,并且通常会终止isolate及其程序。

    与Java相比,Dart的所有异常都是未经检查的异常。方法不会声明它们可能引发的异常,并且您不需要捕获任何异常。

    Dart提供了ExceptionError 类型,以及许多预定义的子类型。当然,您可以定义自己的Exception。但是,Dart程序可以抛出任何非null对象,作为Exception(不仅仅是Exception和Error对象)。

    (一)throw

    以下是抛出或引发异常的示例:

    throw FormatException('Expected at least 1 section');
    

    你也可以抛出任意对象,例如:throw '格式不正确!';
    通常在开发中会抛出Error或者Exception类型。

    因为抛出异常是一个表达式,所以可以在=>语句中以及允许表达式的任何其他地方抛出异常:

    void distanceTo(Point other) => throw UnimplementedError();   
    

    (二)try catch

    捕获或捕获异常会阻止异常传递(除非您重新抛出异常)。捕获异常使您有机会处理它:

    try {
        breedMoreLlamas();
    } on OutOfLlamasException {
        buyMoreLlamas();
    }
    

    要处理可能抛出多种类型异常的代码,可以指定多个catch子句。与抛出对象的类型匹配的第一个catch子句处理异常。如果catch子句未指定类型,则该子句可以处理任何类型的抛出对象。
    您可以使用on或catch两者兼而有之。使用on时需要指定异常类型。使用catch时,你的异常处理程序需要异常对象。
    示例:

    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');
    }
    

    要部分处理异常,同时允许它传递,请使用rethrow关键字。
    示例:

    void misbehave() {
      try {
        dynamic foo = true;
        print(foo++); // 运行时异常
      } catch (e) {
        print('misbehave() partially handled ${e.runtimeType}.');
        rethrow; // 允许调用者查看exception.
      }
    }
    
    void main() {
      try {
        misbehave();
      } catch (e) {
        print('main() finished handling ${e.runtimeType}.');
      }
    }
    

    (三)finally

    无论是否抛出异常,要确保某些代码运行,请使用finally子句。如果没有catch子句匹配该异常,则在finally子句运行后传递异常。
    示例:

    try {
      breedMoreLlamas();
    } finally {
      // 即使抛出异常  也会执行这句代码.
      cleanLlamaStalls();
    }
    该finally子句在任何匹配的catch子句之后运行:
    try {
      breedMoreLlamas();
    } catch (e) {
        // 首先会处理异常
      print('Error: $e'); 
    } finally {
      // 然后执行这句语句
      cleanLlamaStalls(); 
    }
    

     

    作者写文章不容易,觉得写的不错的,就赞赏一下吧。感谢大家的支持。



    作者:AWeiLoveAndroid
    链接:https://www.jianshu.com/p/3d927a7bf020
    来源:简书
    简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

    展开全文
  • Flutter系列之Dart语法基础

    千次阅读 2018-10-20 22:40:47
    今天开始分享下Flutter移动开发的相关知识,下面是部分的Dart基础语法知识。 Dart中所有东西都是对象,包括数字、函数等, 它们都继承自Object,并且默认值都是null(包括数字)因此数字、字符串都可以调用各种方法...

    今天开始分享下Flutter移动开发的相关知识,下面是部分的Dart基础语法知识。

    Dart中所有东西都是对象,包括数字、函数等,
    它们都继承自Object,并且默认值都是null(包括数字)因此数字、字符串都可以调用各种方法。

    Dart中支持以下数据类型:

    • Numbers
    • Strings
    • Booleans
    • List(也就是数组)
    • Maps
    void main()
    {
      //Dart 语言本质上是动态类型语言,类型是可选的
      //可以使用 var 声明变量,也可以使用类型来声明变量
      //一个变量也可以被赋予不同类型的对象
      //但大多数情况,我们不会去改变一个变量的类型
      
      //字符串赋值的时候,可以使用单引号,也可以使用双引号
      var str1 = "Ok?";
      
      //如果使用的是双引号,可以内嵌单引号
      //当然,如果使用的是单引号,可以内嵌双引号,否则需要“\”转义
      //String str2 = ‘It\’s ok!’;
      String str2 = "It's ok!";
      
      //使用三个单引号或者双引号可以多行字符串赋值
      var str3 = """Dart Lang
      Hello,World!""";
      
      //在Dart中,相邻的字符串在编译的时候会自动连接
      //这里发现一个问题,如果多个字符串相邻,中间的字符串不能为空,否则报错
      //但是如果单引号和双引号相邻,即使是空值也不会报错,但相信没有人这么做
      //var name = 'Wang''''Jianfei'; 报错
      var name = 'Wang'' ''Jianfei';
      
      //assert 是语言内置的断言函数,仅在检查模式下有效
      //如果断言失败则程序立刻终止
      assert(name == "Wang Jianfei");
      
      //Dart中字符串不支持“+”操作符,如str1 + str2
      //如果要链接字符串,除了上面诉说,相邻字符串自动连接外
      //还可以使用“$”插入变量的值
      print("Name:$name");
      
      //声明原始字符串,直接在字符串前加字符“r”
      //可以避免“\”的转义作用,在正则表达式里特别有用
      print(r"换行符:\n");
      
      //Dart中数值是num,它有两个子类型:int 和 double
      //int是任意长度的整数,double是双精度浮点数
      var hex = 0xDEADBEEF;
          
      //翻了半天的文档,才找打一个重要的函数:转换进制,英文太不过关了
      //上面提到的字符串插值,还可以插入表达式:${}
      print("整型转换为16进制:$hex —> 0x${hex.toRadixString(16).toUpperCase()}");
    
    }

    上面列举一些常见的数据对象; 

    在声明变量的时候,除了var,还可以使用const和final,同时,在使用const和final的时候,可以省略var或者其他类型;

    const和final定义的都是常量,值不能改变,

    • const定义的是编译时常量,只能用编译时常量来初始化
    • final定义的常量可以用变量来初始化
    const list = const[1,2,3];//Ok
    const list = [1,2,3];//Error
    
    final list = [1,2,3];//Ok
    final list = const[1,2,3];//Ok
    final list = const[new DateTime.now(),2,3];//Error,const右边必须是常量

    函数定义:

    如果函数只是简单的返回一个表达式的值,可以使用箭头语法 =>expr;
    它等价于{return expr;}
    所以上面的函数也可以这样写

    sayHello(name) => 'Hello $name!';

    可选参数:

    Dart中支持两种可选参数:命名可选参数和位置可选参数
    但两种可选不能同时使用

    • 命名可选参数使用大括号{},默认值用冒号:
    • 位置可选参数使用方括号[],默认值用等号=

    在命名可选参数的函数中,大括号外的a为必填参数
    大括号内的参数可以指定0个或多个
    并且与顺序无关,在调用函数的时候需要指明参数名
    没有赋值的参数值为null

    FunX(a, {b, c:3, d:4, e})
    {
      print('$a $b $c $d $e');
    }

    在位置可选参数的函数中,大括号内的参数可以指定0个或多个
    在调用的时候参数值会依次按顺序赋值

    FunY(a, [b, c=3, d=4, e])
    {
      print('$a $b $c $d $e');
    }
    
    
    void main()
    {
      FunX(1, b:3, d:5);
      FunY(1, 3, 5);
    }

    本文图片资料来源出自“Dart语言中文社区”,允许转载,转载时请务必以超链接形式标明文章原始出处 。

     

     

    展开全文
  • Flutter是谷歌的移动应用SDK,用于在短时间内在iOS和Android上制作高质量的原生界面。 谷歌使用的是自家的语言dart(谷歌亲儿子,据说是因为Dart项目组就在flutter隔壁而被选上的zzzzzzz) 目前跨平台(移动端)的...
  • 1. dart 在线编辑器 一般一些简单的 dart 测试我们可以直接用在线编辑器来做测试和验证。 下面给大家介绍的两个都是官网的。 dart 在线运行器主页版:????... dart 在线运行器全屏版:?...左边如果为空返回...
  • Flutter 基础语法学习

    2020-05-28 19:50:38
    void main (List < String > args) { print("helloword"); //单行注释 /** * 多行注释 * */ //变量和数据类型 //Number : int double int score = 23; ...
  • 这段代码写在根目录\lib\main.dart文件中,这就是Flutter主文件. 基本思路: 引入flutter包 入口函数 声明MyApp类 重写build方法,它返回一个Material风格的组件 在Material组件的home属性里赋值一个Scaffold组件 ...
  • 一、语言特性 基本类型:bool,int,double。没有 byte、char 和 float final和java中一样,赋值不可再改变。const 表示一个编译时常量,在程序编译的时候它的值就确定了 Dart 里所有的东西都是对象,包括 int、...
  • iOS开发者入门Flutter 首先说一下,为什么要关心iOS和Flutter的区别问题。因为移动端开发的业务逻辑设计模式等是一致的,区别可能只在于使用的语言不同,实现逻辑的风格不同而已。所以这里我们先分析一下iOS和...
  • 基本语法
  • 04.Flutter之Dart基础语法(一)

    千次阅读 2020-03-29 02:27:17
    Flutter之Dart基础语法 一.代码运行 ide使用androidstudio 在Flutter项目的test目录下,新建并编写待执行的dart文件 然后右键run就可以运行了。(注意:需要连接手机或者模拟器) 二.注释 单行注释: 以’/...
  • 字符串 两个常量字符串(不是变量,是放在引号中的字符串),你不需要使用 + 来连接它们。 推荐的写法 print( 'ERROR: Parts of the spaceship are on fire.... 'parts are overrun by martians.... ...
  • dart语法

    千次阅读 2019-01-01 21:23:40
    文章目录编译时常量关于const 与 finalfinal实例成员的用法const实例成员的用法常量对象和常量构造函数异步Async和awaitFutureFuture与异常事件队列如何调度任务生成器同步生成器:sync*异步生成器:async* ...
  • Dart 是一种 易于学习、 易于扩展、并且可以部署到 任何地方 的 应用 编程 语言。
  • 解决vscode中开发flutter代码提示

    万次阅读 2019-07-22 22:57:42
    开始开发flutter,首先配置环境和编译器,是很重要的,根据flutter中文网或者官网提示,我们安装好一切开发环境,可以正常项目并运行,我们会遇到使用vscode的同事无法进行代码只能提示,我们只需要简单配置即可。...
  • Flutter 开发规范

    千次阅读 2020-02-05 09:55:03
    学习 Flutter 同样建议大家先了解掌握其开发规范,大致包括:项目结构规范、命名规范、缩进格式规范、注释规范、代码规范、其他规范。良好的开发规范不但能提升自己的编程水平及能力,有利于提升自己的开发效率,也...
  • Flutter的嵌套编码风格,让很多做web前端的同学感到很恶心,我看有人在github上向官方提问,官方表示并没有支持xml的计划。在github上搜索了一下,发现一个vscode-flutter.xml-layout库,其实现Angular风格编写...
  • VSCode 开发 Flutter 必用插件

    万次阅读 2019-04-25 20:19:41
    它支持调试,嵌入式 Git 控件,语法突出显示,智能代码补全,代码片段和代码重构。 同时也是支持自定义的,开发者可以更改编辑器的主题,键盘快捷键和首选项,拥有强大的拓展能力。 VSCode 的...
  • 使用 Android Studio 进行 Flutter 开发

    千次阅读 2019-11-01 12:34:58
    本文将与你一起回顾如何在 Android Studio 里进行 Flutter 工具的配置。创建项目你可以通过多种方式来创建新项目。创建新项目使用 Futter 应用模板...
1 2 3 4 5 ... 20
收藏数 4,460
精华内容 1,784
热门标签
关键字:

flutter 语法