jd-smart-fe / shared

共享文档
MIT License
25 stars 4 forks source link

Flutter 基础—— Dart 入门 #15

Open zxiaohong opened 5 years ago

zxiaohong commented 5 years ago

Flutter 基础—— Dart 入门


Flutter简介

Flutter是Google公司2018年2月27日发布的第一个开源跨平台软件开发工具包 (SDK),支持Android、iOS两个平台,可实现高性能、高保真的应用程序开发。开发者借助Flutter开发平台可轻松实现自己的应用,其开发框架默认支持了Material(类似Google设计风格)和Cupertion(类似iOS设计风格)两种风格,且无论从UI样式、用户体验上都非常接近原生应用。 经过近7个月的优化改进2018年9月19日Google公司在上海正式发布非常接近1.0正式版本的Flutter Release Preview 2。基于其优越性能Flutter有望成为未来应用开发的主流工具。

Flutter类似且优于Html、React Native、Weex等跨平台解决方案。ReactNative将JSX生成的代码最终都渲染成原生组件,JS与原生通过JSBridge传递数据。而Flutter则采用完全不同的设计,底层是一套独立的渲染引擎–Skia,所有组件也都是独立于平台的Widget组件,可以在最大程度上保证了跨平台体验的一致性。

image

Dart

Flutter 使用 Dart 作为开发语言,在深入flutter框架前应该先对dart语言有所了解。据说,dart语言里的API 90%以上与 Java 相同,如果你有 Java 基础,几乎可以很快上手,没有 Java 基础也不用担心,它的 API 很简单,学过 JavaScript 也可以轻松get✅;

几个重要的点:

这里简单介绍下:

1. Hello World

    main(List<String> args){
        print("Hello World");
    }

和 Java 程序一样,dart程序也有一个入口函数 main ,把上面的程序保存为hello_world.dart, 在命令行运行:

dart hello_world.dart

安装之后需要配置环境变量

启动 dart 程序

// 定义一个函数
    printNumber(num aNumber) {
        print("The number is `${aNumber}`."); //控制台打印
    }

    // 启动
    main() {
        var number = 42; 
        printNumber(number); 
    }

2. 变量声明

变量声明关键字: varconstfinalintStringdouble(浮点型)、 bool

2.1 var 声明变量 —— 不指定类型

和 JavaScript 一样, 可以用 var 声明变量

main(List<String> args) {
  var number = 18;
  var name ="xiao ming";
  var salary = 150300.56;
  var isDoorOpen = true;
}

不一样的是,dart自带类型推断,一旦给变量赋值,它的类型就不可变了。

尽量给变量定义一个类型,会更安全,没有显示定义类型的变量在 debug 模式下会类型会是 dynamic(动态的) dart 在 running 之前解析你的所有代码,指定数据类型和编译时的常量,可以提高运行速度。

//类似于 typescript, 可以推导出 name 为字符串类型
var name = "Bob";
// 如果不想 推导出类型,下边两种写法:
dynamic name = "Bob";
Object name= "Bob";

2.2 intStringdouble(浮点型)、 bool声明变量——指定变量类型

数值类型num,有两个子类int,double,int可进行按位运算,没有基本类型的概念,由于int,double都是num的子类,定义为num的变量可以在int、double间多态

main(List<String> args) {
  int number = 18;
  String name = "xiaoming";
  double salary = 150300.56;
  bool isDoorOpen = true;
}

2.3 constfinal声明常量

main(List<String> args) {
  final int number = 42;
  const String name = "xiao ming";

  //Omit explicitly defining data types
  final salary = 150300.56;
  const isDoorOpen = true;
}

const是编译时常量,在编译时确定,不可定义为实例变量;final是运行时常量,在第一次使用时确定;一个常量是const类型也暗示必然是final类型 const也可定义为其它const的算式结果,在类中定义需与static一起 const可写在变量或构造函数前用来定义常值,常量表示引用关系不可变,常值表示值本身不可变

3. 数据类型

3.1 dart提供基本的数据类型:

main(List<String> args) {
  //Numbers
  int x = 100;
  double y = 1.1;
  int z = int.parse('10');
  double d = double.parse('44.4');

  //Strings
  String s = "This is a string";
  String backslash = "I can't speak";
  //String interpolation
  String interpolated = "Value of x is $x";   //Prints: Value of x is 100
  String interpolated2 = "Value of s is ${s.toLowerCase()}"; 
    //Prints: Value of s is this is a string

  //Booleans
  bool isDoorOpen = false;
}

3.2 常用方法

3.2.1 number 取值范围:-2^53 to 2^53
// String -> int
var one = int.parse('1');

// String -> double
var onePointOne = double.parse('1.1');

// int -> String
String oneAsString = 1.toString();

// double -> String 注意括号中要有小数点位数,否则报错
String piAsString = 3.14159.toStringAsFixed(2);
3.2.2 string

示例代码:

 String s = "FRONT END DEVELOPER";

  print ("A Commpany has a $s, which is good idea." ==
      "A Commpany has a Front End Developer," +
          " which is good idea.");
  print("I am a " +
      "${s.toUpperCase()} is very hornor!" ==
      "I am a " +
          "FRONT END DEVELOPER is very hornor!");
3.2.3 bool

Dart 是强 bool 类型检查,只有bool 类型的值是true 才被认为是true

3.2.4 List

声明 List 用 new List() 或者简单的赋值

下面是一些基本操作

main(List<String> args) {
    // 声明
    var vegetables = new List();

    //或者简单赋值声明
    var fruits = ["apples", "peaches"];

    // 添加元素
    fruits.add("bananas");

    // 添加多个元素
    fruits.addAll(["grapes","oranges"]);

    // 获取第一元素
    fruits.first

    // 获取最后一个元素
    fruits.last

    // 查找某个元素的索引号
    assert(fruits.indexOf("apples") === 0);

    // 删除指定位置的元素,返回删除的元素
    fruits.removeAt(index);

    // 删除指定元素,成功返回 true ,失败返回 false
    fruits.remove("oranges");

    // 删除最后一个元素,返回删除的元素
    fruits.removeLast

    // 删除指定范围的元素,含头不含尾。成功返回 null
    fruits.removeRange(start, end);

    // 删除指定条件的元素,成功返回null
    fruits.removeWhere((item) => item.length > 6);

    //删除所有元素
    fruits.clear();

    // sort()排序 传入一个函数作为参数,return <0 表示有小到大,>0 表示由大到小;
    fruits.sort((a, b) => a.compareTo(b));

    //常量 List
    var list = const [1,2,3,4];    //Cannot alter elements of this list
}
3.2.5 map 散列表

基本操作

// 声明
// 简单赋值
var smartHomeProducts = {
    "largeAppliances": ["air conditioning", "television", "fridge"],
    "homeDevices": ["air purifier","Sweeping robot", "humidifier"]
}

var searchTerms = new Map();

// 指定键值对的参数类型
var smartHomeSkills = new Map<int, String>();

// 取值
print(smartHomeProducts["largeAppliances"]); // Output: ["air conditioning", "television", "fridge"];
// 不存在的 key 返回 null
print(smartHomeProducts["kitchenAppliances"]); // Output: null;

// Map的赋值,中括号中是Key,这里可不是数组
smartHomeSkills[1] = "chart";

//Map中的键值对是唯一的
//同Set不同,第二次输入的Key如果存在,Value会覆盖之前的数据
smartHomeSkills[1] = "sing";
assert(smartHomeSkills[1] == "sing");

// 检索Map是否含有某Key
assert(smartHomeSkills.containsKey(1));

//删除某个键值对
smartHomeSkills.remove(1);
assert(!smartHomeSkills.containsKey(1));

// 获取所有值

 var entries = smartHomeProducts.entries;
 var values = smartHomeProducts.values;
 print(entries); // Output: (MapEntry(largeAppliances: [air conditioning, television, fridge]), MapEntry(homeDevices: [air purifier, Sweeping robot, humidifier]))
 print(values); // Output: ([air conditioning, television, fridge], [air purifier, Sweeping robot, humidifier])

// 常量map

var squares = const {    //Cannot change the content of this map
    2: 4,
    3: 9,
    4: 16,
    5: 25
  };

4. 函数

Dart是一种真正的面向对象语言,因此即使是函数也是对象并且具有类型Function。这意味着函数可以分配给变量或作为参数传递给其他函数。您也可以像调用函数一样调用Dart类的实例。

函数声明无需 function 关键字;

函数名前可标注函数返回类型,没有返回值的函数返回类型可设为void; 函数的参数可设定类型也可以不设

main(List<String> args) {
  var name = fullName("John", "Doe");
  print(name);
}

String fullName(String firstName, String lastName) {
  return "$firstName $lastName";
}

//或忽略返回类型
fullName(String firstName, String lastName) {
  return "$firstName $lastName";
}

支持箭头函数

fullName(String firstName, String lastName) => "$firstName $lastName";

命名参数

顾名思义:调用命名参数的函数时,必须指定参数的名字;


fullName({String firstName, String lastName}) {
  return "$firstName $lastName";
}
main(List<String> args) {
  var name = fullName(firstName: "John", lastName: "Doe");
  print(name);
}

调用命名参数的函数时,如果没有指定参数的名字,程序会崩溃报错。

参数默认值

为命名参数的某个参数指定默认值,这个参数就会变成可选参数。如上面的函数指定默认参数后可以这样调用:

main(List<String> args) {
  var name = fullName(firstName: "John");
  print(name);
}

fullName({String firstName, String lastName = "Doe"}) {
  return "$firstName $lastName";
}

5. 流程控制

这个流程控制语句使用和 JavaScript 没什么差异

6. 异常处理

try...catch...finaly 也跟 JavaScript 差不多。。。

7. 类 Class

7.1 类的定义

7.1.1 一个简单的类
//一个简单的类
class Point {
  num x;
  num y;
}

void main() {
  var point = Point();
  point.x = 4; // Use the setter method for x.
  assert(point.x == 4); // Use the getter method for x.
  assert(point.y == null); // Values default to null.
}

还记得吗: dart中一切未赋值的变量值都是 null;

7.1.2 构造函数

通过创建一个与类同名的函数来声明构造函数;

class Cat {
    String name;
    int age;

    Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

void main() {
  Cat Tom = new Cat("Tom", 2);
    print(Tom.name);
}

dart 提供了语法糖,以更简单的方式来生成构造函数;

class Cat {
    String name;
    int age;

    Cat(this.name ,this.age) 
}

void main() {
  Cat Tom = new Cat("Tom", 2);
    print(Tom.name);
}

上面的代码只用一行代码就完成了构造函数,第一个参数,对应到name,第二个参数对应age;

实际上,dart 会为没有声明构造函数的类创建默认的构造函数,默认构造函数没有参数,并在超类中调用无参数构造函数。

命名构造函数(构造函数标识符)

class Cat {
    String name;
    int age;

    Cat(this.name ,this.age) 

    Cat.newBorn(){
        name = "Cherry";
        age = 0;
    }
}

void main() {
  Cat Cherry = new Cat.newBorn();
    print(Cherry.name);
}

上面的代码,构造函数有了一个名字(newBorn), 这样在初始化时更清楚使用了哪个类

7.2 继承

dart 的类通过 extends 关键字实现继承

如果没有特别指明,子类的构造函数中第一步会调用父类的默认构造函数 子类构造函数的执行顺序是:初始化字段表->父类构造函数->子类构造函数

调用父类构造函数

// 调用父类构造函数
class Cat {
    String name;
    int age;

    Cat(this.name ,this.age) 

    Cat.newBorn(){
        name = "Cherry";
        age = 0;
    }
}

class Garfield extends Cat{
    Garfield(String name, int age): super(name,age)
}

void main() {
  Cat Garfield = new Garfield("jiafei", 1);
    print(Garfield.name);
}

Garfield类继承了Cat类,并且在Garfield的构造函数里用SUPER关键字调用了Cat类的构造函数,

重定向构造函数:有时构造函数的唯一目的是重定向到同一个类中的另一个构造函数。重定向构造函数的主体是空的,构造函数调用出现在冒号(:)之后。

main(List<String> args) {
  Pug p = new Pug.small('Duffy');
  print(p.name);
}

class Dog {
  String name;
  int age;

  Dog(this.name, this.age);

  Dog.newBorn() {
    name = 'Doggy';
    age = 0;
  }
}

class Pug extends Dog {
  Pug(String name, int age): super(name, age);

  Pug.small(Stirng name): this(name, 1);

  Pug.large(Stirng name): this(name, 3);
}

上面定义了两个命名构造函数, small 调用了自身的构造函数,而自身又调用了dog的构造函数。

任何构造函数都不会被继承,这意味着父类的命名构造函数不会被子类继承。如果希望使用父类中定义的命名构造函数创建子类,则必须在子类中用冒号手动指明调用父类构造函数。

class Employee extends Person {
  // Person does not have a default constructor;
  // you must call super.fromJson(data).
  Employee.fromJson(Map data) : super.fromJson(data) {
    print('in Employee');
  }
}

7.3 类中的函数

class Cat { String name; int age;

Cat(this.name ,this.age) 

Cat.newBorn(){
    name = "Cherry";
    age = 0;
}
eat(){
    print("I like eating fish!");
}

}

- 函数重写

main(List args){ Cat Cherry = new Garfield.small("jiafei"); Cherry.eat(); } // 父类 -- 猫 class Cat{ String name; int age;

Cat(this.name, this.age);

Cat.newBorn(){
    name="dahua";
    age=0;
}

eat(){
    print("I like eating fish!");
}

} // 子类 -- 加菲猫 class Garfield extends Cat{

Garfield(String name, int age): super(name,age);

Garfield.small(String name, int age): this(name, 1);

Garfield.large(String name, int age): this(name, 3);

@override
eat(){
    print("I like eating small can!");
}

}

#### 7.4 getter setter

默认的情况下类里定义的任何变量都是可以访问的,如 `dog.name`,变量的值也可以直接读写。但是有时候不希望直接读写而是通过`getter` `setter`。dart里 是通过`get` `set` 关键字实现的;

