Flutter Dart语法学习

作者: Miaoz0070 | 来源:发表于2020-05-10 10:54 被阅读0次
    Dart

    开发FlutterApp之前我们肯定要先了解Dart这门语言及语言的特性、语法等。最近看了大量的Dart语言相关内容,本章会来简述。
    目录

    • 概念及优点
    • 变量
    • 函数
    • 闭包
    • 异步支持
    概念及优点:
    • Dart:
      Google及全球的其他开发者,使用 Dart 开发了一系列高质量、 关键的 iOS、Android 和 web 应用。 Dart 非常适合移动和 web 应用的开发。
      1.高效
      Dart 语法清晰简洁,工具简单而强大。 输入检测可帮助您尽早识别细微错误。 Dart 拥有久经考验的 核心库(core libraries) 和一个已经拥有数以千计的 packages 生态系统
      2.快速
      Dart 提供提前编译优化,以在移动设备和 web 上实现可预测的高性能和快速启动。
      3.可移植
      Dart 可以编译成 ARM 和 x86 代码,因此 Dart 移动应用程序可以在 iOS,Android 及 更高版本上实现本地运行。 对于 web 应用程序,Dart 可以转换为 JavaScript。
      4.易学
      Dart 是面向对象的编程语言,语法风格对于许多现有的开发人员来说都很熟悉。了解Java、JS语言 ,使用 Dart 也就很简单,也有Swift的一些特性。
      5.响应式
      Dart 可以便捷的进行响应式编程。由于快速对象分配和垃圾收集器的实现, 对于管理短期对象(比如 UI 小部件), Dart 更加高效。 Dart 可以通过 Future Stream 的特性和API实现异步编程。
    变量
    • var
      自动推断类型(这点与OC、Java不同),接收任何类型的的变量,但是一旦赋值,类型就不能改变,即本来是字符串,之后就只能是字符串(这点与JS不同)。
    var a = "字符串";
    //主意:如果这样就会报错,类型在第一次指定后就不能改变
    a = 1;
    

    原因:Dart是强类型语言,任何变量都有各自的类型,编译时会根据首次赋值数据的类型来推断其类型,编译结束后其类型不能更改。JS是纯粹的弱类型脚本语言,var只是变量的声明。

    • dynamic
      dynamic与var一样都是关键词,声明的变量可以赋值任意类型对象。声明的变量可以在后期改变赋值类型。即本来是字符串,之后可以赋值为number等其他类型。
    dynamic a = "字符串";
    //不会报错
    a = 1;
    
    • Object
      Object与dynamic一样也是声明的变量可以赋值任意类型对象,声明的变量可以在后期改变赋值类型。
    Object b = "hello world";
    //不会报错
    b = 10;
    

    不同之处,dynamic声明的对象编译器会提供所有可能的组合,至少不会报错(但有可能运行时会因为找不到之前预制的组合,造成崩溃), 而Object声明的对象只能使用Object的属性与方法, 否则编译器会报错。

    dynamic a = "";
    Object b = "";
    //编译器不报错,不警告。
    print(a.length);
    //编译器会警告报错(Object没有length的getter方法):The getter 'length' is not defined for the class 'Object'
    print(b.length);
    

    注意:dynamic可以理解为id类型,任何类型都可以转换成id(dynamic)类型,可以用id(dynamic)去接,编译器不会报错,但是在运行时可能会产生错误出现崩溃现象。

    • final
      final 为运行时常量。
      final修饰的常量必须在声明的时候就进行初始化,而且在初始化之后值不可变,
    final a = "名字";
    //会报错
    a = "性别";
    
    • const
      const 为编译时常量。
      const不仅仅可以声明常数变量,也可以声明常量值以及声明创建常量值的构造函数,任何变量都可以有一个常量值;
    final aList = const[];
    const bList = const[];
    var cList = const[];
    
    这里的aList和bList就是两个空的、不可变的列表集合,而cList则是空的、可变的列表集合;
    需要注意的是:cList可以重新赋值,可以改变,而aList和bList不可以重新赋值;
    
    • 函数
      Dart是面向对象的语言,所以即使是函数也是对象,并且有一个类型Function。这意味着函数可以赋值给变量或作为参数传递给其他函数,这是函数式编程的典型特征。
      1.函数声明
    返回类型  方法体  (参数1,  参数2, ...){
        方法体...
        return 返回值
    }
    
    String getPerson(String name, int age){
      return name + '${age}';
    }
    
    //如果返回类型不指定时,此时默认为dynamic。
    
    

    2.箭头函数
    对于只包含一个表达式的函数,可以使用简写语法。

    getPerson(name,  age) => name+ ', $age' ; 
    
    bool isNoble (int atomicNumber)=> _nobleGases [ atomicNumber ] != null ;
    

    3.函数作为变量(方法对象)、入参

    //函数作为变量
    var  method1 = (str){
    print(str)
    };
    method1("kakalala");
    
    //函数作为参数
    void execute(var callbackMethod){
    callbackMethod();
    }
    //两种
    execute(() => print("xxx"));
    execute(method1("kakalala"));
    

    4.可选参数(可选位置参数、可选命名参数)

    • 可选位置参数:[param1, param2, ...],可以设置默认参数
      包装一组函数参数,用[]标记为可选的位置参数,并放在参数列表的最后面:
    getPerson(String name, [int age = 99, String gender = "御姐"]){
      print ("name = $name, age = $age, gender = $gender");
    }
    
    //getPerson() ;这种不传参是会报错的。
    getPerson(null) ;
    getPerson('不知火') ;
    getPerson('不知火', 100);
    getPerson('不知火', null,  "萝莉");
    
    

    控制台输出

    flutter: name = null, age = 99, gender = 御姐
    flutter: name = 不知火, age = 99, gender = 御姐
    flutter: name = 不知火, age = 100, gender = 御姐
    flutter: name = 不知火, age = null, gender = 萝莉
    

    注意:name参数是必须传入的,否则会报错。后边的可选位置参数如果不传会是null,传null还是会返回null。可选位置参数可以设置默认参数。

    • 可选命名参数:{param1, param2, ...}
      在传入的时候,需要指定下对应的参数名,放在参数列表的最后面,用于指定命名参数。可以设置默认参数。
    getPerson(String name, {int age = 100, String gender = "狼狗"}){
      print("name = $name, age = $age, gender = $gender");
    }
    
    //getPerson() ;这种不传参是会报错的。
    getPerson(null) ;
    getPerson('烬天玉藻前') ;
    getPerson('烬天玉藻前', age: 99 );
    getPerson('烬天玉藻前', gender: "御姐" );
    getPerson('烬天玉藻前', age: 99, gender: "奶狗");
    

    控制台输出:

    flutter: name = null, age = 100, gender = 狼狗
    flutter: name = 烬天玉藻前, age = 100, gender = 狼狗
    flutter: name = 烬天玉藻前, age = 99, gender = 狼狗
    flutter: name = 烬天玉藻前, age = 100, gender = 御姐
    flutter: name = 烬天玉藻前, age = 99, gender = 奶狗
    

    注意:固定参数必须传入(那怕传个null),可选命名参数可以设置默认参数。

    • 默认参数值
      默认参数值即我们在方法的参数列表上面使用 “=” 号给入一个常量值,如果没有传入该值的时候,就使用我们给入的常量值。
    注意,不能同时使用可选的位置参数和可选的命名参数
    //这种是不可以的,错误事例。
    getPerson(String name, {int age = 100, String gender = "狼狗"}, [int age2 = 1002, String gender2 = "狼狗2"]){
     
    }
    
    闭包

    闭包是一个方法(对象),闭包定义在其它方法内部,能够访问外部方法的局部变量,并持有其状态。

    void main() {
    
        // 创建一个函数add1,返回加2
        Function add1 = addNum(2);
        
        // 创建一个函数add2,返回加4
        Function add2 = addNum(4);
    
        // 2 + 3 = 5
        print(add1(3));
        // 4 + 3 = 7
        print(add2(3));
    }
    
    // 返回一个函数对象,功能是返回累加的数字
    Function addNum(int addBy){
        return (int i) => addBy + I;
    }
    

    控制台输出:

    flutter: 5
    flutter: 7
    
    异步支持

    Dart代码运行在一个单线程,如果Dart代码阻塞了---例如,程序计算很长时间,或者等待I/O,整个程序就会冻结。
    Dart异步函数:Future、Stream,设置好耗时操作后返回,不会阻塞线程。
    async和await关键词支持了异步编程,允许写出和同步代码很像的异步代码。

    Future

    Future与JS中的Promise和Swift的RXSwift非常相似,其语法也是链式函数调用,该函数异步操作执行后,最终返回成功(执行成功的操作)、失败(捕获错误或者停止后续操作),失败和成功是对立的只会出现一种。
    注意:Future 的所有API的返回值都是一个Future对象,所以可以进行链式调用。

    • Future构造函数

    Future(FutureOr<T> computation())
    computation 的返回值可以是普通值或者是Future对象,但是都是Future对象接收。

     Future<num> future1 = Future(() {
      print('async call1');
      return 123;
    });
    //直接调用
    future1.then((data) {
      //执行成功会走到这里
      print(data);
    }, onError: (e) {
      print("onError: \$e");
    }).catchError((e) {
      //执行失败会走到这里
      print(e);
    }).whenComplete(() {
      //无论成功或失败都会走到这里
    });
    
    Future<Future> future2 = Future((){
        print('async call2');
        return future1;
    });
    
    //嵌套调用
    future2.then((value) => value).then((value) => {
       print('222---'+value.toString())
    });
    

    控制台打印

    Reloaded 1 of 499 libraries in 154ms.
    flutter: async call1
    flutter: 123
    flutter: async call2
    flutter: 222---123
    

    注意:computation函数体中的代码是被异步执行的,与JS中Promise构造函数的回调执行时机不一样,如需要被同步执行,则使用如下这个命名构造函数:

    Future.sync(FutureOr<T> computation())

    //该段代码放到上边代码之后执行
    Future<num> future3 = Future.sync((){
        print('sync call');
        return 333;
    });
    
    future3.then((value) => {
       print('sync'+'$value')
    });
    

    控制台输出

    flutter: sync call
    flutter: sync333
    flutter: async call1
    flutter: 123
    flutter: async call2
    flutter: 222---123
    

    由此可见,future3(sync)方法会先执行,之后在执行之前的future1、future2.可见正常的future中的computation函数体中的代码是被异步执行的。

    • Future.then
      then中接收异步结果
    Future.delayed(new Duration(seconds: 2),(){
       return "延迟2s执行";
    }).then((data){
       print(data);
    });
    
    • Future.catchError
      捕获错误
    Future.delayed(new Duration(seconds: 2),(){
       //return "延迟2s执行";
       throw AssertionError("Error");  
    }).then((data){
       //执行成功会走到这里  
       print("success");
    }).catchError((e){
       //执行失败会走到这里  
       print(e);
    });
    

    在异步任务中抛出了一个异常,then的回调函数将不会被执行, catchError回调函数将被调用;并不是只有 catchError回调才能捕获错误,then方法还有一个可选参数onError(之前介绍结构体时已经提到),我们也可以它来捕获异常:

    Future.delayed(new Duration(seconds: 2), () {
        //return "延迟2s执行";
        throw AssertionError("Error");
    }).then((data) {
        print("success");
    }, onError: (e) {
        print(e);
    });
    
    • Future.whenComplete
      不管成功失败都要处理事件的场景,会调用此方法,比如在网络请求前弹出加载对话框,在请求结束后关闭对话框。这种场景,有两种方法,第一种是分别在then或catch中关闭一下对话框,第二种就是使用Future的whenComplete回调:
    Future.delayed(new Duration(seconds: 2),(){
       //return "延迟2s执行";
       throw AssertionError("Error");
    }).then((data){
       //执行成功会走到这里 
       print(data);
    }).catchError((e){
       //执行失败会走到这里   
       print(e);
    }).whenComplete((){
       //无论成功或失败都会走到这里
    });
    
    • Future.wait
      需要等待多个异步任务都执行结束后再统一进行一些操作(比如我们有一个界面,需要先分别从两个网络接口获取数据,获取成功后,我们需要将两个接口数据进行特定的处理后再显示到UI界面上)
      Future.wait就是做这件事的(类似RXswift的zip函数),它接受一个Future数组参数,只有数组中所有Future都执行成功后,才会触发then的成功回调,只要有一个Future执行失败,就会触发错误回调。
    Future<List<Future>> future4 = Future.wait([
     // 2秒后返回结果  
     Future.delayed(new Duration(seconds: 2), () {
       return "延迟2s执行";
     }),
     // 4秒后返回结果  
     Future.delayed(new Duration(seconds: 4), () {
       return " 延迟4s执行";
     })
    ])
    
    future4.then((value) => {
    print(value[0]+ value[1]);
    }).catchError((e){
     print(e);
    });
    

    控制台输出

    //等待4s输出
    flutter: 延迟2s执行 延迟4s执行
    
    • 回调地狱(Callback Hell)
      代码中有大量异步逻辑,并且出现大量异步任务依赖其它异步任务的结果时,必然会出现回调中套回调情况。我们需要使用async/await和Future.then来解决这种问题。
      使用场景:大量依赖的业务逻辑,登录流程逻辑等
      先来看下回调地狱例子:
    //先分别定义各个异步任务
    Future<String> future1(String str1){
        ...
    //第一个任务
    };
    Future<String> future2(String str2){
        ...
    //第二个任务
    };
    Future future3(String info){
        ...
        // 第三个任务
    };
    
    future1("str1").then((str2){
     //1返回数据,作为2的参数
     future2(str2).then((info){
        //2的返回数据,作为3的入参
        future3(info).then((){
           //获取3的返回数据
            ...
        });
      });
    })
    

    Future消除Callback Hell
    使用Future的链式机制,依次向下就避免了嵌套。跟JS的Promise完全一样。不足之处是还是有一层回调。

    future1("str1").then((str2){
          return future2(str2);
    }).then((info){
        return future3(info);
    }).then((e){
       //执行3接下来的操作 
    }).catchError((e){
      //错误处理  
      print(e);
    });
    

    async/await消除callback hell
    上边的方式虽然避免了嵌套,但是在每个方法还是有一层回调。我们可以使用async/await来实现像同步代码那样来执行异步任务而不使用回调的方式。

    task() async {
       try{
        String str2 = await future1("str1");
        String info = await future2(str2);
        await future3(info);
        //执行接下来的操作   
       } catch(e){
        //错误处理   
        print(e);   
       }  
    }
    
    • async/await
      async用来表示函数是异步的,定义的函数会返回一个Future对象,可以使用then方法添加回调函数
      await 后面是一个Future,表示等待该异步任务完成,异步完成后才会往下走;await必须出现在 async 函数内部。
      async/await将一个异步流用同步的代码表现出来。

    async/await只是一个语法糖,JS编译器或Dart解释器最终都会将其转化为一个JS的Promise和Dart的Future的调用链。

    Stream

    如果说Future是可以接收单个异步事件返回单个事件的成功失败,那么Stream就可以接收多个异步事件,并返回多个事件的成功失败,供使用者使用。
    使用场景:多次读取数据的异步任务场景,如网络内容下载、文件读写等
    该例子借助了其他地方的例子。

    Stream.fromFutures([
      // 1秒后返回结果
      Future.delayed(new Duration(seconds: 1), () {
        return "hello 1";
      }),
      // 抛出一个异常
      Future.delayed(new Duration(seconds: 2),(){
        throw AssertionError("Error");
      }),
      // 3秒后返回结果
      Future.delayed(new Duration(seconds: 3), () {
        return "hello 3";
      })
    ]).listen((data){
       print(data);
    }, onError: (e){
       print(e.message);
    },onDone: (){
    
    });
    

    控制台输出

    flutter: hello 1
    flutter: Error
    flutter: hello 3
    

    Future.wait是函数中存在多个延时操作,则以延时最长操作完成后统一返回,其他的延时操作等待最长的延时操作完成。
    async/await:处理多个异步操作,前后有依赖逻辑的,使用异步实现同步。
    Stream:统一监听该Stream中的多个异步延时操作返回,相当于之前的多个Future异步处理统一监听。

    • 其中Future和Stream只做了常用的方法和函数的介绍,更详细的会在之后依次给大家做下总结。

    到这里大概把Dart中经常使用的语法和属性方法介绍了一遍,有错误或者理解不到位的地方,可以提出,共同进步。

    Dart相比Java和JavaScript还是有许多优点有优势的,Dart既能进行服务端脚本、APP开发、web开发,但是生态目前不足,不过Flutter目前火热,相信生态之后会越来越好。

    相关文章

      网友评论

        本文标题:Flutter Dart语法学习

        本文链接:https://www.haomeiwen.com/subject/jnjnwhtx.html