文章目录
- Flutter必备Dart基础:Dart快速入门
- 学习资料
- 4-1 Flutter之Dart概述
- 4-2 Flutter之Dart常用数据类型(数字、类型转换)
- 4-3 Flutter之Dart常用数据类型(字符串)
- 4-4 Flutter之Dart常用数据类型(布尔, List)
- 4-5 Flutter之Dart常用数据类型(Map)
- 4-6 Flutter之Dart常用数据类型(科普小姿势)
- 4-7 带你揭开Flutter中的面向对象(标准构造方法、初始化列表)
- 4-8 带你揭开Flutter中的面向对象(命名构造方法)
- 4-9 带你揭开Flutter中的面向对象(工厂构造方法)
- 4-10 带你揭开Flutter中的面向对象(命名工厂构造方法)
- 4-11 带你揭开Flutter中的面向对象(get和set,静态方法)
- 4-12 带你揭开Flutter中的面向对象(抽象类和方法)
- 4-13 带你揭开Flutter中的面向对象(mixins)
- 4-14 带你解锁Flutter中常用的Dart方法类型
- 4-15 带你了解Dart泛型在Flutter中的应用
- 4-16 有哪些可以在Flutter上的编程技巧
- 4-17 小结
Flutter必备Dart基础:Dart快速入门
学习资料
4-1 Flutter之Dart概述
Dart
特性:
JIT
:即时编译,开发期间,更快编译,更快重载。
AOT
:事前编译,release期间,更快更流畅。
4-2 Flutter之Dart常用数据类型(数字、类型转换)
常用数据类型:
创建dart_type.dart
option
+ 回车
引入头文件
option
+ 回车
创建方法
int
类型和double
类型是num
类型的子类。
常用API:
num num1 = -1.0; // 是数字类型的父类
num num2 = 2; // 是数字类型的父类
int int1 = 3; // 只能是整数,int是num的子类
double d1 = 1.68; // 双精度,double是num的子类
print("num:$num1 num: $num2 int: $int1 double: $d1");
print(num1.abs()); // 求绝对值
print(num1.toInt()); // 转换成int类型
print(num1.toDouble()); // 转换成double
4-3 Flutter之Dart常用数据类型(字符串)
Dart里面定义字符串可以用双引号也可以用单引号。
拼接字符串可以用+
也可以用单引号
和$
符号。
字符串其它方法:startsWith
, replaceAll
, contains
, split
等。
// 字符串使用
void _stringType() {
String str1 = '字符串';
String str2 = "双引号";
String str3 = "str1:$str1 str2:$str2"; // 字符串拼接
String str4 = 'str1: ' + str1 + 'str2: ' + str2; // 字符串拼接
print(str3);
print(str4);
// 常用方法
String str5 = '常用数据类型,请看控制台输出';
// 截取字符串
print(str5.substring(1, 5)); //输出:用数据类
// 获取字符串在字符串中的位置
print(str5.indexOf('类型')); // 输出:4
// 其它方法:startsWith, replaceAll, contains, split等
}
Dart
中方法都加下划线,同时要放在build
方法中:
Widget build(BuildContext context) {
_numType(); // 方法放在build里面
_stringType(); // 字符串方法
return Container(
child: Text('常用数据类型,请查看控制台输出'),
);
}
4-4 Flutter之Dart常用数据类型(布尔, List)
bool
类型用法:
// 布尔类型,Dart是强bool类型检查,只有bool类型的值是true,才被认为是true
void _boolType() {
bool success = true;
bool fail = false;
print(success);
print(fail);
// 常用方法
print(success || fail); // 逻辑或
print(success && fail); // 逻辑与
}
集合List
用法:
void _listType() {
print('----_listType----');
// 集合初始化的方式
List list = [1, 2, 3, '集合']; // 1:初始化时添加元素
print(list); // 输出:flutter: [1, 2, 3, 集合]
List<int> list2 = []; // 集合只能是int类型
// list2 = list; // 错误做法,类型转换错误
List list3 = [];
list3.add('list3');
list3.addAll(list);
print(list3); // 输出:flutter: [list3, 1, 2, 3, 集合]
// 集合生成函数
// 第1个参数是元素数量
// 第2个参数是生成规则
List list4 = List.generate(3, (index) => index * 2);
print(list4); // 输出:flutter: [0, 2, 4]
}
遍历List
的3种方法:for
循环, for...in
,forEach
。
// 遍历集合的方式:for循环
for (int i = 0; i < list.length; i++) {
print(list[i]);
}
// 遍历集合的方式2
for (var item in list) {
print(item);
}
// 遍历集合的方式3:forEach
list.forEach((element) {
print(element);
});
List
其它方法:list.removeXx
, insert
, sublist
, indexof
等
4-5 Flutter之Dart常用数据类型(Map)
map
是<key,value>
结构,key
和value
可以是任何类型的对象,并且key
是唯一的,如果key
重复后面添加的value
覆盖之前的value
值。
Map
初始化的2种方式:注意List
初始化是[]
,Map
是{}
// Map初始化方式1
// key和value通过冒号分割,元素通过逗号分割
// 集合List初始化是[], Map是{}
Map names = {'xiaoming': '小明', 'xiaohong': '小红'};
print(names);
// Map初始化方式2
Map ages = {};
ages['xiaoming'] = 16;
ages['xiaohong'] = 18;
print(ages);
Map
遍历的3种方式:forEach
、for
循环、map
方法
// Map遍历:forEach
ages.forEach((key, value) {
print('$key, $value');
});
// Map遍历:for循环
for (var key in ages.keys) {
print('$key ${ages[key]}'); // 注意是${ages[key]},${}
}
// Map遍历:使用map方法
// map方法返回一个新的Map
ages.map((key, value) {
print('$key $value');
return MapEntry(value, key); // 颠倒key,value
});
创建key,value
颠倒的Map
:使用MapEntry
构造新Map
,使用map
方法。
// 创建k,v颠倒的Map
Map ages2 = ages.map((k, v) {
return MapEntry(v, k);
});
print(ages2); // 打印颠倒k,v的Map
注意map
方法返回的是1个新的Map
。
其它方法:keys
, values
, remove
, containsKey
。
4-6 Flutter之Dart常用数据类型(科普小姿势)
dynamic
、var
、Object
三者的区别:
dynamic
:是所有Dart
对象的基础类型,在大多数情况下,通常不直接使用它
通过dynamic
定义的变量会关闭类型检查,这意味着dyamic x = 'hal'; x.foo();
这段代码静态类型检查不会报错,但是运行的时候会报错,因为x
没有foo()
方法,所以建议大家在编程时不要直接使用dynamic
。
dynamic
可以定义任意的数据类型,但是会导致Dart语法检查失效
dynamic
赋值的变量只有在运行时才知道数据类型
变量.runtimeType
输出变量的类型
// dynamic动态数据类型
// dynamic可以定义任意的数据类型,但是会导致Dart语法检查失效
// dynamic赋值的变量只有在运行时才知道数据类型
dynamic x = 'hal';
print(x.runtimeType); // 输出:flutter: String
print(x); // 输出:flutter: hal
// x.foo(); // 编译不报错,运行报错
x = 123;
print(x.runtimeType); // 输出:flutter: int
print(x);
var
:是一个关键字,意思是“我不关心这里的类型是什么。”,系统会自动推断类型runtimeType
。
但是var
定义的变量不能改变类型。
// var关键字,意味着不关心数据类型是什么
// var定义的数据类型被定义后不能修改
var a = 'var';
print(a.runtimeType); //输出:flutter: String
print(a); // var
// a = 123; // 因为a被var定义成String,所以报错
a = 'deed'; // 正确
Object
:是Dart
对象的基类,当你定义:Object o=xxx;
时这时候系统会认为o
是个对象,你可以调用o
的toString()
和hashCode()
方法。
因为Object
提供了这些方法,但是如果你尝试调用o.foo()
时,静态类型检查会进行报错。
综上不难看出dynamic
与Object
的最大的区别是在静态类型检查上。
// Object是Dart对象的基类
Object o1 = 'deed';
print(o1.runtimeType); //输出:flutter: String
print(o1); // deed
4-7 带你揭开Flutter中的面向对象(标准构造方法、初始化列表)
Dart
面向对象脑图:
Dart
中所有的类都继承自Object
。
类的构造方法作用:初始化类的对象
类的构造方法1:初始化构造方法
class Person {
String name;
int age;
// Person类初始化方法,标准化构造方法
Person(this.name, this.age);
}
多态:通过@override
重载父类的方法,如下重载Object
类中方法。
// 重载父类的toString()方法,面向对象多态的体现
@override
String toString() {
return 'name:$name, age:$age';
}
创建Student
类继承Person
类:创建子类的构造方法时要初始化父类的构造方法
类的构造方法2:初始化列表,冒号后面的表达式是类的初始化列表,先完成父类的初始化,再完成子类的初始化
class Student extends Person {
// super完成父类的初始化,再完成子类的初始化
Student(String name, int age) : super(name, age);
}
类的私有变量通过下划线
创建:Dar
t通过下划线
来标识私有变量,类似于private
,同理Dart
中方法
也是下划线
创建
String _school; // Dart通过下划线来标识私有变量,类似于private
父类初始化完成后,初始化自有参数:
// 通过this._school初始化自有参数
// name, age交给父类初始化
Student(this._school, String name, int age) : super(name, age);
构造方法中的可选参数:{}
内为可选参数
// 可选参数
// city为可选参数
Student(this._school, name, age, {this.city}) : super(name, age);
构造方法中的默认参数:首先默认参数必须是可选参数,紧接着默认参数赋值
_school
:私有参数city
:可选参数country
:默认参数,也是可选参数
// 默认参数country
Student(this._school, name, age, {this.city, this.country = 'China'})
: super(name, age);
类的初始化列表是跟在冒号后面的一串表达式,跟在冒号
后面,通过逗号
和父类构造方法隔开。
// 初始化列表表达式
// 逗号后面是父类构造方法
Student(this._school, name, age, {this.city, this.country = 'China'})
: name = '$country.$city',
super(name, age);
构造方法的方法体可以省略,如下显示构造方法的方法体
// 构造方法体
Student(this._school, name, age, {this.city, this.country = 'China'})
: name = '$country.$city',
super(name, age) {
print('构造方法体不是必须的'); // 构造方法的方法提
}
4-8 带你揭开Flutter中的面向对象(命名构造方法)
命名构造方法:
- [
类名
+.
+方法名
],比如Student.cover
,其中cover
是自定义的方法名 - 使用命名构造方法为类实现多个构造方法
- 命名构造方法可以有方法体
// 命名构造方法: [类名 +1 . + 方法名]
// 使用命名构造方法为类实现多个构造方法
// 命名构造方法可以有方法体1
Student.cover(Student stu) : super(stu.name, stu.age){
print('命名构造方法');
}
4-9 带你揭开Flutter中的面向对象(工厂构造方法)
工厂构造方法:返回单例模式,factory
关键字
oop_learn.dart
:
class Logger {
static Logger _cache; // 静态实例
// factory关键字,返回唯一的类的实例
// 工厂构造方法:
// 不仅仅是构造方法,更是一种模式
// 有时候为了返回一个之前已经创建的缓存对象,原始的构造方法已经不能满足要求
// 那么可以使用工厂模式来定义构造方法
// 对外只暴露了工厂构造方法
factory Logger() {
if (_cache == null) {
_cache = Logger._internal();
}
return _cache;
}
// 私有的命名构造方法,没有对外暴露
Logger._internal();
void log(String msg) {
print(msg);
}
}
main.dart
:使用工厂构造方法
void _oopLearn() {
// 因为Logger类只对外暴露了工厂构造方法Logger() ,所以log1和log2应该相等
// 即工厂构造方法返回单例
Logger log1 = Logger();
Logger log2 = Logger();
print('----_oopLearn----');
print(log1 == log2); // 判断log1和log2是否相等 true
}
输出:可以看到log1和log2实例相等。
4-10 带你揭开Flutter中的面向对象(命名工厂构造方法)
做网络请求的时候,将网络请求的数据转换成模型的时候常用。
命名工厂构造方法:factory
[类名
+ .
+ 方法名
],形如factory Student.stu(Student stu){}
// 命名构造方法: [类名 + . + 方法名]
// 使用命名构造方法为类实现多个构造方法
// 命名构造方法可以有方法体
// 因为city是final String,所以必须加入参数
Student.cover(Student stu, this.city) : super(stu.name, stu.age) {
print('命名构造方法');
}
// 命名工厂构造方法:factory [类名 + . + 方法名]
// 它可以有返回值,而且不需要将类的final变量作为参数,是提供一种灵活获取类对象的方式
// 不用加入this.city,相比命名构造方法cover更加灵活
factory Student.stu(Student stu) {
return Student(stu._school, stu.name, stu.age);
}
问题:5种构造方法的实际使用?????
几种构造方法大综合:
// 标准构造方法
Student(String name, int age) : super(name, age);
// 父类初始化完成后,初始化自有参数
Student(this._school, String name, int age) : super(name, age);
// 可选参数city
Student(this._school, name, age, {this.city}) : super(name, age);
// 默认参数country
Student(this._school, name, age, {this.city, this.country = 'China'})
: super(name, age);
// 初始化列表表达式
Student(this._school, name, age, {this.city, this.country = 'China'})
: name = '$country.$city',
super(name, age);
// 构造方法体
Student(this._school, name, age, {this.city, this.country = 'China'})
: name = '$country.$city',
super(name, age) {
print('构造方法体不是必须的');
}
// 命名构造方法: [类名 + . + 方法名]
Student.cover(Student stu, this.city) : super(stu.name, stu.age) {
print('命名构造方法');
}
// 命名工厂构造方法:factory [类名 + . + 方法名]
factory Student.stu(Student stu) {
return Student(stu._school, stu.name, stu.age);
}
感觉初始化列表表达式
使用冒号:
后面的表达式,也就是用父类Person
的name
值初始化子类Student
的name
值
4-11 带你揭开Flutter中的面向对象(get和set,静态方法)
Flutter
面向对象中的方法:
set
和get
方法类似Java
:
// 可以为私有字段设置getter来让外界获取到私有字段
String get school => _school;
// 可以为私有字段设置setter来控制外界对私有字段的修改
set school(String value) {
_school = value;
}
静态方法使用:打印时增加字符串doPrint
// 静态方法
static doPrint(String str) {
print('doPrint: $str ');
}
main.dart
中调用:
// 调用工厂构造方法
void _oopLearn() {
print('----静态方法调用----');
Student.doPrint('_oopLearn'); // print('----_oopLearn----');
}
调用初始化列表表达式:
// 调用工厂构造方法
void _oopLearn() {
// 因为Logger类只对外暴露了工厂构造方法Logger() ,所以log1和log2应该相等
// 即工厂构造方法返回单例
Logger log1 = Logger();
Logger log2 = Logger();
print('----_oopLearn----');
print(log1 == log2); // 判断log1和log2是否相等 true
print('----静态方法调用----');
Student.doPrint('_oopLearn'); // print('----_oopLearn----');
// 创建Student的对象
Student stu1 = Student('清华', '成续源', 18);
stu1.setSchool = '985'; // 调用私有变量的settter方法
print(stu1.getSchool); // 985
/// 此处子类Student调用了父类的toString方法,返回的name和age是父类Person的参数
print(stu1.toString()); // name:China.null, age:12
/*****
父类的构造方法初始化了子类
Student(this._school, String name, int age,
{this.city, this.country = 'China'})
: name = '$country.$city',
age = 12,
super(name, age);
**/
}
子类Student
的toString()
方法:
/// Student子类toString()方法
@override
String toString() {
return '子类toString: \n' +
'name: $name school: ${this._school} city:$city country:$country \n' +
'父类toString:\n' +
'${super.toString()}';
}
父类Person
的toString()
方法:
/// 重载父类的toString()方法,面向对象多态的体现
@override
String toString() {
return 'name:$name, age:$age';
}
发现的问题?父类的name初始化了子类
初始化可选参数
和默认参数
:
// flutter: 子类toString:
// name: 中国.南京 school: 北大 city:南京 country:中国
// 父类toString:
// name:中国.南京, age:12
Student stu2 = Student('北大', '成竹', 19, city: '南京', country: '中国');
print(stu2.toString());
4-12 带你揭开Flutter中的面向对象(抽象类和方法)
抽象类和抽象方法:比如StatefulWidget
就是1个抽象类
抽象类不能有自己的实例,也就是不能创建自己的对象
抽象类:
- 使用
abstract
修饰符定义的类,抽象类不能被实例化 - 抽象类常用于定义接口
- 抽象类可以有抽象方法(不包括方法体的方法)
- 抽象类可以有抽象方法,也可以没有抽象方法
- 如果1个类有抽象方法,这个类必须被标识成抽象类(
abstract
修饰)
abstract class Study {
// 抽象方法(没有方法体的方法)
void study();
// 正常的方法
@override
String toString() {
return super.toString();
}
}
继承抽象类:
- 继承抽象类要实现它的抽象方法,否则也需要将自己定义成抽象类(不实现抽象方法,需要增加
abstract
修饰符),否则报错
// 没有实现Study中的抽象方法study(),必须用abstract修饰StudyFlutter类,否则报错
abstract class StudyFlutter extends Study {
}
- 实现抽象类中的抽象方法,则不用添加
abstract
修饰符
class StudyFlutter extends Study {
// 实现抽象类Study中的抽象方法
@override
void study() {
print('Learning Flutter!');
}
}
使用抽象类:
main.dart
中_oopLearn
方法
void _oopLearn() {
...
/// 抽象类用法
StudyFlutter studyFlutter = StudyFlutter();
studyFlutter.study(); // Learning Flutter!
}
输出: Learning Flutter!
4-13 带你揭开Flutter中的面向对象(mixins)
mixins:在多个类层次结构中重用代码的1种方式
- 要使用mixins,需要在with关键字后面跟上1个或多个mixin的名字(用逗号分隔),并且with要用在extends关键字之后
- 实现mixin,就必须创建1个继承Object类的子类(不能继承其他类),不声明任何构造方法,不调用super
/// 为类添加特征:mixins
/// mixins是在多个类层次结构中重用代码的一种方式
/// 要使用mixins,需要在with关键字后面跟上1个或多个mixin的名字(用逗号分隔),并且with要用在extends关键字之后
/// mixins的特征:实现mixin,就必须创建1个继承Object类的子类(不能继承其他类),不声明任何构造方法,不调用super
/// 如上Study类是mixin
///
/// Test使用了mixins Study
/// 通常在开发中使用mixins复用已经存在的一些类的相关特性
class Test extends Person with Study {
Test(String name, int age) : super(name, age);
@override
void study() {
// TODO: implement study
}
}
with Study
:即Study
类是mixin
,Test
类使用了mixins Study
4-14 带你解锁Flutter中常用的Dart方法类型
Dart中的方法构成和方法类型:
公有方法
Dart
中的方法构成:
- 返回值 + 方法名 + 参数
- 其中:返回值类型可缺省,也可为void或具体的类型
- 方法名:匿名方法不需要方法名
- 参数:参数类型和参数名,参数类型可缺省(另外,参数又分可选参数和参数默认值,可参考面向对象一节中构造放方法部分)
function_learn.dart
:创建sum
方法
/// 创建测试类
class TestFunction {
/// 创建FunctionLearn对象
FunctionLearn functionLearn = FunctionLearn();
void start() {
print(functionLearn.sum(1, 2)); // functionLearn.sum(1, 2)
}
}
class FunctionLearn {
/// 方法的构成
/// 返回值 + 方法名 + 参数
/// 其中:返回值类型可缺省,也可为void或具体的类型
/// 方法名:匿名方法不需要方法名
/// 参数:参数类型和参数名,参数类型可缺省(另外,参数又分可选参数和参数默认值,可参考面向对象一节中构造放方法部分)
int sum(int val1, int val2) {
return val1 + val2;
}
}
私有方法
Dart
中的私有方法:方法前加下划线表示私有方法,只能在该类访问
/// 创建测试类
class TestFunction {
/// 创建FunctionLearn对象
FunctionLearn functionLearn = FunctionLearn();
...
void privatePrint() {
functionLearn._learn();
}
}
class FunctionLearn {
...
/// 私有方法
/// 通过——开头命名的方法
/// 作用域是当前文件
_learn() {
print('私有方法');
}
}
main.dart
中使用公有方法和私有方法:
void _funtionLearn() {
TestFunction testFunction = TestFunction();
testFunction.start(); // 调用公有方法
testFunction.privatePrint(); // 调用私有方法
}
匿名方法
class FunctionLearn {
/// 匿名方法:
/// 大部分方法都带有名字,例如 main() 或者 print();
/// 在Dart中你有可以创建没有名字的方法,称之为 匿名方法,有时候也被称为 lambda 或者 closure 闭包;
/// 你可以把匿名方法赋值给一个变量, 然后你可以使用这个方法,比如添加到集合或者从集合中删除;
/// 匿名方法和命名方法看起来类似— 在括号之间可以定义一些参数,参数使用逗号 分割,也可以是可选参数;
/// 后面大括号中的代码为函数体:
/// ([[Type] param1[, …]]) {
/// codeBlock;
/// };
anonymousFunction() {
var list = ['私有方法', '匿名方法'];
/// 下面的代码定义了1个参数为element(该参数没有指定类型)的匿名函数
/// list中的每个元素都会调用这个函数来打印出来,同时来计算了每个元素在list中的索引位置
list.forEach((element) {
print(list.indexOf(element).toString() + ': ' + element); // 打印是第几个元素
});
}
}
入口方法
main
方法:
void main() {
runApp(MyApp());
}
4-15 带你了解Dart泛型在Flutter中的应用
定义泛型类文件genericLearn.dart
:
import 'package:flutter_dart_learn/opp_learn.dart';
class TestGeneric {
void start() {
Cache<String> cache1 = Cache();
/// 泛型作用:类型检查约束类比:List<String>
cache1.setItem('cache1', 'cache1');
String string1 = cache1.getItem('cache1');
print(string1); // flutter: cache1
Cache<int> cache2 = Cache();
cache2.setItem('cache2', 1008);
print(cache2.getItem('cache2')); // flutter: 2
Member<Student> member = Member(Student('', '', 16));
print(member.fixedName()); // flutter: fixed:China.null
}
}
/// 泛型
/// 通俗理解:泛型主要是解决类、接口、方法的复用性,以及对不特定数据类型的支持
///
/// 泛型类
/// 作用:提高代码的复用度
class Cache<T> {
static final Map<String, Object> _cached = Map();
/// 泛型方法
void setItem(String key, T value) {
_cached[key] = value;
}
T getItem(String key) {
return _cached[key];
}
}
/// 有时候你在实现类似通用接口的泛型中,期望的类型是某些特定类型时,这时可以使用类型约束
class Member<T extends Person> {
T _person;
/// 泛型作用:约束参数类型
Member(this._person);
String fixedName() {
return 'fixed:${_person.name}';
}
}
main.dart
中使用泛型方法:
/// 泛型
void _genericLearn() {
TestGeneric testGeneric = TestGeneric();
testGeneric.start();
}
4-16 有哪些可以在Flutter上的编程技巧
编程技巧:skills_learn.dart
安全的调用
对于不确定是否为空的对象通过?.
的来访问它的属性或方法以防止空异常,如:a?.foo()
List list;
/// Dart编程小技巧1:安全的调用
/// 对于不确定是否为空的对象可以通过?.的方式来访问它的属性或方法以防止空异常,如:a?.foo()
print(list?.length); // null
使用??
设置默认值
list.length
为空的时候,默认值为-1
print(list?.length ?? -1); // -1
类似ES6中的简化判断
使用contains方法:
list = [];
list.add(0);
list.add('');
list.add(null);
if (list[0] == null || list[0] == '' || list[0] == 0) {
print('list[0] is empty!'); // list[0] is empty!
}
等价于
/// 简化代码判断,类似ES6方法
if ([null, '', 0].contains(list[0])) {
print('list[0] is empty!'); // list[0] is empty!
}
4-17 小结
学习资料: