您的位置:首页 > 其它

Flutter的优势以及Dart基础语法

2020-05-31 18:44 51 查看

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 表示 List 里的数据类型 用中括号来赋值 List list = [1, 2, 3];
Set
Set E 表示 Set 里的数据类型 用大括号来赋值 Set 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’;
如果emp为null,抛出异常
is 判断是否是某个类型,如果是的话,就返回 true if (emp is Person) {
// 如果 emp 是 Person 类型
emp.firstName =‘Bob’;
}
如果emp 为null ,返回false
is! 判断是否不是某个类型,如果不是的话,就返回 true if (emp is! Person) {
// 如果 emp 不是 Person 类型
}
<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 ,希望大家多多支持!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: