V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
winlee28
V2EX  ›  Android

Flutter 开发必备 Dart 基础: Dart 快速入门

  •  
  •   winlee28 · 2019-12-06 13:50:31 +08:00 · 10702 次点击
    这是一个创建于 1848 天前的主题,其中的信息可能已经有所发展或是发生改变。

    本文首发于微信公众号「 Android 开发之旅」,欢迎关注 ,获取更多技术干货

    概述

    Dart 从 2.0 开始变为强类型语言,静态类型。这点和 Java、C#等比较相似。也就是说在编译时就已经知道变量的类型那么就是静态类型语言。开发人员在开发的时候需要指定变量的类型。这有什么优点呢? 就是所有类型检查都可以通过编译器来完成。可以提前预报一些琐碎的错误。 同时 Dart 还是面向对象的编程语言。像 python、Java、Koltin、PHP 等等都是面向对象的语言。

    dart 的特性: JIT:即时编译,开发期间做到更快的编译和更快的代码重载。 但也有劣势就是在运行时需要将代码编译为机械码,那么直观感受就是慢,所以我们在开发期间有时候会发现卡顿,但是打 release 包之后效果会变好的原因。 AOT:事前编译,release 期间已经编译为二进制代码,所以加载会更快更流畅。

    常用数据类型

    任何语言的基础都是数据类型,dart 也一样。dart 中主要有数字、字符串、布尔、集合和 var 类型。

    num 类型

    num 是 dart 中的数字类型的父类,它有两个子类:int 和 double。当类型指定为 num 的时候可以赋值为小数也可以赋值为整数,但是一旦指定了某一个具体的类型,就只能赋值这一类型的值。

      void _numType() {
        num num1 = -2.0; //定义为小数
        num num2 = 4; //定义 num 为你太
        int num3 = 5; //只能是整数
        double num4 = 6.0; //只能是双精度
      }
    

    其中这些类型之间是可以相互转换的,如:

        print(num1.abs()); // 取绝对值 输出 2.0
        print(num2.toDouble()); // 转为小数 输出 4.0
        print(num3.toString()); // 转为字符串 输出"5"
        print(num4.toInt()); // 转为整数 输出 6
    

    String 类型

    String 的定义也比较简单,它可以使用单引号也可以使用双引号定义,定义一个 String 类型后面可以通过逗号隔开定义多个变量。在 dart 中拼接字符串可以是 + 号链接,还可以使用 $ 符号进行拼接,如果是变量则直接使用$xx,如果是表达式则使用${xxx}将其包裹起来。

      void _stringType() {
        String str1 = 'str1', str2 = "str2"; //定义字符串 str1 和 str2
        String str3 = "字符串数据类型"; //定义字符串 str3
        String str4 = 'str1=$str1;str2=$str2'; //通过$符拼接字符串
        String str5 = "str1=" + str1 + " str3=" + str3; //通过+号拼接字符串
      }
    

    字符串还有一些常用的 API,比如字符串截取,获取指定字符串位置,匹配字符串开头等等。

        print(str3.substring(1, 5)); //符串数据 输出 符串数据
        print(str3.indexOf("数据")); // 获取位置 输出 3
        print(str3.startsWith("字")); //匹配起始字符串 true
        print(str3.replaceAll("字符串", "dart 的 String")); //替换 输出 dart 的 String 数据类型
        print(str3.split("数")); //切够字符串 输出[字符串,据类型]
    

    布尔类型

    dart 中布尔类型是强 bool 类型,只有 bool 类型的值为 true,才被认为是 true。bool 还有一些常用的运算符比如 ||(或) 或者 &&(且) ,在运算的时候,教大家一个口诀就是,且同真为真,或同假为假。

      void _boolType() {
        bool success = true; //定义 bool 值为 true
        bool failure = false; //定义 bool 值为 false
    
        print(success || failure); //或运算 输出 true
        print(success && failure); //且运算 输出 false
      }
    

    集合类型

    dart 中定义集合直接使用 List list=[],如果没有指定泛型,默认类型为 dynamic 类型,集合中可以添加任何数据类型的数据,一旦指定了泛型,那么就只能添加约束的类型数据。除了初始化的时候添加元素另外还可以通过 API 的方式像集合中添加数据,add 添加单一数据,addAll 添加集合。还有另外一种方式是通过 List.generate 来生成集合,它的第一个参数表示集合的大小,第二个参数表示集合的元素。

      void _listType() {
        List list = [1, 2, 3, "list"]; //泛型为 dynamic
        //报 type 'List<dynamic>' is not a subtype of type 'List<int>' 异常
        //因为指定了 intList 的泛型为 int 但是 list 的泛型为 dynamic,随意赋值失败
    	//List<int> intList = list;
        List<int> intList2 = [1, 2, 3, 4, 5]; //定义 int 集合
        list.add("hello"); //添加单一元素
        list.addAll(intList2); //添加集合
        print(list); //输出 [1, 2, 3, "list","hello",1, 2, 3, 4, 5]
    
        List<String> strList = List.generate(2, (index)=> "我是第$index 个元素"); //通过 generate 定义一个 String 集合
        print(strList); //输出 [我是第 0 个元素, 我是第 1 个元素]
      }
    

    有了集合那么我们就需要遍历它,dart 中常用的遍历集合方式有:

        for (int i = 0; i < list.length; i++) {
          print(list[i]);
        }
    
        for (var data in list) {
          print(data);
        }
    
        list.forEach((it) {
          print(it);
        });
    

    上面三种遍历方式输出结果都为:

    1, 2, 3, "list","hello",1, 2, 3, 4, 5
    

    Map 类型

    Map 类型是将 key 和 value 相关联的对象,key 和 value 可以是任意类型的数据,并且 key 是唯一的,如果 key 重复那么后添加的会覆盖之前添加的数据。定义 map 类型直接看代码:

      void _mapList() {
        Map map = {"lisi": 20, "zhangsan": 24}; //直接通过 {key:value} 方式定义
        Map map2 = {};
        map2[11] = 20;
        map2["zhangsan"] = "24";
        //上面两个效果是一样的且都没有指定泛型。
    
        Map<int, int> intMap = {1: 2, 3: 4}; //指定 map 的泛型
      }
    

    下面我们看下 Map 的遍历:

        map.forEach((key, value) {
          print(
              "key:$key,value:$value"); //输出 key:lisi,value:20 和 key:zhangsan,value:24
        });
    
        map.map((key, value) {
          return MapEntry(value, key); //返回一个新的 map 对象,我们将 key value 值进行颠倒后返回
        }).forEach((key, value) {
          print("key:$key,value:$value"); //输出 key:20,value:lisi 和 key:24,value:zhangsan
        });
    
        for(var key in map.keys){ //遍历 map 的 key 元素 同理还可以遍历 map.values
          print(key); //输出 lisi 和 zhangsan
        }
    

    dynamic、var、Object 的区别

    dynamic:动态数据类型,是所有 Dart 对象的基础类型, 在大多数情况下,通常不直接使用它,通过它定义的变量会关闭类型检查,这意味着 dynamic x = 'hello world'; 调用 x.foo()方法时静态类型检查不会报错,但是运行时会 crash,因为 x 并没有 foo()方法,所以建议大家在编程时不要直接使用 dynamic。

    var:是一个关键字,意思是“我不关心这里的类型是什么。”,系统会自动推断类型 runtimeType ;而且一旦指定了类型,就不可以修改。

    Object:是 Dart 对象的基类,当你定义:Object obj=xxx 时这时候系统会认为 obj 是个对象,你可以调用 obj 的 toString()和 hashCode()方法,因为 Object 提供了这些方法,但是如果你尝试调用 obj.foo()时,静态类型检查会进行报错。

    综上不难看出 dynamic 与 Object 的最大的区别是在静态类型检查上。

    面向对象

    类的定义和构造

    定义一个 dart 类,使用 class 关键字加上类名,构造方法和类名相同,默认所有的类都是继承 Object 的。其中可以定义一些方法和变量,如:

    class Person {
      String name;
      int age;
    
      //标准构造方法
      Person(this.name, this.age);
      
      //重载父类的 toString 方法 也是多态的重要体现
      @override
      String toString() {
        return "name=$name,age=$age";
      }
    }
    

    一个类继承另一个类使用关键字 extends,如果父类没有默认无参构造函数,那么子类需要使用 super 对父类进行初始化,子类的变量使用 this.xxx 来指定初始化,如:

    class Worker extends Person {
      
      String workType;
      String workAddress;
      //通过 this 来初始化子类字段,将其他字段交由父类初始化
      Worker(this.workType,this.workAddress,String name, int age) : super(name, age);
    }
    

    上面代码中的 :super(name,age) 被称作为初始化列表,除了调用父类构造器,还可以初始化实例变量,不同的初始化变量之间用逗号隔开。

    class Worker extends Person {
    
      String workType;
      String workAddress;
      String companyName;
    
      //通过 this 来初始化子类字段,将其他字段交由父类初始化,这里指定了 companyName 的初始化。
      //如果要初始化变量,那么其不可以在构造方法中定义
      Worker(this.workType, this.workAddress, String name, int age)
          : companyName = "world",
            super(name, age);
    }
    

    我们还可以通过命名构造函数的方式初始化实例类。使用就是 类名.xx() 即可,主要注意的是当有变量的类型是 final 的时候,命名构造方法就要求其在构造方法中指定初始化。

    class Worker extends Person {
    
      ......
    
      //命名构造函数
      Worker.begin(Worker work) : super(work.name, work.age) {
        print("命名构造函数");
      }
      
      //假如有变量 final String workAddress,那么就需要在构造方法中指定:
      //Worker.begin(Worker work,this. workAddress) : super(work.name, work.age) {
      //  print("命名构造函数");
      //}
      
    }
    

    工厂构造函数就是都只返回同一个实例类,可以理解为 Java 中的单例模式。

    class Logger {
      static Logger _logger;
    
      factory Logger() {
        if (_logger == null) {
          _logger = Logger._initInstance();
        }
        return _logger;
      }
    
      //通过命名构造函数初始化
      Logger._initInstance();
    }
    

    还有一种构造方法是命名构造和工厂构造的结合体,通常在网络请求的时候将 json 映射为 object 的时候使用,它有一个好处就是不需要将类的 final 变量作为参数传递给构造方法。提供了一种灵活获取类对象的方式。定义格式:factory 类名.方法名。

    class Worker extends Person {
     
      ......
     
      factory Worker.workHard(Worker work) {
        return Worker.begin(work, "");
      }
    }
    

    dart 默认都会隐式的定义 setter 方法,对非空的变量还会增加 getter 方法。但是加了私有后,外界就无法访问变量了,需要我们手动的添加 set 和 get 方法。

    class Worker extends Person {
    
      ......
    
      double _salary;
    
      set salary(double value) => _salary = value;
    
      get salary => _salary;
    
      ......
    
    }
    

    抽象类和方法

    使用 abstract 修饰符来定义一个抽象类,该类不能被实例化。抽象类在定义接口的时候非常有用,实际上抽象类中也包含一些实现。如果一个类继承了抽象类要么实现它的抽象方法,要么也将自己定义成抽象类。一个抽象类中不一定要有抽象方法,但是有抽象方法的类一定是抽象类。

    //抽象类
    abstract class Animal {
      //抽象方法
      void eat();
    }
    
    //实现抽象类
    class Dog extends Animal {
      //实现 eat 方法
      @override
      void eat() {
        print("啃骨头");
      }
    }
    

    mixins

    mixins 是在多个类层次结构中重用代码的一种方式。要使用 mixins,在 with 关键字后面跟一个或多个 mixin 的名字(用逗号分开),并且 with 要用在 extends 关键字之后。

    mixins 的特征:实现 mixin,就创建一个继承 Object 类的子类(不能继承其他类),不声明任何构造方法,不调用 super。

    //声明 mixins 类
    class Pig {
      void action() {
        print("吃完就睡");
      }
    }
    
    //使用 mixins 类,可以复用它的 action 方法
    class Cat extends Animal with Pig {
      @override
      void action() {
        print("抓老鼠");
      }
    
      @override
      void eat() {
        print("吃猫粮");
      }
    }
    

    方法定义

    方法是由返回值类型+方法名+参数构成。其中返回值类型可缺省,也可为 void 或者具体的类型。正常情况下都是由方法名的,但有一种特殊就是匿名方法。参数分为参数类型和参数名,其中参数类型可缺省。参数又分为可选参数和默认参数,他们都使用 {} 来定义。

    class FunClass {
      //city 为可选参数,town 为默认参数
      String fromWhere(country, {String city, String town = "小红帽村"}) {
        if (city != null) {
          return "我来自 $country $city $town";
        } else {
          return "我来自 $country $town";
        }
      }
    }
    

    我们来调用打印下看看:

    FunClass funClass = FunClass();
    print(funClass.fromWhere("格林国")); //输出 我来自 格林国 哈哈村
    print(funClass.fromWhere("格林国", city: "童话镇"));//输出 我来自 格林国 童话镇 哈哈村
    

    匿名方法有时候也被称为 lambda 或者 closure 闭包,可以把匿名方法赋值给一个变量,直接调用变量名即可。

    var printI = (i) => print(i); 
    

    使用:

    FunClass funClass = FunClass();
    funClass.printI(999);
    

    泛型

    泛型主要是解决类、接口、方法的复用性、以及对不特定数据类型的支持。

    class Cache<T> {
      static final Map<String, Object> _cached = Map();
    
      void setItem(String key, T value) {
        _cached[key] = value;
      }
    
      ///泛型方法
      T getItem(String key) {
        return _cached[key];
      }
    }
    

    这里我们定义了一个缓存类,存储的类型被定义为泛型,提高代码的复用度。

    有时候我们在实现类似通用接口的泛型中,期望的类型是某些特定类型时,这时可以使用类型约束。

    class Member<T extends Person> {
      T _person;
    
      ///泛型作用:约束参数类型
      Member(this._person);
    
      String fixedName() {
        return 'fixed:${_person.name}';
      }
    }
    

    总结

    本文主要是讲解了在 Flutter 中常用到的 dart 的基础知识,当然还有其他很多的知识点没有涉及到。这里推荐几个 dart 学习网站给大家使用。

    https://www.dartlang.org

    https://dart.dev/guides/language/language-tour

    http://dart.goodev.org/guides/language/language-tour

    我已经将全部 Demo 源码上传到后台,关注公众号回复「 dart 基础」即可获得下载链接。

    如果觉得文章不错,别只收藏,扫描下方二维码关注公众号,获取更多技术干货。

    3 条回复    2019-12-06 16:09:37 +08:00
    kardec
        1
    kardec  
       2019-12-06 14:32:06 +08:00
    不错,通俗易懂
    laravel
        2
    laravel  
       2019-12-06 15:07:05 +08:00
    谁有 flutter 小项目让我做做,只做过一个 flutter app,现在还在运行中,好几个月不搞,都快忘完了
    IGJacklove
        3
    IGJacklove  
       2019-12-06 16:09:37 +08:00
    @laravel 老哥写个微信么?只写 ui 那种。找到项目啦我一个可以么
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5306 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 07:50 · PVG 15:50 · LAX 23:50 · JFK 02:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.