安装步骤
-
ANDROID_HOME 安卓SDK的位置
-
ANDROID_SDK_HOME 安卓模拟器avd的位置
-
GRADLE_USER_HOME .gradle的位置
-
将flutter>bin配置成环境变量
Dart
Dart是谷歌开发的,类型安全的,面向对象的编程语言,被应用与web、服务器、移动应用和物联网等领域
dart中,所有内容都是对象。
变量
- 明确指定类型:
int String
- 不明确类型:
var age = 18; dynamic age = 18;
- 大小写敏感
- 变量的默认值是
null
(js变量的默认值是undefined
) - Dart变量的值不会进行隐式转换
常量
常量就是值不可变的变量
const age = 18;
final age = 18;
const和finale的区别
const time = DateTime.now();
报错,无法将运行时的值分配给const变- final time = DateTime.now();` 将运行时的值分配给final变量
数据类型
- Number
- num数字类型(可是是整数,也可以是小数)
num n;
- int 表示整数(必须是整数)
- double表示浮点数(可以是整数,也可以是小数)
- num数字类型(可是是整数,也可以是小数)
- String
- 单双引号都可以
- 三个引号可以生命包含换行符(回车)的字符串
- Boolean
bool
- List(数组)
List list = [];
不限制数据类型List list = <int>[];
限制元素类型- 构造函数
- 扩展操作符
...
- Set
- 无序,元素唯一的集合
{}
- 无法通过下标取值
- 可以求交集、并集
- Map
var map = new Map();
var map = { key:value};
- 其他
- Runes https://copychar.cc/
- Symbol
- 用
#
开头来表示的标识符
- 用
dynamic
动态数据类型
类型转换
num n1 = 3.3;
n1.toString();
遍历List
- forEach()
- 遍历列表
- map()
- 遍历并处理元素,然后生成新的列表
- where()
- 返回满足条件的数据
- any()
- 只要有一项满足条件,即返true
运算符
- 地板除(
~/
)7~/41.751 - 类型判断运算符(
is|is!
) `` - 避空运算符(
??|??=
) - 条件属性访问(
?.
) - 级联运算符(
..
)myObj.myMethod();
返回myMethod的返回值myObj..myMethod();
返回myObj的引用
var a;
a ??= 3; // a为空,赋值为3
a ?? 3; // a为空返回3 类似于js得 |
Set s = new Set();
s..add(1)..add(2); // 级联写法
函数
- 直接声明
- 箭头函数,此函数体只能写一行且不能带有结束符号。他是函数的一种简写形式。
- 匿名函数
- 立即执行函数
// 声明函数,不需要 function 关键字
void printInfo() {
print('hello,world');
}
// 匿名函数
var myPrint = (val) {
print(val);
};
// 箭头函数
f.forEach((element) => {print(element)});
// 立即执行函数
((int n) {
print(n);
})(18);
函数参数
- 必填参数 参数类型 参数名称。
- 可选参数 放在必选参数后面,通过中括号包裹。
- 命名参数 用大括号包裹,调用时命名参数的名称与声明函数中的名称保持一致。
// 可选参数 [int age = 22]
String userInfo(String name, [dynamic age]) {
return '你好:${name}, 年龄:$age';
}
// 命名参数
String userInfo(String name, {int age = 22}) {
return '你好:${name}, 年龄:$age';
}
// 调用时,实参和形参一致
String res = userInfo('rain', age: 22);
// 函数参数
var myPrint = (val) {
print(val);
};
List f = ['苹果', '猕猴桃'];
f.forEach(myPrint); // 这里是函数参数
作用域&闭包
dart中的闭包的实现方式与JS中完全一致。
使用时机:既能重用变量,又保护变量不被污染。
实现原理:外层函数被调用后,外层函数的作用域对象(AO) 被内层函数引用着,导致外层函数的作用域对象无法释放,从而形成闭包
parent() {
// 外层函数的作用域并没有释放,会一直在内存中存在
// 所以money的值被记录了
var money = 1000;
print('parent money: $money');
return () {
// 内层函数每次调用会开辟自己的作用域
// 调用完释放作用域
money -= 100;
print('money:$money');
};
}
var p = parent();
p();
p();
p();
// money:900
// money:800
// money:700
异步函数
异步调用通过Future实现。
async函数返回一个Future,await用于等待Future
import 'package:http/http.dart' as http;
import 'dart:convert';
Future getIpAddress() {
final url = Uri.parse('https://httpbin.org/ip');
return http.get(url).then((res) {
// print(res.body); // 这个body是接口的返回值
String ip = jsonDecode(res.body)['origin'];
return ip;
});
}
void main() {
getIpAddress().then((ip) => print(ip)).catchError((err) => print(err));
}
Future getIpAddress() async {
final url = Uri.parse('https://httpbin.org/ip');
final res = await http.get(url);
String ip = jsonDecode(res.body)['origin'];
return ip;
}
void main() {
final ip = await getIpAddress();
print(ip);
}
类与对象
类是通过class声明的代码段,包含属性和方法。
对象是类的实例化结果。
class Person{}
构造器(构造函数)
默认构造函数
class Point {
num x, y;
// 声明普通构造函数
Point(num x) {
print('我是默认构造函数,实例化时第一个被调用');
this.x = 0;
y = 0; // Dart中 this 可以省略
}
}
class Point {
num x, y;
Point(this.x, this.y); // 省略函数体直接赋值
}
var p = new Point(11, 2);
p.x; // 11
命名构造函数
在类中使用命名构造器(类名.函数名)实现多个构造器,可以提供额外的清晰度
class Point {
num x, y;
Point(this.x, this.y);
Point.origin() {
x = 0;
y = 0;
}
// 命名构造函数
// 函数的命名参数
Point.fromJson({x: 0, y: 0}) {
this.x = x;
this.y = y;
}
}
void main(List<String> args) {
// 默认坐标
Point p1 = new Point.origin(); // 调用命名构造函数
print(p1.x);
// 手动坐标
Point p2 = new Point.fromJson(x: 6, y: 6);
print(p2.x); // 6
}
常量构造函数
如果类生成的对象不会改变,您可以通过常量构造函数使这些对象成为编译时常量
class ImmutablePoint {
// 属性必须通过 final 声明
final num x, y;
// 常量构造函数,通过const声明
// 不可以有函数体
const ImmutablePoint(this.x, this.y);
}
void main() {
// 当通过new的实例化时。常量构造函数可以当作普通构造函数使用
var p3 = new ImmutablePoint(1, 2);
var p4 = new ImmutablePoint(1, 2);
print(p3 == p4); // false
// 正确的声明方式 使用const声明
var p5 = const ImmutablePoint(1, 2);
var p6 = const ImmutablePoint(1, 2);
print(p5 == p6); // true
}
工厂构造函数
通过factory
关键字声明,工厂函数不会自动生成实例,而是通过代码来决定返回的实例。
- class里不能用this
- 避免多次实例化,优化性能
- 单一模式
class Person {
String name;
// static 静态内容
static Person instance;
factory Person([String name = '刘备']) {
// 工厂构造函数不能使用this
if (Person.instance == null) {
// 第一次实例化
Person.instance = new Person.newSelf(name);
}
// 非第一次实例化
return Person.instance;
}
Person.newSelf(this.name); // 使用命名构造函数初始化
}
void main() {
Person p1 = new Person('关羽'); //工厂函数不能用来实例化
print(p1.name); //The getter 'name' was called on null
Person p2 = new Person('张飞');
print(p2.name);
print(p1 == p2); // true
}
访问修饰
Dart
与TS不同,没有访问修饰符。
Dart类中,默认修饰符是公开的(即public)
如果属性或方法以 _ 开头,则表示私有(即private)
可以通过getter方法访问
import './lib//Person.dart';
void main() {
// main() 和 Person 在同一作用域,私有则不起作用
// 只有把类抽离出去,私有才起作用
print(p._money); // 错
}
getter与setter
-
Getter (获取器)是通过get关键字修饰的方法
- 函数没有小括号,访问时也没有小括号(像访问属性-样访问方法)
-
Setter (修改器)是通过set关键字修饰的方法
- 访问时,像设置属性一样给函数传参
class Circle {
final double PI = 3.1415;
num r;
Circle(this.r);
// num area() { 普通方法
// return (this.PI * this.r * this.r).floor();
// }
num get area {
return (this.PI * this.r * this.r).floor();
}
set setR(value) {
this.r = value;
}
}
void main() {
var c = new Circle(10);
print(c.area); // 使用gatter
//通过set修改
c.setR = 20;
}
初始化列表
- 作用:在构造函数中设置属性的默认值
- 时机:在构造函数体执行之前执行
- 语法:使用逗号分隔初始化表达式
- 场景:常用于设置
final
常量的值
class Rect {
int height, width;
// 当类被初始化时,给属性赋值
Rect(): height = 1, width = 1 {
// $访问的时参数。${}访问属性
print('${this.height}----${this.width}');
}
}
class Point {
double x, y, z;
// 初始化列表特殊用法 重定向构造函数
// 把z改成0,x、y时调用twoD()传递来的
Point.twoD(double x, double y) : this(x, y, 0);
}
void main() {
var p2 = new Point.twoD(3, 4); // 这里只能传递x y。z已经被改成0了
print(p2.z); // 0.0
}
static关键字
static关键字用来指定静态成员。通过static修饰的属性是静态属性,通过static修饰的方法是静态方法。
静态成员可以通过类名称直接访问(不需要实例化)
静态方法中不能访问非静态成员,非静态方法可以访问静态成员
静态方法不能使用this关键字
实例化是比较消耗资源的,声明静态成员,可以提高程序性能
class Person {
static String name = '张三';
int age = 18;
static printInfo() {
// print(this.name); // ❌ 不能使用this访问静态属性
print(name); // 直接访问静态书信
// print(this.age); // static方法不能用this 静态方法不能非静态属性
}
printUserInfo() {
print(name); // 非静态方法可以访问静态属性
}
}
void main(List<String> args) {
// 静态成员可以通过类名称直接访问
print(Person.name);
print(Person.printInfo()); // 返回的是null
// Psreon. 只能访问静态的
}
元数据
- 元数据以
@
开头,可以给代码标记些额外信息 。可以用来库、类、构造器、函数、字段、参数或变量声明
元数据 | 含义 |
---|---|
@overrid(重写) | 谋方法添加该注解,表示重写父类中同名方法 |
@required(必填) | 注解dart中的命名参数 |
@deprecated(弃用) | 标记类或方法不在建议使用 |
继承
子类可以使用父类中可见的内容。通过super关键字引用父类可见内容
class Son extends Father{}
抽象类
- 抽象类是用
abstract
关键字修饰的类 - 抽象类的作用是充当普通类的模板,约定一些必要的属性和方法
- 抽象防范是指没有方法体的方法
- 普通类不能有抽象方法
- 抽象类中一般都有抽象方法,也可以没有
- 抽象类不能被实例化,可以被继承,继承后需要实现所有抽象方法
- 可以充当接口被实现,普通类必须实现抽象类里定义的所有属性
abstract class Phone {
void processor(); // 抽象方法
void camera();
}
class Xiaomi extends Phone {
@override
void camera() {
// TODO: implement camera
print('骁龙888+');
}
void processor() {
print('普通方法');
}
}
接口
接口在Dart中就是一个类(只是用法不同)与java不同,java中接口需要用interface关键字声明,Dart不需要。接口可以是任意类,但一般使用抽象类做接口。一个类实现多个接口需要使用逗号分隔。
普通类实现接口后,必须重写接口中的所有属性和方法。
abstract class Processor {}
abstract class Camera {}
class Phone implements Processor, Camera {}
混入(Mixin)
“混入”是一段公共代码。混入有两种声明方式
- 将类当作混入
class MixinA{}
。作为Mixin的类只能继承与Object且不能有构造函数 - 使用
mixin
关键字声明
混入可以提高代码复用的效率,普通类课通过with
实现混入。class A with MixinA
使用多个混入时,后引入的混入会覆盖之前混入中的重复的内容。
// class MixinA extends Father { 只能继承Object类
class MixinA {
String name = 'mixinA';
// MixinA(); // 不能有自己的构造函数
void printA() {
print('A');
}
}
mixin MixinB {
String name = 'mixinB';
void printB() {
print('B');
}
}
class MyClass with MixinA, MixinB {
// 可以使用混入进来的类的方法和属性
}
void main() {
var c = new MyClass();
c.printA(); // A
c.printB(); // B
}
泛型
泛型实在函数、类、接口中指定宽泛数据类型的语法。他们分别称为
- 泛型函数
- 泛型类
- 泛型接口
T getData<T>(T value) {
return value;
}
1、泛型函数
// 泛型函数
T getData<T>(T value) {
return value;
}
void main() {
print(getData<String>('10'));
print(getData<int>(20));
}
Set s = <int>{}; // 泛型set
s.add(1);
2、泛型类
// 泛型类
class GenericsClass<T> {
Set s = new Set<T>();
add(T value) {
this.s.add(value);
}
void info() {
print(this.s);
}
}
void main() {
GenericsClass g = new GenericsClass<String>();
g.add('str'); // {str}
GenericsClass g = new GenericsClass<int>();
g.add('10'); // {10}
}
3、泛型接口
// 泛型接口
abstract class Cache<T> {
getKey(String key);
void setByKey(String key, T value);
}
// 文件缓存
class FileCache<T> implements Cache<T> {
@override
getKey(String key) {
return null;
}
@override
void setByKey(String key, T value) {
print('文件缓存:key=${key} value = $value');
}
}
void main() {
// 文件缓存
FileCache fc = new FileCache<String>();
fc.setByKey('foo', 'bar');
// fc.setByKey('foo', 2); // 报错,已经传递String泛型
}
泛型限制
class SomeBaseClass {}
// 传入的类型必须是继承了SomeBaseClass
class Foo<T extends SomeBaseClass> {
String toString() => "Instance of 'Foo<$T>'";
}
class AnotherClass {// ...}
class Extender extends SomeBaseClass {// 子类}
void main() {
var someBaseClassFoo = Foo<SomeBaseClass>();
print(someBaseClassFoo); //Instance of 'Foo<SomeBaseClass>'
var extendeFoo = Foo<Extender>();
print(extendeFoo); // Instance of 'Foo<Extender>'
// var f = Foo<AnotherClass>(); // 报错了,Another没有继承SomeBaseClass
}
枚举
枚举是数量固定的常量值,通过
enum
关键字声明。final、static声明的变量是分散的。
enum Color { red, green, blue }
void main(List<String> args) {
print(Color.green.index); // 1 通过index 返回枚举中具体常量的值
// 通过 values 返回常量列表
List<Color> colors = Color.values; // [Color.red, Color.green, Color.blue]
print(colors); // 是一个列表
// 通过下标,访问内容
print(colors[0]); //Color.red
colors.forEach((element) => print('value: $element,index: ${element.index}'));
/**
value: Color.red,index: 0
value: Color.green,index: 1
value: Color.blue,index: 2
*/
}
dart 库与生态
Dart中的库就是具有特定功能的模块,可能包含单个文件,也可能包含多个文件。
按照库的作者进行划分,库可以分成三类
-
自定义库(工程师自己写的)
-
系统库(Dart 中自带的)
-
第三方库(Dart 生态中的)
pub命令在D:\File\flutter\bin\cache\dart-sdk\bin
@Dart | @JavaScript |
---|---|
库(library) | 包(package) |
https://pub.dev | https://npmjs.com |
pubspec.yaml | package.json |
pub | npm |
自定义库
通过 library来声明库
每个Dart文件默认都是一个库,只是没有使用library声明
Dart使用下划线开头的标识符,表示库内访问可见(私有)
library关键字声明的库名称建议使用:小写字母+下划线
// 自定义库
library MyCustom; // 大驼峰在dart没事,在flutter有问题
library my_custom;
class MyCustom {}
// 引用
import 'lib/MyCustom.dart';
系统库
是Dart提供的常用内置库,直接引入就可以用
import 'dart:math';
import 'dart:core'; // 自动引入的,写不写都行
void main() {
print(pi); // 3.141592653589793
print(min(3, 6)); // 3
}
第三方库
来源:
- https://pub.dev
- https://pub.flutter-io.cn/packages
- https://pub.dartlang.org/flutter
使用
- 在项目目录下创建pubspec.yaml
- 在pubspec.yaml中声明第三方库(依赖)
- 命令行进入pubspec所在目录,执行
pup get
进行安装 - 项目中引入已安装第三方库
import 'packages:xxx/xxx.dart';
引入部分库
仅引入需要的内容。使用
show
关键字
// 只引入 f1() f2()方法
import 'lib/Common.dart' show f1, f2;
// 引入时,隐藏了f3()方法
import 'lib/Common.dart' hide f3;
引入冲突
// Common和function里都有f1()
import 'lib/Common.dart';
import 'lib/function.dart' as func;
f1();
func.f1(); // 这样调用
延迟引入-懒加载
// 建立关系
import 'lib/function.dart' deferred as func;
void main() {
// func.hello();
print('1');
greet();
print('2');
print('3');
// 执行顺序: 1 2 3 hello
}
Future greet() async {
await func.loadLibrary(); // 调用这个方法才会真的加载
func.hello();
}
part与prt of
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7pl79cCk-1657018032136)(D:\File\learn\Flutter\笔记\images\part.png)]
// 摄像头 Camera.dart
// 与主库建立联系
part of phone;
// 处理器 Processor.dart
// 与主库建立联系
part of phone;
library phone;
// 与分库建立联系
part 'Camera.dart';
part 'Processor.dart';
// 测试
import 'lib/phone/main.dart';
void main() {
Camera c = new Camera();
c.info(); // 是摄像头哦
Processor p = new Processor();
p.info(); // 是处理器哦
}
mon.dart’;
import ‘lib/function.dart’ as func;
f1();
func.f1(); // 这样调用
### 延迟引入-懒加载
~~~dart
// 建立关系
import 'lib/function.dart' deferred as func;
void main() {
// func.hello();
print('1');
greet();
print('2');
print('3');
// 执行顺序: 1 2 3 hello
}
Future greet() async {
await func.loadLibrary(); // 调用这个方法才会真的加载
func.hello();
}
part与prt of
// 摄像头 Camera.dart
// 与主库建立联系
part of phone;
// 处理器 Processor.dart
// 与主库建立联系
part of phone;
library phone;
// 与分库建立联系
part 'Camera.dart';
part 'Processor.dart';
// 测试
import 'lib/phone/main.dart';
void main() {
Camera c = new Camera();
c.info(); // 是摄像头哦
Processor p = new Processor();
p.info(); // 是处理器哦
}