Appearance
简介
官方文档 dart.cn
环境搭建
安装 flutter SDK
1.直接去官网下载并解压 Flutter SDK 即可。Flutter SDK 自带了 Dart SDK 所以无需单独安装 Dart SDK
2.打开电脑高级系统设置配置环境变量找到 path 变量编辑添加一条并粘贴 Flutter SDK 安装位置(如:futterSDK/futter/bin)即可
3.确认是否安装完成
bash
flutter doctor -v4.配置镜像。由于在国内访问 flutter 可能会受到限制,所以我们需要配置镜像。
先在系统高级设置中设置环境变量,添加两个变量。具体变量名与对应值:
PUB_HOSTED_URL=https://pub.flutter-io.cn
FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
安装 vscode 插件
打开 vscode 直接搜索 Flutter 安装插件即可。安装 Flutter 插件后会自动安装 Dart 插件。
安装 JDK
1.Android SDK 的命令行工具依赖 Java ,所以要先安装好 JDK。官网:https://www.oracle.com/java/technologies/downloads/#jdk25-windows。直接下载 .exe 文件然后安装即可。
2.配置环境变量。修改 Path 变量,添加 bin 目录的地址。如: D:\JDK\bin
3.打开命令行工具输入 java 验证。
bash
java安装安卓 SDK
可以直接安装 Android Studio 借助它安装安卓 SDK。如果安装了 Android Studio 这一步就可全部跳过。
1.如果不想安装 Android Studio 可以去官网滑倒底部寻找 “命令行工具” 下载命令行工具包。官网:https://developer.android.google.cn/studio?hl=zh-cn
2.解压及配置:
1)将解压缩的
cmdline-tools目录移至您选择的新目录,例如 android_sdk。这个新目录就是您的 Android SDK 目录。2)在解压缩的
cmdline-tools目录中,创建一个名为latest的子目录3)将原始
cmdline-tools目录内容(包括lib目录、bin目录、NOTICE.txt文件和source.properties文件)移动到新创建的latest目录中。现在,您就可以从这个位置使用命令行工具了官网安装教程:https://developer.android.google.cn/tools/sdkmanager?hl=zh-cn
2.配置环境变量:
1)变量名:ANDROID_HOME 变量值:C:\Android(解压的根目录)
2)修改 Path 变量,添加:%ANDROID_HOME%\cmdline-tools\latest\bin 以及 %ANDROID_HOME%\platform-tools
3.打开命令行工具输入 sdkmanager --list 验证
bash
sdkmanager --list4.安装必要组件。这些组件会安装到第二步配置的 ANDROID_HOME 环境变量对应的目录中。
bash
# platforms;android-34 :提供特定Android版本的框架API和系统库,是应用开发的基础
# build-tools;34.0.0 :包含编译工具链(如aapt、dx、zipalign),负责将Java/Kotlin代码编译为APK,并进行资源打包、签名优化。
# platform-tools :包含Android调试桥(ADB)、Fastboot等核心工具,是设备通信、应用安装/调试、日志查看的基础。
sdkmanager "platforms;android-34" "build-tools;34.0.0" "platform-tools"安装模拟器
如果上一步选择安装 Android Studio 这一步可跳过。
这里以 MuMu 模拟器为例。
1.先安装 MuMu 模拟器。
2.打开模拟器设置中找到网络选项下“网络桥接模式” 并将其打开,重启模拟器。
3.此时用 vscode (需安装 flutter 插件)打开一个 flutter 项目,在 vscode 右下角即可选择刚刚启动的模拟器。
创建第一个项目
第一次创建 flutter 项目建议使用 Android Studio 创建,可以避免很多问题。第一次运行项目会很慢。
当然也可以通过命令行直接创建
bash
flutter create 工程名运行项目
bash
flutter run运行第一个程序
新建 holle.dart 文件并打开终端运行
dart
// 程序的入口:main 函数
void main() {
// 打印字符串
print('holle');
}其他注意事项
适配更多端
如果创建项目时没有适配其他端,可打开原有项目运行如下命令,让项目可以适配其他端
bash
flutter create .设置安卓gradle国内镜像和maven仓库
项目启动或者打包安卓应用时,会下载一些必要的东西,但速度极慢,需要替换为国内镜像
1.打开 flutter 项目 android/gradle/wrapper/gradle-wrapper.properties 设置 distributionUrl 为国内镜像地址
properties
# gradle-wrapper.properties 文件
distributionurl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.14-all.zip2.打开 flutter 项目 android/gradle/wrapper/settings.gradle.kts 修改 gradle 的 maven 仓库镜像地址
properties
# settings.gradle.kts 文件
repositories {
# 在这之间添加如下代码
maven { url = uri("https://maven.aliyun.com/repository/google") }
maven { url = uri("https://maven.aliyun.com/repository/releases") }
maven { url = uri("https://maven.aliyun.com/repository/central") }
maven { url = uri("https://maven.aliyun.com/repository/public") }
maven { url = uri("https://maven.aliyun.com/repository/gradle-plugin") }
maven { url = uri("https://maven.aliyun.com/repository/apache-snapshots") }
maven { url = uri("https://maven.aliyun.com/nexus/content/groups/public/") }
maven { url = uri("https://jitpack.io") }
# 在这之间添加如下代码
google()
mavenCentral()
gradlePluginPortal()
}配置安卓网络权限
打开 flutter 项目 android/app/src/main/AndroidManifest.xml 修改 gradle 的 maven 仓库镜像地址
xml
<!-- AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 添加此条代码 -->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>打包
打包成安卓端程序
bash
# 生成调试版 APK(未签名,用于测试)
flutter build apk --debug
# 生成发布版APK(需要签名配置)
flutter build apk --release运行到鸿蒙端
1.需要下载鸿蒙版的 flutter SDK,并修改 flutter SDK 环境变量
bash
git clone https://gitcode.com/openharmony-tpc/flutter_flutter.git2.安装 DevEcoStudio 下载地址:https://developer.huawei.com/consumer/cn/download/
并在编辑器中安装鸿蒙模拟器
3.配置鸿蒙环境变量。
4.如果一开始是通过原版 flutter 创建的项目,可能需要降低 Dart SDK 的版本以及一些插件的版本。直接打开项目下 pubspec.yaml 文件修改对应版本即可,然后运行如下命令。
bash
# 清理依赖并重新安装依赖
flutter clean && flutter pub get
# 可选 如果一开始是通过原版 flutter 创建的项目,此处需要执行如下命令来适配鸿蒙端
# flutter create .5.解决兼容性问题。有些插件可能不支持鸿蒙端,但是如下仓库内有许多社区提供了适配版本,需要打开项目下 pubspec.yaml 文件将对应有适配问题的插件使用 git 仓库模式安装
yaml
shared_preferences:
git:
url:"https://gitcode.com/openharmony-sig/flutter_packages.git"
path:"packages/shared_preferences/shared_preferences"main 函数
给 main 函数设置参数并传参
dart
// 先写入参数,后打开命令行运行此文件:dart test.dart 1,23
// 文件名后面就可以传递参数
void main(List<String> args) {
print(args);// [1,23]
}基础语法
变量
使用 var 定义一个变量。变量名必须由字母、数字、下划线、$ 组成。不可以以数字开头。
dart
// 变量默认值是 null
// 变量的值不会进行隐式转换
void main() {
// 定义变量,变量支持类型推断
var a = 1;
a = 2;
// a='ddd' 报错
print(a);
}常量
定义常量使用 const 和 final
dart
void main() {
const a = 1;
final b = 2;
const f = 3;
// a=2; 报错,常量不可修改
// b=3; 报错,常量不可修改
var c = 1;
var d = 2;
// const res = a + b; 报错 const 定义的常量是编译期常量,值是在编译时赋值
const res2 = a + f; // 如果都是编译期常量可以赋值给 const
final res3 = a + b; // 不报错,final 定义的常量是运行时常量,值是在运行时赋值
print(res2); // 4
print(res3); // 3
const time = DateTime.now();// 报错,无法将运行时的值分配给 const 常量
final time2 = DateTime.now();// 成功,可以将运行时的值分配给 final 常量
}枚举
dart
// 通过 enum 声明枚举,它是数量固定的常量值
enum Color { red, green, blue }
void main() {
// 访问枚举某个值的索引
print(Color.green.index); // 1
// 访问枚举整个列表
print(Color.values); // [Color.red, Color.green, Color.blue]
}数据类型
dart 中分为如下 5 中常用数据类型
dart
// num (数值) bool(布尔) String(字符串) List(列表) Map(字典)数值
dart
void main() {
num n = 2; // num 用来定义整数或浮点数
n = 2.3;
int n2 = 3; // int 用来定义整数
// n2 = 3.3; // 报错,int 不能用来定义浮点数
double n3 = 4.4; // double 用来定义浮点数
n3 = 4;// 如果 double 赋值一个整数,实际上还是转成了浮点数保存
print(n3); // 4.0
// 常用 api
n3.toString(); // 类型转换
n3.abs(); // 绝对值
n3.toStringAsFixed(4); // 保留 4 位小数
int f = 2.5.toInt();// 会将浮点数转换成整数,当是小数时但会舍弃小数部分
print(f); // 2
double f1 = 2.toDouble();// 会将数值转换成浮点数,当是整数时会在末尾添加 0
print(f1); // 2.0
num n = 0 / 0;
print(n); // NaN
print(n.isNaN); // true
}
// int.parse('2', radix: 10) 将字符串转换为整数。radix 表示转换为的进制,比如这里就是转换为 10 进制
// double.parse('2') 将字符串转换为浮点数。布尔值
dart
void main() {
bool a = true;
a = false;
}字符串
dart
void main() {
String str = 'holle';
print(str);
// 字符串拼接
double price = 199.0;
String res = '${price}元';
// 可写成 '$变量' 或 '${表达式}'
print(res); // 199.0元
String s = """
三个引号可以换行
""";
// api
print("str".split(''));// [s, t, r] 和 js 中 split 方法一致
print('str'.replaceAll('s', '')); // tr 和 js中替换字符串api一致
// 支持正则替换
print('s2t3r44'.replaceAll(RegExp(r'\d+'), '')); // str
print('erf'.contains('e'));// true
print('erf'.indexOf('e')); // 0
var str = '';
// String.isEmpty是判断是否是空白字符串,他并不是null,不是空,依然有值,只不过值是空白字符串""
print(str.isEmpty); // true
// isNotEmpty跟isEmpty完全相反
print(str.isNotEmpty); // false
// String.split 字符串切割成列表
// String.replaceAll 字符串替换
// String.contains 字符串查找,检测字符串是否包含某个字符
// String.indexOf 找出某个字符出现的索引
// String.isEmpty 判断是否是空白字符串(即 "")
// String.isNotEmpty 与 isEmpty 相反
}列表
dart
void main() {
List arr = [1, '2', 3];
print(arr.length); // 列表长度
print(arr[2]); // 查询列表某一项
arr[2] = '3'; // 修改
print(arr);
arr.add('3'); // 在列表末尾添加一个元素
print(arr);
arr.addAll(['t', 'y']); // 在列表末尾添加多个元素
print(arr); // [1, 2, 4, value, t, y]
arr.remove('3'); // 根据元素名删除(删除一个),这里是删除字符串类型的3
print(arr);
arr.removeAt(0); // 根据索引删除元素
print(arr);
List a = [1, 2, 3, 4, 5];
a.removeRange(0, 2);
print(a);// [3, 4, 5]
// 列表遍历
arr.forEach((element) {
print(element);// 打印列表的每一项
});
// 注意如下代码中打印语句不会执行(forEach 不会出现此情况)可使用 b 变量或者在后方调用 toList才会执行
List a = [1, 2, 3, 4, 5];
var b = a.map((e) {
print('1');
return e * 2;
});
var list = List.generate(3, (index) => index);
print(list); // [0, 1, 2]
List a = [1, 2, 3];
List a2 = [...a, 4, 5, 6];
Map o = {'a': 2};
Map o2 = {...o, 'k': 4}; // 展开运算符和js中使用类似
print(a2); // [1, 2, 3, 4, 5, 6]
print(o2); // {a: 2, k: 4}
// 创建列表,当 empty 方法参数 growable 为true时表示创建一个可以改变长度的列表
var l3 = new List.empty(growable: true);
l3.add(1);
print(l3); // [1]
var l4 = new List.filled(3, 6);
print(l4); // [6, 6, 6]
List a = [1, 2, 3];
var l;
print([...?l, ...a]); // [1, 2, 3]
print([2, 3, 4].reversed); // (4, 3, 2)
print([2, 3, 4].reversed.toList()); // [4, 3, 2]
List a = [1, 2, 3];
a.insert(1, 4);
a.insertAll(1, [5, 6]);
print(a);
print(['123', '456'].join('-')); //123-456
var a = [1, 2, 3].map((e) {
return e * e;
});
print(a); //(1, 4, 9)
print([1, 2, 3].where((element) {
return element <= 2;
})); //(1, 2)
print([1, 2, 3].any((element) {
return element <= 2;
})); //true
print([1, 2, 3].every((element) {
return element <= 2;
})); //false
var a = [[1],[2]];
var a2 = a.expand((element) => element);
print(a2); // (1,2)
print([1, 2, 3].fold<num>(2, (p, element) => p * element)); // 12
}
// List.add 在列表末尾添加一个元素
// List.addAll 在列表末尾添加多个元素
// List.remove 根据元素名删除(删除一个)
// List.removeRange 删除一个范围的元素:removeRange(0, 2),将删除列表索引0到2(不包含2)的元素
// List.removeAt 根据索引删除元素
// List.removeLast 删除列表最后一个元素
// List.forEach 列表遍历
// List.isEmpty 判断列表是否为空,返回一个布尔值
// List.isNotEmpty 判断列表是否不为空,返回一个布尔值
// new List.empty 创建列表的一种方式,当参数为true时表示创建的列表可以改变长度(可以添加元素)
// new List.filled 创建列表的一种方式,参数一表示创建列表的长度,参数二表示要填充的元素
// [...a] 和js展开运算符类似,还可以展开字典
// [...?a] 当a不能被展开时不会执行展开操作避免报错
// List.reversed 列表翻转,反转后不是列表
// List.reversed.toList() 将reversed反转后的数据转化为列表,也可使用 [...a]来转换成数组
// List.insert 在列表指定索引插入元素
// List.insertAll 在列表指定索引插入多个元素
// List.join 将数组拼接成字符串,和 js 中使用类似
// List.map 遍历列表对列表进行一些操作并返回一个由括号包裹的可迭代数据,需要转换为列表
// List.where 遍历列表返回符合条件的可迭代数据,需要转化为列表
// List.any 查找该列表是否有符合条件的元素,返回一个布尔值
// List.every 查找该列表里的元素是否全符合条件,返回一个布尔值
// List.expand 列表降维默认只能将二维转化成一维
// List.fold 和 js 中 reduce 相似
// List.generate 生成指定长度的列表,使用:List.generate(3, (index) => index)Set
dart
// Set 是一个无序的,元素唯一的集合。
void main() {
// 字面量方式创建 Set
var s = <int>{1, 2, 3};
// 构造函数创建。在 Dart 中 new 可以省略
var fr = new Set();
fr.add('1');
fr.add('2');
fr.addAll([3, 4]);
print(fr); // {1, 2, 3, 4}
print([1, 2, 2, 3].toSet().toList()); // [1, 2, 3] 利用Set给列表去重
// 求交集
print({1, 2, 3, 4}.intersection({1, 2, 5, 6})); // {1, 2}
// 求并集
print({1, 2, 3, 4}.union({1, 2, 5, 6})); // {1, 2, 3, 4, 5, 6}
// 求差集
print({1, 2, 3, 4}.difference({1, 2, 5, 6})); // {3, 4}
// 返回第一个
print({1, 2, 3, 4}.first); // 1
// 返回最后一个
print({1, 2, 3, 4}.last); // 4
// 集合不能通过下标取值
print({1, 2, 3, 4}[1]); // 报错
// 和 js 类似可以用 Set 对列表去重
var arr = [0, 0, 1, 2, 2];
var fr1 = {...arr};
print(fr1); // {0, 1, 2}
// 可以用 for in 遍历 Set
var s = {1, 2, 3, 4, 5};
for (var item in s) {
print(item);
}
}Map
dart
// Map 是键值对,泛型第一个类型是键,key,第二个是值,value。其中键、值都可以是任何类型
var m = Map<int, String>();
// 添加或修改键值对
m[0] = 'one';
print(m); // {0: one}
Map m = {};
// 判断当前 Map 是否为空
print(m.isEmpty); // true
// 还可以这样定义 Map
var map = {'name': '张三', 2: 18};
// addEntries 添加一组数据
map.addEntries([MapEntry('age', 18), MapEntry('sex', '男')]);
print(map); // {name: 张三, 2: 18, age: 18, sex: 男}
var map = {'name': '张三', 2: 18};
// 遍历 Map 并返回一个新的 Map
print(map.map((key, value) => MapEntry(key, '123'))); // {name: 123, 2: 123}
var map = {'name': '张三', 2: 18};
// entries.map 返回一个选代器,可以是任何类型,这样就可以把 Map 转换为其他类型的选代器,再转为数组
var it = map.entries.map((e) {
return 'key:${e.key},value:${e.value}';
});
print(it); // (key:name,value:张三, key:2,value:18)
var l = [1, 2, 3];
// asMap 可以将列表转换为 Map
print(l.asMap()); // {0: 1, 1: 2, 2: 3}
var map = {'name': '张三', 2: 18};
// forEach 遍历 Map
map.forEach((key, value) {
print('key:$key,value:$value');
});
Map m = {"1": 'r', "2": 'e', "3": 'p'};
// for in 循环遍历 Map
for (var key in m.keys) {
print('key:$key, value:${m[key]}');
}
// Map.containsValue 检测字典中是否含有某个值
// Map.keys 列举字典中所有的键
// Map.values 列举字典中所有的值
// Map.removeWhere 遍历字典根据条件删除对应元素dynamic
dart
// dynamic 可以保存任意类型的数据,编码灵活
void main() {
// 使用 dynamic 声明的变量会关闭编译器的类型检查
dynamic a = 20;
// runtimeType 可以获取变量运行时的类型
print(a.runtimeType); // int
a = '30';
print(a); // 也是可以正常使用的
print(a.runtimeType); // String
}其他数据类型
基本使用
可选类型
dart
// 可选类型,可空类型
int? i;
print(i); // null
// 对比普通 int 类型可以赋值为 null
i = null;
// 可选类型是不可以直接比较的,因为有可能是 null
print(i>10);// 报错,编译不通过
// 如果你确定一定不为 null 可以使用 ! 强制解包来解决
print(i!>10);// 编译通过,但运行出错类型别名
dart
void main() {
int add(int a, int b) {
print(a + b);
return a + b;
}
// 使用
Add fn = add;
fn(1, 2);
}
// 类型别名,注意不能写在 main 函数内
typedef Add = int Function(int, int);as 类型转换
dart
dynamic d1 = '1223';
var s = d1 as String;安全机制
dart
void main() {
String str2 = 'lol';
print(str2.length); // 3
String str;
print(str.length); // 报错
// 解决
// 使用 ? 明确指定的变量可以为空
String? str3;
// 表示非空检查,如果 str3 为空则不会调用属性或方法
print(str3?.length);
}运算符
算术运算符
dart
void main() {
int n1 = 10;
int n2 = 3;
print(n1 + n2); // 13 加
print(n1 - n2); // 7 减
print(n1 * n2); // 30 乘
print(n1 / n2); // 3.3333333333333335 除
print(n1 ~/ n2); // 3 取整:取除法结果的整数部分
print(n1 % n2); // 1 取余
// print(n1++); // 10 自增
// print(n2--); // 3 自减
print(++n1); // 11 自增
print(--n2); // 2 自减
}赋值运算符
dart
void main() {
int n1 = 10;
double n2 = 10;
// print(n1 += 1); // 11 加等于
// print(n1 -= 1); // 9 减等于
// print(n1 *= 2); // 20 乘等于
print(n2 /= 2); // 5.0 只有 double 类型数据才能进行除等于运算
}比较运算符
dart
void main() {
int n1 = 10;
int n2 = 20;
print(n1 > n2); // false 大于
print(n1 < n2); // true 小于
print(n1 >= n2); // false 大于等于
print(n1 == n2); // false 等于
print(n1 != n2); // true 不等于
}逻辑运算符
dart
void main() {
int n1 = 0;
int n2 = 5;
print(n1 > 28 && n2 > 4); // false 一假即假
print(n1 > 28 || n2 > 4); // true 一真即真
print(!(n1 > 28 || n2 > 4)); // false 取反
}地板除
dart
// 地板除是先进行除法再将结果向下取整
print(7 ~/ 4); // 1is 运算符
dart
var str = 'Hello World';
// is 运算符可以判断一个变量是否属于一个类型或者他的子类型
print(str is String); // true
// is! 跟 is 完全相反,当运算符 is! 左边的变量不属于右边的类型,或者不属于右边的子类型,才返回 ture
print(str is! String); // false
// 还可以用来判断左边是否是右边的子类
print(str is Object); // true避空运算符
dart
// 表示如果左边为空则取右边的值,和或运算符类似
print(null ?? 3); // 3
// 另一种用法
var a;
// if (a == null) {
// a = 3;
// }
// 上面注释的代码可用避空运算符实现
a ??= 3;?. 运算符
dart
// 保护为空的属性,当不存在某一属性时不会去访问防止报错
var obj;
print(obj?.length); // null级联运算符
dart
// myobj.myMethod();返回的是 myMethod 方法的返回值
// myobj..myMethod();返回的是 myobj 对象的引用
Set s = new Set();
// s.add(1);
// s.add(2);
// s.remove(2);
// 下面写法利用级联运算符可实现上面一样的操作
s..add(1)..add(2)..remove(2);
print(s);循环
for 循环
dart
for (int i = 0; i < 3; i++) {
print(i);
}for in 循环
dart
List m = [1, 2, 3];
for (int item in m) {
print(item);
}while 循环
dart
int m = 1;
while (m < 3) {
print(m++);
}do while 循环
dart
// do while 循环会先执行 do 里面的代码再走 while 里的判断
int m = 1;
do {
print(m++);
} while (m < 3);函数
dart
// void 表示该函数无返回值(默认返回null),函数也有自己的类型是 Function
void main() {
print(sum(2, 3));
}
// 返回两数之和的函数,sum 也可以写在 main 函数体内,不过必须写在调用之前
int sum(int a, int b) {
return a + b;
}
// 借助类型推断可以简写为如下形式,此时参数的类型是 dynamic 动态类型,什么类型都能接收
sum( a, b) {
return a + b;
}
// 定义可选参数和给可选参数指定默认值,这种写法在调用的时候需要以键值对的方式传入
sum(int a , int b,{int? c,String? d='2'}) {
print(a + b);
}
// 这种写法在调用的时候需要以键值对的方式传入,这里的 c、d参数需要如下写法传入
sum(1, 2,c:2,d:'3');
// required 表示参数必填
fn(int a, int b, {int c = 9, required String d}) {
print('a+b=$a+$b=$c+$d');
}
// 这种写法在调用的时候需要以键值对的方式传入,这里的 c、d参数需要如下写法传入
fn(1, 2, d: 'hello'); // a+b=1+2=9+hello
// 可选参数的另一种写法,这种写法表示 c 可选且默认值是 0
sum(int a, int b, [int c = 0]) {
print(a + b);
}
sum(1, 2,3);箭头函数
dart
// 箭头函数的函数体里只能有一条语句
// 箭头函数仅仅只是普通函数的简写方式不同于 js 里的箭头函数有其他的作用
void main() {
print(sum(2, 3));
}
sum(int a , int b) {
return a+b;
}
// 上面函数可简写为如下箭头函数形式
sum(int a , int b) => a+b;立即执行函数
dart
((int n) {
print(n);
})(17); // 17函数类型
dart
void main() {
int add(int a, int b) {
return a + b;
}
// 给函数执行类型
int Function(int, int) fn = add;
fn(1, 2);
}作用域
dart 中的作用域和 js 中一致
闭包
dart 中闭包和 js 中一致
类
基本使用
在 Dart 中所有的类都继承自 object
dart
void main() {
// 实例化
Person p = Person('张三', 18);
// 调用类方法
p.printInfo();
}
// 自定义类只能编写在 main 函数之外
class Person {
String name;
int age;
// 默认构造函数
/*
Person(String name, int age) {
this.name = name;
this.age = age;
}
可简写为如下写法
*/
Person(this.name, this.age);
void printInfo() {
print('姓名:${this.name},年龄:${this.age}');
}
// @override 表示重写父类的方法
@override
String toString() {
return 'name:$name,age:$age';
}
}构造函数
默认构造函数
在实例化时就会执行
dart
void main() {
Person p = Person();
}
class Person {
Person() {
print('这是构造函数里面的内容,这个方法在实例化的时候触发');
}
}命名构造函数
dart
void main() {
// Person p = Person(); 默认实例化类的时候调用的是默认构造函数
// 如果想调用其他构造函数来创建实例化,可调用命名构造函数
Person p = Person.withName('张三');
print(p.age);
print(p.jiao());
}
class Person {
Person(this.name, this.age);
// 命名构造函数可以有多个
// Person.withName(String name){
// this.name = name;
// }
// 上面可简写为如下格式
Person.withName(this.name);
String name = '小名';
num? age;
String jiao() {
return '${this.age}${this.name}';
}
}常量构造函数
dart
// 如果类生成的对象不会改变,可以通过常量构造函数使这些对象成为编译时常量,可以提高性能
class Point {
num x;
Point(this.x);
}
class ImmutablePoint {
// 属性必须通过 final 声明
final x;
// 常量构造函数必须通过 const 声明,且不能有函数体
const ImmutablePoint(this.x);
}
void main() {
// 创建实例对象时 new 关键字可以省略不写
var p1 = new Point(1);
var p2 = new Point(1);
print(p1 == p2); // false
// 声明不可变对象必须通过 const
var p3 = const ImmutablePoint(1);
var p4 = const ImmutablePoint(1);
print(p3 == p4); // true
// 常量构造函数可以当做普通构造函数使用
var p5 = new ImmutablePoint(1);
var p6 = new ImmutablePoint(1);
print(p5 == p6); // false
}工厂构造函数
dart
// 不会直接创建对象,而是在构造函数内部通过代码来决定要创建的对象
void main() {
// 使用工厂构造函数创建对象
Person p = Person.withInfo('张晓', 19);
print(p.age);
print(p.jiao());
}
class Person {
Person(this.name, this.age);
Person.withName(this.name, this.age);
// 工厂构造函数必须返回一个对象,可以是之前定义的类名构造函数
factory Person.withInfo(String name, int age) {
// 工厂函数内不能访问 this
// 工厂函数不能被实例化
// return Person.withName(name, age);
return age < 0 ? Person.withName(name, 0) : Person.withName(name, age);
}
String name = '小名';
num? age;
String jiao() {
return '${this.age}${this.name}';
}
}
class Person {
String name = '';
static dynamic instance = null;
// 工厂构造函数主要是用来避免多次实例化重复实例化出来多个对象,提高性能
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 p = new Person('小米');
print(p.name); // 小米
Person p2 = new Person('小米2');
print(p2.name); // 小米
}私有属性和方法
test.dart
dart
// 只有把类单独抽离出去私有属性和方法才起作用,写在一个文件中不起作用
class Dog {
// 公有属性
String name = '小名';
// 私有属性 在名称前加上 _ 表示私有,私有属性只能在本类中使用
int _age = 20;
// 公有方法
void jiao() {
print('jiao');
}
// 私有方法
void _run() {
print('jiao');
}
}holle.dart
dart
// 导入其他文件
import 'test.dart';
void main() {
Dog dog = Dog();
dog.name = '大黄';
dog.jiao();
// dog.run(); 报错,无法访问私有属性或方法
}getter 和 setter
dart
class Circle {
final double PI = 3.14;
num r;
Circle(this.r);
// 使用 get 声明的方法不能有小括号
num get area {
return this.PI * this.r * this.r;
}
set setR(value) {
this.r = value;
}
}
void main() {
Circle c = new Circle(2);
print(c.area); // 12.56
c.setR = 3;
print(c.area); // 28.259999999999998
}初始化列表和重定向构造函数
dart
class Rect {
int height;
int width;
// Rect([int height = 2, int width = 2]) {
// this.height = height;
// this.width = width;
// print('${this.height} -- ${this.width}');
// }
// 初始化列表,上面的构造函数赋默认值写法可简写为如下代码
Rect()
: height = 4,
width = 5 {
print('${this.height} -- ${this.width}');
}
}
void main() {
Rect c = new Rect();
}
////////////////////////////////////////////////////
class Point {
double x, y, z;
Point(this.x, this.y, this.z);
// 初始化列表特殊用法(重定向构造函数)
// 这里表示如果值传递两个参数,就用 Point 构造函数去初始化值,这里的 this 代表类本身
Point.towD(double x, double y) : this(x, y, 0);
}
void main() {
Point c = new Point.towD(1, 2);
}static
dart
// 通过 static 来声明静态成员,静态成员可以通过类名称直接访问(不需要实例化)
// 实例化是比较消耗性能的,声明静态成员可以提高程序性能
class Person {
static String name = '张三';
int age = 2;
printInfo() {
// 静态方法可以访问静态成员以及非静态成员
// print(this.name); 不能通过 this 访问静态属性或方法
// print(age); 静态方法中不能访问非静态属性以及方法
print(name);
}
printInfo2() {
// 非静态方法可以访问静态属性
print(name);
}
}
void main() {
print(Person.name); // 静态成员可以直接通过类名访问
// print(Person.printInfo2); 不能直接使用类名非访问静态方法或属性
}元数据
dart
// 元数据以@开头可以给代码标记一些额外的信息,仅作为一个标注和提示,对代码运行无影响
// @override(重写) 某方法添加该注解后表示重写了父类中同名方法
// @required(必填) 可以通过它来注解 Dart 中的命名参数,用来指示它是必填参数
// @deprecated(弃用) 若某类或方法加上此注解后,表示此方法或类不再建议使用
class Phone {
// 这表示 activate 即将被弃用
@deprecated
activate(){
turnOn();
}
turnOn(){
print('开机');
}
}
void main() {
Phone p = Phone();
p.activate();
}继承
dart
void main() {
Zi z = Zi('张三', '男');
print(z.name);
z.jiao();
}
class Fu {
String name;
Fu(this.name);
void jiao() {
print('jiao$name');
}
}
// dart 的继承是单继承,一个子类只能有一个父类
// 类使用 extends 关键词来继承父类
// 子类会继承父类里面可见的(除私有以及静态)属性和方法但是不会继承构造函数
class Zi extends Fu {
String sex;
String name;
// 当父类初始化时需要参数,子类需要调用 super 将参数传递过去,也就是调用父类的构造函数
Zi(this.name, this.sex) : super(name);
// 子类可以重写父类的方法
@override
void jiao() {
// 可以通过 super 关键字来调用父类的方法
// super.jiao();
print('子类重写父类的方法');
}
}抽象类
dart
// 抽象类不能被实例化
// 通过 abstract 声明抽象类用来做类的模板
abstract class Phone {
// 抽象类中可以没有抽象方法也可以有
void processor(); // 声明抽象方法
void camera(); // 声明抽象方法
void info() {
print('抽象类中可以有普通方法');
}
}
class Xiaomi extends Phone {
// 普通类中不能有抽象方法
// 普通类继承抽象类必须实现抽象类中所有的抽象方法
// 使用元数据标记这是一个重写的方法
@override
void processor() {
print('骁龙888');
}
@override
void camera() {
print('三星');
}
}
void main() {
Xiaomi p = Xiaomi();
p.processor();
}接口
dart
// 接口就是一个类,接口可以是任意类,但一般使用抽象类做接口
// 一个类可以实现(implements)多个接口
// 普通类实现接口后必须重写接口所有的属性和方法
// 使用抽象类来当做接口
abstract class Processor {
late String cores;
arch(String name);
}
abstract class Camera {
late String resolution;
brand(String name);
}
// 通过普通类来实现接口
class Phone implements Processor, Camera {
// 实现属性
String cores;
String resolution;
// 实现方法
@override
arch(String name) {
print('实现接口');
}
@override
brand(String name) {
print('实现接口');
}
Phone(this.cores, this.resolution);
}
void main() {
Phone p = Phone('2', '3');
p.brand('3');
p.arch('3');
}mixin
dart
// 可以为类扩展功能,而不需要使用继承
// 可以扩展属性和方法
// 不能被实例化,不能被继承
// 一个类支持 with 多个 mixin,调用优先级遵循“后来居上”原则,即后混入的会覆盖先混入的同名方法
void main() {
zi z = zi();
s s1 = s();
fu f = fu();
z.walk();
s1.walk();
f.walk();
print(f.sex);
}
// 定义 mixin
mixin WalkMixin {
void walk() {
print('walk');
}
}
// 定义 mixin
mixin sexMixin {
String sex = '男';
}
// 可以使用多个 mixin
class fu with WalkMixin, sexMixin {
String name = '大黄';
void jiao() {
print('jiao');
}
}
class s with WalkMixin {}
class zi extends fu with WalkMixin {
void jiao() {
print('jiao2');
}
}
////////////////////////////////////////////////////////////////////////
// 通过类的方式声明混入,这种方式 MixinA 不能继承除 Object 之外的类,也不能有构造函数
class MixinA {
String name = 'MixinA';
printA() {
print(name);
}
}
// 通过 mixin 关键字声明混入
mixin MixinB {
String name = 'MixinB';
printB() {
print(name);
}
}
class Myclass with MixinB, MixinA {}
void main() {
Myclass p = Myclass();
// 使用多个混入时后引入的混入会覆盖之前混入中重复的内容
print(p.name); // MixinA
p.printA(); // MixinA
p.printB(); // MixinA
}异步编程
Dart 是单线程语言,即同时只能做一件事,遇到耗时任务就会造成程序阻塞,此时需要异步编程。
Dart 采用单线程+事件循环的机制完成耗时任务的处理
事件循环:执行同步代码 => 执行微任务队列 => 执行事件队列 => 结束
微任务队列:Future.microtask()
事件队列:Future、Future.delayed()、I/O操作(文件、网络)等
Future
Future 代表一个异步操作的最终结果
有三种状态:Uncompleted(等待)、Completedwithavalue(成功)、Completedwithaerror(失败)
执行成功:不抛出异常就是成功状态
dart
void main() {
Future f = Future(() {
// 没有抛出异常那就是成功状态
return "HelLo Flutter";
// 抛出异常就是失败状态
// throw Exception('出错了');
});
// then 中接收成功状态
f.then((value) {
print(value); // HelLo Flutter
});
f.catchError((error) {
print('发生错误$error'); // 发生错误出错了
});
}如下代码打印顺序为:
开始执行 => 假装这是不能被阻塞的代码 => Future => 这是异步任务执行的结果
dart
import 'dart:io';
void main() {
print('开始执行');
// Future 可以用来解决异步编程问题,写在这里的耗时任务不会立即执行
Future(() {
print('Future');
// 耗时任务执行的地方
sleep(Duration(seconds: 5)); // 表示程序在这里会延时 5 秒,本身 sleep 是同步代码
return '这是异步任务执行的结果';
})
.then(
(value) => {
// 这里表示异步任务执行完成就会执行这里,value 可以接收到异步任务返回的数据
print(value),
},
)
.catchError((error) {
// 这里可以捕获异步任务的错误
print(error);
});
print('假装这是不能被阻塞的代码');
}链式调用
Future 可以通过链式的方式连续得到异步的结果
在上一个 then 返回对象会在下一个 then 中接收
dart
// 如下代码会打印 task1-task2-task3
void main() {
Future(() => "task1")
.then((value) {
// 上一个 then 返回对象会在下一个 then 中接收
// 第二个任务
return Future(() => "$value-task2");
})
.then((value) {
// 第三个任务
return Future(() => "$value-task3");
})
.then((value) {
print(value); // task1-task2-task3
// throw Exception("异常");
})
.catchError((error) {
print("出现错误");
});
}async 简化
除了通过 then/catchError 的方式,还可以通过 async/await 来实现异步编程
await 总是等到后面的 Future 执行成功,才执行下方逻辑,async 必须配套 await 出现
dart
void main() {
test();
}
test() async {
String res =
await Future(() {
return 'hello world';
// throw Exception();
}).catchError((onError) {
// 在这里捕获错误
print('出错了');
});
// await 下方的逻辑会在 Future 执行成功后执行
print(res); // hello world
}泛型
使用泛型指定类型
dart
void main() {
// List arr = ['s',1,true]; 不指定泛型时 List 是可以存储任意类型的元素的
List<String> arr = ['s', '2']; // 通过泛型指定 List 中保存指定类型
// 不指定泛型时 Map 中 key 可以是任意类型
// Map obj = {'id': 1, 'name': "居家", 1: 2, true: 1};
// 使用泛型指定 Map 键和值的类型
Map<String, String> obj = {'id': '1', 'name': "居家", '1': '2', 'true': '1'};
// 还可以这样写
List a = <int>[2,3];
Map m = <String,String>{
'2':''
};
}使用泛型简化代码
dart
void main() {
String s(String str) {
return str;
}
int a(int str) {
return str;
}
// 上面的两个函数可以利用泛型写成一个
T fn<T>(T info) {
return info;
}
}
// 泛型类
class P<T> {
getinfro(T s) {
print(s);
}
}
void main() {
P p = P<String>();
p.getinfro('2');
}泛型类型限制
dart
class Somether {}
class Anoth {}
// 限制参数类型必须继承 Somether 或 Somether 子类
class Foo<T extends Somether> {
getinfo() {
print(T);
}
}
void main() {
Foo f = new Foo<Somether>();
f.getinfo();
}异常
dart
void main() {
try {
dynamic a = 'ss';
a.haha();
} catch (error) {
print('出错');
} finally {
// 无论是否出现异常都会执行这里的代码
}
String s = 'zxc';
if (s == 'zxc1') {
print('ok');
} else {
// 手动抛出异常
throw '字符串不相等';
}
}dart 库与生态
dart
// https://pub.flutter-io.cn/ 对标js中的 npm自定义库
dart
// 每个 Dart 文件默认都是一个库,只是没有使用 library 来显示声明
// Dart 使用 _(下划线)开头的标识符,表示库内访问可见(私有)
// library 关键字声明的库名称建议使用: 小写字母 + 下划线index.dart
dart
import './test.dart';
void main() {
Dog m = new Dog();
print(m.name);
}test.dart
dart
// library 关键字声明的库名称建议使用: 小写字母 + 下划线
library my_dog;
class Dog {
String name = '小名';
void info() {
print('自定义库');
}
}系统库
dart
// import 'dart:系统库名称';
import 'dart:math';
// import 'dart:core'; // core 库即使不手动引入也会默认引入
void main() {
print(pi); // 3.141592653589793 获取圆周率
print(min(3, 6)); // 3
print(max(3, 6)); // 6
}按需引入
dart
// show 表示只引入后续的内容(除了后续指定的内容,其他的都不引入)
import './test.dart' show f1, f3;
// hide 表示隐藏后面的内容(除了后续指定的内容,其他的都引入)
import './test.dart' hide f1, f3;
void main() {
f2();
}引入冲突
dart
import './test.dart';
// 当多个包里的名称重名时使用 as 添加前缀解决
import './test2.dart' as test2;
void main() {
f2();
test2.f2();
}test.dart
dart
f2() {
print('f2');
}test2.dart
dart
f2() {
print('f2');
}异步引入(懒加载)
dart
// 异步加载此时只是和该库建立联系并未真正引入
import './test.dart' deferred as func;
void main() {
greet();
}
Future greet() async {
// loadLibrary 函数返回一个 Future 表示等待库引入,这时可以使用 async 或者 then 来指定引入完成后要干的事
await func.loadLibrary();
func.f1();
}主库和分库
分库使用 part of 和主库建立联系,主库使用 part 和分库建立联系,最终组成一个完整的库