class Rectangle { num left, top, width, height;

Rectangle(this.left, this.top, this.width, this.height);

// Define two calculated properties: right and bottom. num get right => left + width; set right(num value) => left = value - width; num get bottom => top + height; set bottom(num value) => top = value - height; }

void main() { var rect = Rectangle(3, 4, 20, 15); assert(rect.left == 3); rect.right = 12; assert(rect.left == -8); }

#### 7.5 抽象方法和抽象类

> 抽象类使用 `abstract` 关键字定义,抽象方法只能存在于抽象类中;
> 除非定义了工厂构造函数,抽象类不能被实例化
> 抽象方法以(;)结尾,不写函数体,即不用实现它

abstract class Doer { // 实例的变量和方法...

// 定义一个抽象方法,以(;)结尾,没有函数体.

void doSomething(); }

class EffectiveDoer extends Doer { void doSomething() { // 提供了函数体,所以这不是一个抽象方法 } }


#### 7.6 隐式接口

> 每个类都隐式定义一个接口,这个接口包含该类的所有实例成员及其实现的任何接口。
> 如果要在不继承B实现的情况下,创建支持B类API的A类,则A类应该实现B类的接口。

一个类通过`implements`语句实现接口

class Person{ final _name; // 这是一个接口,但只能在本类中访问

Person(this.name); // 这不是接口,因为它是构造函数

String greet(String who) => "Hello $who, I am $name";   // 这是接口

}

Class Imposter Implements Person{ get _name => ""

String greet(String who) => "Hi $who, do you know who I am ?"; }

String greetBob(Person person) => person.greet("Bob");

void main(){ print(greetBob(Person("Kathy")));// Output: Hello, Bob. I am Kathy. print(greetBob(Imposter())); // Output: Hi Bob. Do you know who I am? }


#### 7.7 noSuchMethod

检测是否使用了不存在的变量或方法,可以重写`noSuchMethod`方法,否则会抛出`NoSuchMethodError`这个异常;

class A { // 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}'); } }

#### 7.8 静态方法

在字段或方法前增加static关键字就变成了静态,如:

class Dog { String name; int age;

Dog(this.name, this.age);

static bark() { print('Bow Wow'); } }

main() { Dog.bark(); }


> 静态变量在使用之前不会初始化。
> 静态方法(类方法)不对实例进行操作,因此无法访问它.
例如:

import 'dart:math';

class Point { num x, y; Point(this.x, this.y);

static num distanceBetween(Point a, Point b) { var dx = a.x - b.x; var dy = a.y - b.y; return sqrt(dx dx + dy dy); } }

void main() { var a = Point(2, 2); var b = Point(4, 4); var distance = Point.distanceBetween(a, b); assert(2.8 < distance && distance < 2.9); print(distance);//2.8284271247461903

}

#### 7.9  枚举类型Enum

> 枚举类型(通常称为枚举或枚举)是一种特殊类,用于表示固定数量的常量值。
> 

使用enum关键字声明枚举类型:

enum Color { red, green, blue }


枚举中的每个值都有一个索引getter,它返回枚举声明中值的从零开始的位置。例如,第一个值具有索引0,第二个值具有索引1。

assert(Color.red.index == 0); assert(Color.green.index == 1); assert(Color.blue.index == 2);


可以通过`.values`获取枚举类中所有值的列表

List colors = Color.values; assert(colors[2] == Color.blue);

#### 7.10 mixins  为类添加功能

mixins是一种在多层级结构类中重用代码的方法。

定义mixin:使用 `mixin` 关键字创建一个**没有构造函数**的扩展类来实现一个mixin。

使用mixin:通过`with`关键字后跟多个类名来使用mixin;

mixin Musical { bool canPlayPiano = false; bool canCompose = false; bool canConduct = false;

void entertainMe() { if (canPlayPiano) { print('Playing piano'); } else if (canConduct) { print('Waving hands'); } else { print('Humming to self'); } } }

class Musician extends Performer with Musical { // ··· }

class Maestro extends Person with Musical, Aggressive, Demented { Maestro(String maestroName) { name = maestroName; canConduct = true; } }

### 8. 泛型

Dart 支持泛型,在定义 `List` 和`Map` 类型时,可以手动指定其元素类型,类型一旦确定便不可更改,也不可向其中添加其他类型的变量。
如:

// 构造函数的参数化类型 var names = List();// 代表字符串列表 names.addAll(['Seth', 'Kathy', 'Lars']); names.add(42); // Error

var gifts = Map<String, String>(); gifts['first'] = 'partridge'; gifts['second'] = 'turtledoves'; gifts['fifth'] = 'golden rings'; gifts[17] = 'argon'; //Error

// 使用字面量集合 var name = ['Seth', 'Kathy', 'Lars']; var gifts = ,{ 'first': 'partridge', 'second': 'turtledoves', 'fifth': 'golden rings' }


同时,使用字面量定义变量时,Dart  也可以进行类型推断,如`var name = ['Seth', 'Kathy', 'Lars']`,可以推断该 List  为 `List<String>()`类型;

不限定参数类型
如有一个类,管理一个数据,希望这个数据是任何类型。

main(List args) { DataHolder dataHolder = new DataHolder('Some data'); print(dataHolder.getData()); dataHolder.setData('New Data'); print(dataHolder.getData()); }

class DataHolder { T data;

DataHolder(this.data);

getData() { return data; }

setData(data) { this.data = data; } }

### 9. 异步支持
Dart 支持异步操作。
使用异步首先要导入异步库`import: "dart/sync"`;
> Dart 有丰富的库支持,这里先不讲了,开发 Flutter 时,用到再去查就行。

异步操作(函数)有两种返回类型: `Future`和`Streams`;

Future基于观察者模式。类似于JavaScript里的Rx和Promise,如果熟悉这两个,就很容易理解Future。
简单来说,Future定义了未来将要发生的事,例如:未来我们会得到一个值。
Future 是泛型,Future<T>,需要指定未来返回值的类型。如:

``` dart
import 'dart/async';
Future<String> getStory(){
    return new Future<String>.delay(new Duration(milliseconds:2000), (){
        return 'Here is the story';
    })
};

main(){
    getStory().then((value) => {
        print(value);
    }).catchError((error)=> {
        print(error);
    })
    print('Another print statement.');
}

使用async/await 关键字执行异步操作,使用try,catchfinally来处理使用await的代码中的错误; 用async 关键字修饰函数体就声明了该函数是异步函数;

import 'dart/async';
Future<String> getStory(){
    return new Future<String>.delay(new Duration(milliseconds:2000), (){
        return 'Here is the story';
    })
};

main() async{
    try{
        String result = await getStory();
        print(result);
    }catch(e){
        print(e);
    }
    print('Another print statement.');
}

处理流 Stream 可用await for等待遍历stream的所有值,但要小心使用:

Future main() async {
  // ...
  await for (var request in requestServer) {
    handleRequest(request);
  }
  // ...
}

表达式的值必须具有Stream类型。执行过程如下:

  1. 等待流发出值。
  2. 执行for循环的主体,并将变量设置为发出的值。
  3. 重复1和2,直到流关闭。 要停止侦听流,您可以使用break或return语句,该语句将跳出for循环,并从流中取消订阅。

保证awaitawait for要处在异步操作中

参考文档:

  1. dart 官方文档:https://www.dartlang.org/guides/language/language-tour
  2. dart 中文文档:https://www.kancloud.cn/marswill/dark2_document
  3. 学习flutter必会的dart知识系列:https://zhuanlan.zhihu.com/p/38847885
  4. Dart 学习备忘录: https://zhuanlan.zhihu.com/p/39961580