Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flutter 基础—— Dart 入门 #15

Open
zxiaohong opened this issue Nov 29, 2018 · 0 comments
Open

Flutter 基础—— Dart 入门 #15

zxiaohong opened this issue Nov 29, 2018 · 0 comments

Comments

@zxiaohong
Copy link
Member

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✅;

几个重要的点:

  • 能赋值给变量的所以东西都是对象,包括 numbers, null, function, 都是继承自 Object 内置类
  • 尽管 dart 是强类型的,但类型注释是可选的,因为 dart 可以类型推断,如果要明确说明不需要任何类型,请使用特殊类型dynamic。
  • 尽量给变量定义一个类型,会更安全,没有显示定义类型的变量在 debug 模式下会类型会是 dynamic(动态的)
  • dart支持泛型类型,如List <int>(整数列表)或List <dynamic>(任何类型的对象列表)。
  • 支持最外层函数定义,也支持类的静态方法与对象的实例方法,函数中可定义内部函数
    (Dart支持顶级函数(例如main()),以及绑定到类或对象的函数(分别是静态和实例方法)。您还可以在函数内创建函数(嵌套函数或本地函数))。
  • dart 在 running 之前解析你的所有代码,指定数据类型和编译时的常量,可以提高运行速度
  • dart 没有 public、private、protected 这些关键字,变量名以"_"开头意味着对它的 lib 是私有的
  • 标识符以字母或“_”开头,后面可随意组合

这里简单介绍下:

  • Hello World

  • 变量声明

  • 数据类型

  • 函数

  • 流程控制

  • 异常处理

  • 泛型

  • 异步操作

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提供基本的数据类型:

  • Numbers
  • Strings
  • Booleans
  • Lists
  • Maps
  • Runes(字符)
  • Symbol(符号)
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
  • '''...''',"""..."""表示多行字符串(连续三个单引号或双引号)
  • r'...',r"..."表示“raw”字符串(字符串前加字母 r 则不处理转义)
  • 可用${}引用表达式,或直接$引用标识符

示例代码:

 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. 流程控制

  • if...else if... else...
  • 三元表达式
  • for
  • while/do...while
  • switch...case..
    ...

这个流程控制语句使用和 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 类中的函数

  • 定义函数:在类里定义函数和单独定义函数是一样的;
main(List<String> args) {
  Cat Tom = new Cat("Tom", 1);
  Tom.eat();
}
 
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<String> 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<Color> 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 支持泛型,在定义 ListMap 类型时,可以手动指定其元素类型,类型一旦确定便不可更改,也不可向其中添加其他类型的变量。
如:

// 构造函数的参数化类型
var names = List<String>();// 代表字符串列表
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 = <String>['Seth', 'Kathy', 'Lars'];
var gifts = <String>,<String>{
    'first': 'partridge',
    'second': 'turtledoves',
    'fifth': 'golden rings'
}

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

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

main(List<String> args) {
  DataHolder<String> dataHolder = new DataHolder('Some data');
  print(dataHolder.getData());
  dataHolder.setData('New Data');
  print(dataHolder.getData());
}
 
class DataHolder<T> {
  T data;
 
  DataHolder(this.data);
 
  getData() {
    return data;
  }
 
  setData(data) {
    this.data = data;
  }
}

9. 异步支持

Dart 支持异步操作。
使用异步首先要导入异步库import: "dart/sync";

Dart 有丰富的库支持,这里先不讲了,开发 Flutter 时,用到再去查就行。

异步操作(函数)有两种返回类型: FutureStreams

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

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant