前言
学习 Flutter 就必须先学习下 Dart 语言,如果会 Java 语言或者 Kotlin 语言,那么学起来就更简单了,它们语法有很多相似的地方,也有很多不同的地方,这里说说它们不同的地方。当然,也可以直接官网学习Dart学习指南
基本语法
- 所有变量引用的都是对象,下面结合 java 和 kotlin 语法来学习下 dart 的区别
void main(){
int a = 1;
//错误,没有 long 类型,int 包括 long 类型
//long a = 1;
a = 1000000000000000000;
double b = 2.0;
//错误,没有 float 类型,double 包括 float 类型,
// float a1 = 1;
b = 1.012323232233333332332432423323223321322323323232233;
// 如果需要bigDecimal运算,需要导包 https://pub.dev/packages/decimal
//Decimal.parse('0.2') + Decimal.parse('0.1')
//num 包括 int 和 double 类型,运行时类型
num c = 1;
c = 1.0;
String d = "";
//类型后面加?,代表可空,其他类型同理
String? d1 = null;
//三目运算法
String? d2 = d1 == null ? "a" : null;
//强制不为空
String d3 = d2!;
//null 也是对象
Null e;
//返回 true
print(e == null);
String f1 = "aa";
String f2 = "aa";
//true。比较两个字符串直接 ==
print(f1 == f2);
//错误,不能这样构建创建
//String f3 = String("aa");
String f4 = "aa";
String f5 = "aa";
//错误,没有equal
// print(f4.equal(f2));
//错误,没有 public、protected 和 private 成员访问限定符
// public String g1 = "xx";
//代表任意类型
Object? h = null;
var i = null;
//判断是否是 null
print(i == null);
int? j = i??0;
j??=4;
List<int>? list = j == null ? null : [1,2,3];
int add = 4;
// 当list 为空的时候,不加入到list2里面,如果不为空时候则加入
List<int> list2 = [...?list];
print(list2);
late String lateStr;
lateStr = "xxx";
int ss = 1;
String intSS = 1.toString();
int result = int.parse("1");
}
/*错误,没有 public、protected 和 private 成员访问限定符
public int add(){
}*/
//下划线命名表示私有
int _add(int? a,int? b){
return a??0 + (b??0);
}
总结下:
- 和 java 一样语句需要逗号结尾,和 kotlin 不一样,这还是有些遗憾
- 所有引用类型皆对象,没有基本数据类型
- 去除了 long 和 float 类型,增加了 num 类型,涵盖了 int 和 float 类型
- 和 kotlin 一样,类型具有有空安全功能,在申明和引用的时候加 ? 来表示,如 e5?.foo()
- 字符串判断直接用 == ,没有 equal,和 kotlin 一致
- 去除了成员访问限定符,改用命名下划线的方式限定
- 增加了 e2 ?? e3,语法,e2 不为空则为 e2,e2为空的时候设置为 e3,类型 kotlin 的 ?:
- 增加了 x ??= e4 语法,x 不为空时候为 x,否则 x = e4
- 增加了 [...? e6] 语法,如果 e6 不为空的时候,则加入到集合里面,如果为空则不加入
- 增加了 Null 类型,对应着 null 的对象类
- 增加了 late 关键词,类型 kotlin 的 lateinit 的用法,必须初始化再使用,否则会抛出异常。
- int 转换为 string ,如 1.toString(),String 转换为 int,int.parse("1"),其他类似
final 和 const 的区别
接着说说 Dart 语言里面的 final 和 const 的区别
void main(){
const int a = 1;
final int b = 1;
//报错
// a = 2;
//报错
//b = 2;
// int c = 1;
//错误
// const int d = c
const int c = 1;
const int d = c;
const String conStr = "aaa";
final String finalStr = "aaa";
//如果 Person 变量都是 const 或者 final 不报错,否则报错类
const Person conStr1 = Person();
//报错
//const Person conStr1 = new Person();
//不报错
final Person finalStr1 = Person();
//报错
//final const conStr2 = getHello();
//不报错
// final String finalStr2 = getHello();
}
String getHello(){
return "hello, dart";
}
class Person {
static const String TAG = "Person";
final String finalTAG = "Person";
//报错,没加final
// String name = "xx";
const Person();
}
总结下:
相同点
两者定义时候必须初始化,且初始化后的值不可变。
不同点
- const 修饰的是编译时常量,可以修饰字符串,数字,不可修饰变量和实例,final 修饰的是运行时常量,可以是对象,字符串,数字等
- final 只可用来修饰变量, const 可修饰变量和常量构造函数,当 const 修饰 变量时候需要 加 static 修饰,当 const 修饰类的构造函数时,它要求该类的所有成员都必须是 final 或 const 的。
扩展操作符(...)和空感知扩展操作符(...?)
在 Dart 里面这种操作符会经常使用到,上面也提到过,这里举几个例子看看
void main(){
var list = [1,2,3];
var list2 = [0,...list];
print(list2);
var list3 = ["x","2"];
var list4 = [0,...list3];
print(list4);
int a = 1;
var list5 = a == 0 ? null : [1];
var list6 = [...?list5];
print(list6);
var list7 = [1,2];
//这样list8 会是一个二级数组,应该使用 ...list7 ,才能把 list7 加到 list8 集合里面
var list8 = [list7];
var list9 = [...list7];
print(list8);
print(list9);
var b = 1;
var c = 2;
//错误
// var list10 = [...b,...c];
}
如果操作符右边是非空变量使用...,如果是可空变量,则使用 ...?,并且操作符右边必须是一个实现 Iterable 的实例。
集合
- List Dart 里面的 list 和 Java 语言的集合还是有点不同,它支持集合中的 if 和 for 操作
import 'dart:math';
void main(){
var a = Random().nextInt(2);
print(a);
var list = [1,2,3,if(a == 1) 4];
print(list);
var list2 = [0,0,0,for(var i in list) i];
print(list2);
}
- Map
Map 新增一种初始化的方式
void main(){
var info = {
"peter":"23",
"baby":"21",
"gigi":"24",
};
print(info);
info["gigi"] = "25";
print(info);
}
通过一对 map 字面量来创建。
Symbols
在标识符前加 # 前缀来获取 Symbol,当代码压缩后,标识符的名称会改变,但是它们的 Symbol 会保持不变。所以可以用于根据名称获取标识符的情况。
函数
void main(){
show(true,true);
show2(true,true);
show3(true);
show3(true,true);
show3(true,true,true);
show4(true);
print("show4 is null ${show5() == null}");
}
/**
* 两个参数必传
*/
void show(bool isTop,bool isClick){
print("show---$isTop");
print("show---$isClick");
}
/**
* 和 kotlin 不一样,这里两个参数也是必传,只是参数是可空类型
*/
void show2(bool? isTop,bool? isClick){
print("show2---$isTop");
print("show2---$isClick");
}
/**
* 可选参数通过 [] 包裹添加
*/
void show3(bool? isTop,[bool? isClick,bool? isEnable]){
print("show3---$isTop");
print("show3---$isClick");
print("show3---$isEnable");
}
/**
* 使用 = 给参数默认值,默认值必须是常量
*/
void show4(bool? isTop,[bool? isClick = false,bool? isEnable = false]){
print("show4---$isTop");
print("show4---$isClick");
print("show4---$isEnable");
}
/**
* 不使用 void 返回值时候,默认后面会 return null
*/
show5(){
}
以上就是 Dart 函数的使用,可选参数通过 [] 包裹,如果函数加 void 代表没有返回值,如果省略,默认返回 null ,如果加了 return 语句,则以 return 语句类型为准。
级联运算符
运算符为 (..)(?..),注意要和扩展操作符区分,使用方法如下
void main() {
var person = Person()
..name = "GiGi"
..age = 12;
}
class Person {
var name;
var age;
}
类似构建者模式,和 kotlin 的 apply 关键词用法类型。
异常
Dart 异常增加了 on 关键词
void main() {
try {
var a = int.parse("x");
} on FormatException {
} catch (e) {
} finally {
print("end");
}
try {
var a = int.parse("x");
} on FormatException {
rethrow;
} finally {
print("end");
}
}
指定异常类型时候使用 on ,多个异常时候使用 catch。如果需要再次抛出异常则通过 rethrow 关键词来实现。
类
- 获取对象的类型
Dart 语言中,通过 runtimeType 属性来获取对象的类型,比如
void main() {
var list = {"1"};
//输出 _CompactLinkedHashSet<String>
print(list.runtimeType);
}
- 类的实例变量
除了 const 和 final 的变量都会隐式的申明一个 Setter 和 Getter 方法,late final 修饰的变量如果没有初始化之前也会声明一个隐式的 Setter 方法。如
void main() {
var car = Car("BMW",500000);
var type = car.type;
var price = car._price;
print("$type");
print("$price");
}
class Car {
var type;
var _price;
/**
* this 的方式赋值,语法糖简化操作代码
*/
Car(this.type, this._price);
}
- 常量构造函数
class Constant {
static const String version = "1.0";
final String name = "app";
//常量构造函数
const Constant();
}
常量构造函数里面的所有变量都需要是 final 类型.
- 工厂构造函数
主要通过 factory 关键词来实现,如
void main() {
var car1 = Car(20000);
print("type:${car1.type} price:${car1._price}");
var car2 = Car(100000);
print("type:${car2.type} price:${car2._price}");
var car3 = Car(20000);
print("car3:${car3 == car1}");
}
class Car {
var type;
var _price;
static final Map<String, Car> _cache = <String, Car>{};
Car._internal(this.type, this._price);
/**
* 并不一定返回新的对象
*/
factory Car(int money){
if(money >= 50000){
return _cache.putIfAbsent("BMW", () => Car._internal("BMW",500000));
}
return _cache.putIfAbsent("BYD", () => Car._internal("BMW",200000));
}
}
注意的一点就是 factory 返回的对象并不一定是新的对象。
- 抽象方法
abstract class Person{
void name();
}
抽象方法必须要在抽象类里面申明,直接使用分号(;)替代方法体即可声明一个抽象方法,不需要再用 abstract 修饰。
- 隐式接口
Dart 语言里面,类即是接口,每个类都实现了一个隐式的接口,这个接口包含这个类的所有实例成员以及这个类所实现的其它接口。如果想要创建一个 A 类支持调用 B 类的 API 且不想继承 B 类,则可以实现 B 类的接口。例如老婆的资产等于老公的资产+自己的,就可以这样写
void main(){
var wife = Wife();
print("wife:${wife.money}");
}
class Husband{
var money = 100;
}
class Wife implements Husband {
@override
int money = 100 + 10000;
}
- noSuchMethod 方法
这个方法需要配合 dynamic 关键词来使用,使用如下
void main(){
dynamic person = Person();
print("${person.age}");
person.say();
}
class Person{
@override
noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: '
'${invocation.memberName}');
}
}
当返回类型是 dynamic 类型,如果访问类不存在的方法或者属性,则会执行 noSuchMethod 方法,我们可以在里面做一些处理操作。
- Mixin
Mixin 是通过多重继承中复用某个类中代码的方法模式。
使用 with 关键字并在其后跟上 Mixin 类的名字来使用 Mixin 模式,使用如下
void main(){
var g = GiGi();
g.say();
}
class Mother {
void say1(){
print("Mother");
}
}
class Teacher {
void say2(){
print("Teacher");
}
}
class Daughter {
void say3(){
print("Daughter");
}
}
class GiGi extends Mother with Teacher,Daughter {
void say(){
say1();
say2();
say3();
}
}
库和可见性
Dart 语言不在以 public,protected,private 做为成员的限定符了,而是以库来限定,如果以下划线(_)开头命名的成员只能在库里面访问,每一个 dart 程序都是一个库。我们使用库的方式通过 import 导入的方式,如果是 dart:xxx 格式的则代表内置库,如果以package:xxx 格式的则为包管理器管理的。
- 指定库前缀
如果 lib1 和 lib2 有冲突的标识符,则可以通过 as 的方式指定前缀,使用的时候带上前缀即可访问
import 'package:lib2/lib2.dart' as lib2;
lib2.Element element2 = lib2.Element();
- 导入库的一部分
//只导入 foo
import 'package:lib1/lib1.dart' show foo;
//除了 foo 全部导入
import 'package:lib2/lib2.dart' hide foo;
- 延时加载库
//延时加载代码库的用法
import 'package:greetings/hello.dart' deferred as hello;
使用的时候
Future<void> greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
await 等待库加载完成,这里即使 loadLibrary 执行多次库也只加载一次。
异步
在 Dart 语言中,通过 await 和 async 来实现异步编程。例如
void main() async{
var result = await download();
print(result);
}
download(){
sleep(Duration(seconds: 1));
return "ok";
}
这里结合 kotlin 协程来理解就好理解些,用法类似。
生成器
当我们需要延时的生成一串数据时候,我们可以使用 Dart 生成器来生成
同步生成器
void main(){
print(naturalsTo(100));
}
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++;
}
异步生成器,它返回的是一个 Stream,需要通过 await for 来接收
void main() async {
var result = asynchronousNaturalsTo(10);
await for (final request in result) {
print(request);
}
}
Stream<int> asynchronousNaturalsTo(int n) async* {
int k = 0;
while (k < n) yield k++;
}
call 方法(可调用类)
通过实现类的 call() 方法,允许使用类似函数调用的方式来使用该类的实例。例如
void main(){
var call = CallTest();
call("ok");
}
class CallTest {
void call(String info){
print("执行成功$info");
}
}
注解
Dart 的注解声明如下
class Deprecated {
final String message;
const Deprecated(this.message);
}
通过常量构造器方式实现。然后使用的时候类似这样:
@Deprecated('Use `message` instead. Will be removed in Dart 3.0.0')
至此,Dart 语言基础先学习到这里,还有很多知识,后续使用到时候再进一步学习。总体来说入门还是挺简单的。