学习flutter 1.基础知识

32 阅读4分钟

安装flutter

使用flutter模板即可 会自带dart

参考文档

下载模板后 在vscode中按下ctrl+shift+P并使用Flutter: Select Device选择一个平台 再使用flutter run命令运行项目

项目结构

image.png

lib/main.dart是flutter项目的入口 其余文件夹与测试、原生有关 暂时不关心

flutter插件会生成widget树以便于开发 image.png

dart语言基础

基本语法

  • 在每个语句后写分号 语句块则不用
  • 有if-else/for/switch语句
  • 可以以这样的方式构造字面量 条件字段/循环添加字段
{
  if(a>1)'a' :a,
  for (var x in ['a']) 'x': x
}
[
  if(a>1) a,
  for (var x in ['a']) x
]

// dart sdk
import 'dart:xxx';
// 包路径
import 'package:xxx';
// 本地文件
import 'xxx.dart'

引用本地文件时 同文件下可以省略'./'前缀

下载第三方包

pub.dev寻找第三方包 然后在pubspec.yaml添加相应的依赖 最后执行flutter pub get

项目包

import 'package:project_name/a.dart';

可以索引到lib/a.dart 其中project_name来自pubspec.yaml的name属性

导入导出

包内所有以下划线开头的成员外界无法访问 其余成员自动导出

如果包之间有成员名称冲突 可以将包重命名

import 'xxx' as xxx;

想导出其他包的成员 必须主动导出

export 'xxx';

用于聚合导出

变量和类型系统

  • 声明变量时 类型前置String a = 'a'; 可以使用var自动获取变量类型
  • 基本类型
    • int整数 double浮点数 num同时处理整数和浮点数
    • String 字符串 用单引号双引号均可 支持用$${xx}进行变量插值
      • 使用三引号声明的字符串可以换行
      • 使用r'aa\n'声明的字符串会被视为原始字符串(不能转义/插值)
    • bool 布尔类型
    • List List<int> 字面量形如[1,2]
    • Map Map<String, String>字面量形如{'a':'a'}
    • Set Set<String> 字面量形如{'a','b'}。注意 Set类型要求值唯一
  • 强类型 一般情况变量不可更改 所有类型都继承自Object 包括基本类型
  • 使用is运算符判断类型.可以用于判断任何类型,包括自定义类型
  • 使用as运算符强制声明类型
  • dynamic代表着编译时不检查类型 而是把问题留到运行时
  • Object表示需要先判断类型再使用
  • null null的类型是Null 唯一的空值.只有用形如int? a声明的变量可以被赋null.注意 若是List/Set要接受空值 内部数据的类型必须声明为可空
  • 类型别名 typedef 为复杂类型创建可读性高的别名
  • final关键字表示运行时的不变量,const关键字表示编译时的不变量(不一定是字面量).用finalconst修饰正在定义的变量时,可以省略var.

空值处理

dart有完备的空值防备语法

  • a ?? xx 空值合并
  • a! 非空断言
  • a?.xx 非空可访问
  • a??= 在空值时赋值

late关键字

在初始化时不赋值 由开发者确保使用前赋值

函数

函数的定义方式如下

// 按位置传参
// 允许可选参数 不传或者传null都可以
void fn(String a, String? b, String c) {}
// 使用命名参数
void fn({required String a, String? b, String c='a'}) {}

命名参数是推荐的函数定义方式 调用方式为fn(a:'a');.命名参数里 必填参数应当声明为required或者设置默认值

函数也是变量的一种 可以直接赋值 其类型一般形如int add(int,int) 可以使用typedef创建可读性较高的函数类型

可空函数这样调用

fn?.call();

函数具有闭包

可以声明箭头函数 但箭头函数只能指向一个表达式 不能有多行语句

// 箭头函数 必须是单个表达式
var fn = ({required String a, String? b, String c='a'}) => 'a $a';

异步

dart也有事件循环 同样分为同步代码和两个队列 分别为microtask和event queue(类似宏队列) 它们的执行顺序和js一样

Future对象描述一个将来可能取得的值或完成的行为

// 进入microtask
Future.value(42);
Future.error('error');
Future.microtask((){
  // 在microtask创建执行这个函数的任务
})
// 进入event queue
Future.delayed(const Duration(seconds: 1))// 等一秒
Future((){
  // 在event queue创建执行这个函数的任务
})

// 使用Future对象 类似js的then catch finally
Future.value(42).then(() {}).catchError(() {}).whenComplete(() {});

使用Completer创建一个手动控制的Future 类似new Promise

import 'dart:async';

    final completer = Completer<int>();
    // completer.complete(1);
    // completer.completeError('a');
    // await completer.future;

在异步函数里 可以await一个Future

  Future<void> fn() async {
    await Future.value(42);
  }

同样有try catch finally

  • 类可以声明属性和函数
  • 类函数(包括构造函数)访问类属性不需要this.前缀
  • 类函数可以被赋值给其他变量 会自动绑定类实例
  • 创建类实例直接var a = A()即可

构造函数

class A {
    // 不经过构造函数 直接初始化
    String b = 'b';
    String a;
    // 构造函数
    A(this.a);
    // 或者
    A({this.a = 'a'});
    A({required this.a});
}

如果类属性不能为null则this.a必须出现在构造函数中

如果这一属性不能直接从构造函数中获得 应当将其声明为late

class A {
  late String a;
  A({required String b}) {
    a = 'b $b';
  }
}

构造函数可以有多个 并且可以调用其他构造函数

class A {
  late String a;
  A.withName(this.a);
  A() : this.withName('a');
  // 调用默认构造函数直接this()即可
}
// 使用具名构造函数
A.withName('a')

继承

使用extends关键字用于继承

class A extends B {
   A(String a): super(a){
       // xxx
   }
   //或者 直接初始化父类属性
   A(super.key);
}

构造函数后可以跟随初始化列表 在这里调用父类的构造函数
这里不显式调用父类构造函数 则dart会默认调用其无参构造函数

函数重载需要@override

函数或属性会继承父类 可以直接访问 同名的使用super.xxx访问

getter&setter

class A {
  String a;

  A(this.a);
  
  // 不能同名
  get name1 => a;
  set name2(String newName) => a = newName;
}

抽象类

只规定属性和类函数 用于被其他类继承

abstract class A {
  double get a;
  void b();
}

可以在is的右侧用于判断类型
抽象类可以有非抽象函数

minix(混入)

mixin可以为一个类型提供能力 通过with的方式组合到类型里 不改变继承链
可以组合多个minix
minix可以有属性、函数 但不能有构造函数

mixin A {
  void doSomething(){}
}

class B with A {
}