美文网首页Flutter圈子
Flutter的优势以及Dart基础语法

Flutter的优势以及Dart基础语法

作者: 86050d85fb4b | 来源:发表于2020-03-15 12:19 被阅读0次

    1.为什么要学习Flutter?

    对于移动端开发人员来说,跨平台技术一直是关注的重点,从H5,React Native到Flutter,我们似乎一直在寻找一种能“一套代码,多端运行”,同时还能有不俗的用户体验的技术。对于当前的大前端来说,React Native的综合成熟度和生态都要比Flutter好一些,对于中短期项目我们可能会选用前者,但是对于更长期的项目和发展来说,Flutter是一种更彻底的解决方案,渲染能力和平台一致性以及性能,它都具有更大的优势。

    • 我们从Web容器时代,比如H5,Cordova,微信小程序,Ionic,这种基于web相关技术通过浏览器组件来实现界面及其功能,在android上是WebView,IOS上为UIWebView,这种采用原生内嵌的浏览器来进行渲染,虽然可以做到多端一致,对于用户体验很是有限,尤其是一些复杂的UI逻辑。

    • 对于泛Web容器时代来说,代表的技术有React Native和Weex。基本上是完全放弃了浏览器控件的渲染,而是采用原生自带UI组件实现代替了核心的渲染引擎,仅仅保持必要的基本控件渲染能力,简化渲染,保证了良好的渲染性能。这个时代总体来说是做一个平衡,依然采用了前端友好的Js进行开发,原生来接管绘制,依托于Js虚拟机的Js代码提供所需的UI控件的实体。

      但是对于这种映射来说,在维护各个平台的API升级方面往往需要巨大的成本,对于Android/Ios平台上使用React Navie,对开发人员的要求不仅仅是懂React那么简单,而是还需要懂两个平台的一些基础开发知识。

    • 接下来是自绘引擎时代,即从头到尾写一套跨平台的UI框架,包括渲染逻辑甚至开发语言。所以对于移动端的开发人员来说,其实还是有一定的学习成本的(但对于已掌握一门编程语言的人几乎可以忽略不计),但从平台一致性,维护成本,性能以及开发效率上都是全方位碾压之前两个时代的技术。

      1.Dart语言同时支持JIT和AOT。开发周期使用JIT,大大缩短开发周期,调式模式支持有状态的热重载;而发布期使用AOT,本地代码的执行更高效,代码性能和用户体验也更优秀。Dart避免了抢占式调度和共享内存,可以在没有锁的情况下进行对象分配和垃圾回收,在性能方面表现很是卓越。

      2.渲染引擎依靠跨平台的Skia图像绘制引擎,Skia将使用Dart构建的抽象的视图结构结构数据加工成GPU数据,然后数据通过OpenGL最终提供给GPU渲染,因为安卓上已经有Skia引擎了,这也是Flutter Android SDK 要比 Flutter iOS SDK 小一些的原因,打包后的apk也会比ipa小

    明白了为什么要学习Flutter,我们就更能坚定我们的脚步,要学好Flutter,就要先学习它的开发语言Dart,下面我们来看看Dart基础语法:

    2.Dart基础语法

    数据类型及其相关
    含义 使用
    int 整数,范围为 -2^63 到 2^63 - 1. int x = 1;//没有小数点就是int
    double 浮点数,64位(双精度)浮点数 double y = 1.1;//有小数点就是浮点数
    num num 是数字类型,既可以表示整数,也可以表示浮点数,具体看赋的值 num x = 1;//num x是整数 num y = 1.1;//num y是浮点数
    String 字符串 Dart字符串采用UTF-16编码 可以使用单引号或双引号来创建字符串 var s1 = 'string'; var s2 = "string";字符串拼接采用“+”
    bool 布尔值 var isTrue = true;
    List List<E> E 表示 List 里的数据类型 用中括号来赋值 List<int> list = [1, 2, 3];
    Set Set<E> E 表示 Set 里的数据类型 用大括号来赋值 Set<String> halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
    Map Map<K, V> K 是 Key 的数据类型,V是 Value 的数据类型 Map<String,String> gifts = { // Key: Value 格式 'first': 'partridge', 'second': 'turtledoves', 'fifth': 'golden rings'};
    Runes 表示采用 UTF-32 的字符串,用于显示 Unicode 因为Dart字符串是UTF-16,因此在Dart中表示32位的Unicode值需要Runes这个特殊语法。 Runes input = new Runes('\u{1f600}'); print(new String.fromCharCodes(input)); 打印出来是笑脸emoji:😆
    • 在Dart中,所有类型都是对象类型,都继承自顶层类型Object,因此一切变量都是对象,数字,布尔值,函数和null都不能例外。

    • 未手动赋值的变量的值都是null,比如定义一个全局变量 int a,不进行手动赋值,打印出来a的值为null。

    • 默认定义的变量都是public的,加个“_”则为private,限制范围并不是类访问级别的,而是库访问级别。

    • 我们可以用var来声明变量而不需要指定特别的数据类型,但是一旦赋值后就不能改变数据类型的,而使用dynamic关键字就可以。

      1.为什么var能定义所有变量?因为var 并不是直接存储值,而是存储的值的对象的引用,例如:var content = 'Dart 语法' 这句,是名字为 content 的 var 变量存储了值为 'Dart 语法' 的 String 对象的引用,所以 var 才能定义任何变量。

      2.什么情况下用dynamic呢?当这个变量没法用 Dart 的类型来表示时,比如 Native 和 Flutter 交互,从 Native 传来的数据。所以你会看到 PlatformChannel 里有很多地方使用到了 dynamic。

    • final和Java中的final差不多,使用final和const的时候可以把var省略,使用const的时候如果是类里的变量就必须加static,全局变量时不需要的。在使用构造器的时候也可以使用const变量,但是这个时候就不需要方法体了。const是编译时常量,在编译时就进行初始化了,但是final变量是当类创建的时候才初始化

    函数
    • Dart不支持方法重载,而是提供了可选命名参数和可选参数:

      给参数增加{},以 paramName: value 的方式指定调用参数,也就是可选命名参数;

      给参数增加[],则意味着这些参数是可以忽略的,也就是可选参数。

    //要达到可选命名参数的用法,那就在定义函数的时候给参数加上 {}
    void test1Flags({bool bold, bool hidden}) => printTest("$bold , $hidden");
    
    //定义可选命名参数时增加默认值
    void test2Flags({bool bold = true, bool hidden = false}) => printTest("$bold ,$hidden");
    
    //可忽略的参数在函数定义时用[]符号指定
    void test3Flags(bool bold, [bool hidden]) => printTest("$bold ,$hidden");
    
    //定义可忽略参数时增加默认值
    void test4Flags(bool bold, [bool hidden = false]) => printTest("$bold ,$hidden");
    
    //可选命名参数函数调用
    test1Flags(bold: true, hidden: false); //true, false
    test1Flags(bold: true); //true, null
    test2Flags(bold: false); //false, false
    
    //可忽略参数函数调用
    test3Flags(true, false); //true, false
    test3Flags(true,); //true, null
    test4Flags(true); //true, false
    test4Flags(true,true); // true, true
    

    但是不可以同时把这符号同时放到同一个函数声明下面,比如:

    void testFun({int a},[int b]){} //错误的写法
    
    • 对于只有一行的函数可以用 =>来进行简写
    void main() => runApp(MyApp());
    等价于
    void main(){
        return runApp(MyApp());//runApp() 返回的是 void
    }
    
    • Dart还提供了命名构造函数的方式,使得类的实例化过程语义更加清晰。如下面实例,Point类中有两个构造函数,TestA.bottom和TestA
    class TestA {
      num x, y, z;
      
      TestA(this.x, this.y) : z = 0; // 初始化变量z
      TestA.bottom(num x) : this(x, 0); // 重定向构造函数
      TestA.top():x=3; //重定向构造器
      
      void printInfo() => print('($x,$y,$z)');
    }
    
    var p = TestA.bottom(100);
    p.printInfo(); // 输出(100,0,0)
    var p = TestA.top();
    p.printInfo(); // 输出(3,0,0)
    

    对于这种写构造器的方式,和我们用Java自定义控件构造器嵌套有着异曲同工之妙。

    复用(extends,implement,mixin,on)

    • 在Dart中,你可以对同一个父类进行继承或接口实现,但不可同时继承和实现。
    class TestA{
      num x,y,z;
    
      TestA(this.x,this.y):z =0;
      TestA.bottom(num x):this(x,0);
      TestA.top():x=3;
      
      void testCall() => print("($x=======,$y======)");
    
    }
    
    class TestB extends TestA{
      TestB(num x, num y) : super(x, y);
      
      @override
      void testCall() { //重写testCall的实现
        // TODO: implement testCall
        super.testCall();
      }
    }
    
    class TestC implements TestA{
      @override
      num x;
    
      @override
      num y;
    
      @override
      num z;
    
      @override
      void testCall() {  //必须实现该方法
        // TODO: implement testCall
      }
    }
    

    可以看到,对于接口的实现方式,我们只是拿到了TestA的一个空壳子,不能复用原有的实现,这个时候,我们可以用混入(Mixin),混入鼓励代码重用,可以视为具有实现方法的接口,不仅可以解决Dart缺少多重继承的问题,同时也能避免多重继承可能导致的歧义(菱形问题)。要使用混入,我们需要使用with关键字,但是对于混入也有一定的要求,被混入的类不能有自己的构造器,否则就会提示错误。

    class D{
      
      num x,y;
      D(this.x,this.y);
      void testCall() => print("($x=======,$y======)");
    }
    
    class E with D{  //编译失败,提示D类不能被混入,因为已经定义了构造器。
      
    }
    

    至于为什么不能能用构造器,我觉得Mixin目前的实现是以完全忽略构造函数,忽略构造函数调用链的方式实现的,因此只支持隐式的构造函数。一旦放开mixin的构造函数支持,Dart势必需要支持一套新的关键字语法,来支持Mixin类继承链上的构造转发,代价太大。

    • mixin还可以和on进行配合使用**,如果一个类mixin某个类,就必须先实现on的那个类,详情可以看看下面的代码:
    class F{
    
    }
    
    mixin G on F{
    
    }
    
    class H extends F with G{ //必须先继承F类才可以,如果F是接口,那就必须先实现接口F
    
    }
    
    运算符

    这里列出一些常用的表达,过于简单的就不列出来了:

    操作符 含义 例子
    as 类型转换 (emp as Person).firstName = 'Bob';<br />如果emp为null,抛出异常
    is 判断是否是某个类型,如果是的话,就返回 true if (emp is Person) {
    // 如果 emp 是 Person 类型
    emp.firstName ='Bob';
    }<br />如果emp 为null ,返回false
    is! 判断是否不是某个类型,如果不是的话,就返回 true if (emp is! Person) {
    // 如果 emp 不是 Person 类型
    } <br /><br /如果emp 为null ,返回false
    ?. 类似于kotlin中的空安全,为空跳过下面的逻辑,避免抛出异常
    ??= 用默认值兜底的一种方式 a??=value,如果a为null,则给a赋值value,否则跳过
    ?? 类似于三元表达式 a ?? b 类似于(a != null)? a : b
    .. 级联操作符,允许你对同一对象进行一系列的操作。 querySelector('#confirm') // Get an object.
    ..text = 'Confirm' // Use its members.
    ..classes.add('important')
    ..onClick.listen((e) => window.alert('Confirmed!'))
    • 运算符重载
      我们可以通过运算符重载来实现更复杂的功能
    // 自定义相加运算符,实现向量相加 
    Vector operator +(Vector v) => Vector(x + v.x, y + v.y); 
    // 覆写相等运算符,判断向量相等 
    bool operator == (dynamic v) => x == v.x && y == v.y;
    

    3.写在最后

    最近在研究Flutter,写这篇文章也是做一个简单的终结,希望更多的人加入Flutter的开发当中,最近开了一个淘宝店铺,店铺名字叫“程哥的日用百货铺”,这里附上链接 https://shop105359436.taobao.com/?spm=a1z10.1-c-s.0.0.c1b42dd2nh1YwP ,希望大家多多支持!

    掘金链接:https://juejin.im/post/5e6ca09d6fb9a07c89151eda

    相关文章

      网友评论

        本文标题:Flutter的优势以及Dart基础语法

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