前言
如果你只是写界面可能你看Flutter的Widget就够了。
可是当你写业务的时候,你不去了解Dart语言那么你讲寸步难行。
本文致力于以最小的篇幅介绍Flutter的敲门砖:Dart最必备的基础知识。
-
在Dart中,一切都是对象,一切对象都是class的实例,哪怕是数字类型、方法甚至null都是对象,所有的对象都是继承自Object
-
跟Java不同的是,Dart没有public protected private等关键字,如果某个变量以下划线(_)开头,代表这个变量在库中是私有的
一、变量
1、变量定义
int b = 10;
String s = "hello";
var a = 1;
dynamic c = 0.5;
可以明确指定某个变量的类型,如int bool String,也可以用var或 dynamic来声明一个变量,Dart会自动推断其数据类型。
dynamic 则是告诉编译器,我们知道自己在做什么, 不用做类型检测 。
2、变量类型检测
为了在运行时检测进行类型检测,Dart 提供了一个关键字 is:
dynamic obj = {};
if (obj is Map) {
// 进过类型判断后,Dart 知道 obj 是一个 Map,所以这里再不用强制转换 obj 的类型为Map。
obj['foo'] = 42;
}
// 虽然 Dart 也提供了 as 让我们进行类型的强制转换,但为了进来更安全的转换,更推荐使用 is
var map = obj as Map;
二、函数
1、函数的写法
以下三种函数的写法,都是正确的。
// 写法1:声明返回值(推荐)
int add(int a, int b) {
return a + b;
}
// 写法2:不声明返回值(不加返回值的函数同样可以正常工作)
add2(int a, int b) {
return a + b;
}
// 写法3:你还可以用=>代替return语句(=>是return语句的简写)
add3(a, b) => a + b;
main() {
print(add(1, 2)); // 3
print(add2(2, 3)); // 5
print(add3(1, 2)); // 3
}
所有的函数都有返回值,如果没有指定return语句,那么该函数的返回值为null。
2、函数的参数(可选/必选)
void main() {
print(option_foo1(2)); // 结果为2
print(option_foo1(1, 2)); // 结果为3
print(require_foo(2)); // 错误:结果为2
print(require_foo(1, 2)); // 结果为3
// 具名参数的顺序可以是任意的
print(named_foo1(x: 1, y: 2));
print(named_foo1(y: 3, x: 4));
print(named_foo1()); //错误:会导致 foo() 在运行时抛异常
print(named_foo2(x: 1, y: 2));
print(named_foo2()); // 正确:不会导致 foo() 在运行时抛异常
}
// 可选参数
// 使用可选参数的函数写法1:含可选参数的函数
int option_foo1(int x, [int y]) {
if (y == null) { // 是的,int 也可以是 null
return x;
} else {
return x + y;
}
}
// 使用可选参数的函数写法2:可选参数支持有默认值
int option_foo2(int x, [int y = 0]) {
return x + y;
}
// 必须参数(如果想告诉用户某个具名参数是必须的,可以使用注解 @require)
int require_foo({@required int x, @required int y}) {
return x + y;
}
// 具名参数(named parameters)的用法,调用时候具名参数的顺序可以是任意的
// 使用具名参数的函数参数写法1:
int named_foo1({int x, int y}) {
return x + y;
}
// 使用具名参数的函数参数写法2:所有的具名参数本身就都是可选的,而且具名参数也可以有默认参数
int named_foo2({int x = 0, int y = 0}) {
return x + y;
}
3、函数做参数/返回值
// 用typedef用于定义函数类型的别名
typedef Adder = int Function(int, int);
Adder makeAdder1(int extra) {
int adder(int x, int y) {
return x + y + extra;
}
return adder;
}
Dart 里面不仅变量支持类型推断,lambda 的参数也支持自动推断。上面的代码还可以进一步简化为:
Adder makeAdder2(int extra) {
// 我们要返回的类型是 Adder,所以 Dart 知道 x, y 都是 int
return (x, y) => x + y + extra;
}
void main() {
var adder1 = makeAdder1(2);
print(adder1(1, 2));
var adder2 = makeAdder2(2);
print(adder2(1, 2));
}
4、函数类型
Dart是一个面向对象的编程语言,所以即使是函数也是一个对象,也有一种类型Function,这就意味着函数可以赋值给某个变量或者作为参数传给另外的函数。
printNum(int a) {
print("$a");
}
test(Function callback) {
callback("hello");
}
main() {
var f1 = printNum;
Function f2 = printNum;
var f3 = (int a) => print("a = $a");
f1(1);
f2(2);
f3(6);
}
三、类
1、类的定义
class User {
// 成员变量写法1:
String name;
int age;
String gender;
// 成员变量写法2:使用getter/setter声明的成员变量
String get uid => "24210853532539238";
set right(String uid) => left = value - width;
// 通过创建一个与其类同名的函数来声明构造函数。如果您未声明构造函数,则会为您提供默认构造函数。默认构造函数没有参数,并在超类中调用无参数构造函数。
// 构造方法写法1:跟类名相同的构造方法
User(this.name, this.age, this.gender);
// 构造方法写法2:命名的构造方法
User.defaultUser() {
name = "dvlp";
age = 20;
}
// 成员方法写法1:有方法体(是非抽象方法)
sayHello() {
print("hello, this is $name, I am $age years old, I am a $gender");
}
// 成员方法写法2:没有方法体(是抽象方法,需要子类去实现)
void doSomething();
}
// 抽象类(无法实例化的类),如果希望抽象类看起来是可实例化的,请定义工厂构造函数。
// 抽象类通常有抽象方法,如下:
// 此类声明为abstract,因此是抽象类,即也就无法实例化
abstract class Test {
//定义构造函数,字段,方法...
// 抽象方法
void test();
}
说明类中的构造方法Person(this.name, this.age, this.gender);
的这种语法是Dart比较独特而简洁的构造方法声明方式,它等同于下面的代码:
User(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
其他声明成员变量的方法:
// 使用getter/setter声明的成员变量
class Rectangle {
num left, top, width, height;
// 构造方法传入left, top, width, height几个参数
Rectangle(this.left, this.top, this.width, this.height);
// right, bottom两个成员变量提供getter/setter方法
num get right => left + width;
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
}
2、类的使用
main() {
var user1 = new Person("zhangsan", 20, "male");
user.age = 30;
user.gender = "female";
user.sayHello();
var user2 = new Person.defaultUser()
}
当我们调用一个不存在的方法时,会执行 noSuchMethod() 方法,默认情况下它会抛出 NoSuchMethodError。你也可以重写noSuchMethod()
,改成你输出你希望的错误提示
class Test {
// 除非你重写noSuchMethod,否则使用不存在的成员会导致NoSuchMethodError
// Unless you override noSuchMethod, using a
// non-existent member results in a NoSuchMethodError.
@override
void noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: ' +
'${invocation.memberName}');
}
}
3、类的继承
Dart中使用extends关键字做类的继承。
class Developer extends User {
// 实现了父类的抽象方法
void doSomething() {
print("I'm developing...");
}
}
虽然 Dart 是单继承的,但它也提供了一定程度的多重继承支持:
使用@override
注解声明你要重写的函数,在这个函数内部可以使用super调用重写的这个父类的函数。
class FlutterDeveloper extends Developer {
@override // 表示doSomething是重写父类的方法
void doSomething() {
super.doSomething();
print("I'm developing Flutter...");
}
}
class iOSDeveloper extends Developer {
@override // 表示doSomething是重写父类的方法
void doSomething() {
super.doSomething();
print("I'm developing iOS...");
}
}
class FullStackDeveloper extends Developer with iOSDeveloper with FlutterDeveloper {
@override // 表示doSomething是重写父类的方法
void doSomething() {
super.doSomething();
print("I'm developing...");
}
}
mixins
mixins是一个重复使用类中代码的方式,比如下面的代码:
class A {
a() {
print("A's a()");
}
}
class B {
b() {
print("B's b()");
}
}
// 使用with关键字,表示类C是由类A和类B混合而构成
class C = A with B;
main() {
C c = new C();
c.a(); // A's a()
c.b(); // B's b()
}
四、库
// 用import语句来导入某个包
// 如果你想导入自己写的某个代码文件,使用相对路径即可
import './util.dart';
// 你可以使用as关键字为导入的某个包设置一个前缀,或者说别名
import 'package:lib2/lib2.dart' as lib2;
// 导入包时使用deferred as可以让这个包懒加载,懒加载的包只会在该包被使用时得到加载,而不是一开始就加载,比如
import 'package:greetings/hello.dart' deferred as hello;
1、问题一:在同一工程中可否有两个同名的类
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// Uses Element from lib1.
Element element1 = Element();
// Uses Element from lib2.
lib2.Element element2 = lib2.Element();
2、问题二、如何只允许/只不允许使用某个包中的部分功能
// 只导入lib1中的foo功能
import 'package:lib1/lib1.dart' show foo;
// 导入lib2中的除了foo外的所有其他部分
import 'package:lib2/lib2.dart' hide foo;
五、运算符
1、常见的
运算符 | 含义 |
---|---|
++var |
var=var+1 表达式的值为var+1
|
var++ |
var=var+1 表达式的值为var
|
--var |
var=var-1 表达式的值为var-1
|
var-- |
var=var-1 表达式的值为var
|
++a
a == b
b ? a : b
2、特殊的
2.1、..运算符(级联操作)
使用..调用某个对象的方法(或者成员变量)时,返回值是这个对象本身,所以你可以接着使用..调用这个对象的其他方法。
class Person {
eat() {
print("I am eating...");
}
sleep() {
print("I am sleeping...");
}
study() {
print("I am studying...");
}
}
main() {
// 依次打印
// I am eating...
// I am sleeping...
// I am studying...
new Person()..eat()
..sleep()
..study();
}
六、控制流程
语句 | 说明 |
---|---|
if / else | 略 |
switch | 略(switch 也支持 String 和 enum) |
for /while | 略 |
try / catch | 略 |
// switch语句
String a = "hello";
// case语句中的数据类型必须是跟switch中的类型一致
switch (a) {
case "hello":
print("haha");
break;
case "world":
print("heihei");
break;
default:
print("WTF");
}
七、异步
如果一个方法中有耗时的操作,你需要将这个方法设置成async,并给其中的耗时操作加上await关键字,如果这个方法有返回值,你需要将返回值塞到Future中并返回,如下代码所示:
Future<String> checkVersion() async {
var version = await lookUpVersion();
return version;
}
八、其他/奇技淫巧
1、静态成员变量和静态成员方法
// 类的静态成员变量和静态成员方法
class Cons {
static const name = "zhangsan";
static sayHello() {
print("hello, this is ${Cons.name}");
}
}
main() {
Cons.sayHello(); // hello, this is zhangsan
print(Cons.name); // zhangsan
}
2、修饰符final和const
final 跟 Java 里的 final 一样,表示一个运行时常量(在程序运行的时候赋值,赋值后值不再改变)。const 表示一个编译时常量,在程序编译的时候它的值就确定了。
修饰符 | 作用 |
---|---|
final | 一个被final修饰的变量只能被赋值一次, |
const | 一个被const修饰的变量是一个编译时常量(const常量毫无疑问也是final常量) |
final和const的区别:
区别一:final 要求变量只能初始化一次,并不要求赋的值一定是编译时常量,可以是常量也可以不是。而 const 要求在声明时初始化,并且赋值必需为编译时常量。
区别二:final 是惰性初始化,即在运行时第一次使用前才初始化。而 const 是在编译时就确定值了。
3、泛型:
var names = List<String>();
附
其他参考文档:
网友评论