Flutter 开发必备 Dart 基础: Dart 快速入门 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
winlee28
V2EX    Android

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

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

    本文首发于微信公众号「 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     Solana     2211 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 01:03 PVG 09:03 LAX 17:03 JFK 20:03
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86