Flutter的优势以及Dart基础语法

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容