由于futter是跨平台开发,而iOS打包又需要Mac电脑,因此推荐使用macOS进行flutter开发。
flutterSDK 2.2.3
推荐开发工具:Android Studio
创建项目
flutter create demo
默认iOS是用swift语言,Android是kotlin语言。
flutter run
运行项目。
变量var
变量var
默认是没有类型的,即Null类型。变量可以在运行的过程中赋值不同类型的变量。
var a;
print(a);//null
print(a.runtimeType);//Null
a = "Hello world";
print(a);//Hello world
print(a.runtimeType);//String
a = 10;
print(a);//10
print(a.runtimeType);//int
最终变量final
最终变量只能赋值一次,且在赋值之前是不能使用的。
final a;
// 语法错误,赋值之前不能使用
// print(a.runtimeType);
a = 10;
print(a);//10
print(a.runtimeType);//Int
// 语法错误,只能赋值一次
// a = 20;
// 定义变量的时候一起赋值
final b = 20;
常量const
定义的时候必须初始化,且不能再次赋值。
const a = 10;
print(a);//10
print(a.runtimeType);//int
num
类型
num
不是一种类型,而是包含了int和double两种类型。
//number类型 num不是一种类型,而是包含了int和double两种类型类型
num a;
// 语法错误, 使用之前必须赋值
// print(a);
a = 10;
print(a);//10
print(a.runtimeType);//int
a = 20.0;
print(a);//20.0
print(a.runtimeType);//double
int
类型
如果显示的声明为int类型,则不能赋值为double类型的数据
int a ;
a = 10;
a = 30;
// 语法错误,int不能赋值double类型的数据
// a = 20.0;
print(a);//30
print(a.runtimeType);//int
// isOdd判断是否为奇数
var odd = a.isOdd;
// isEvent判断是否为偶数
var even = a.isEven;
print(odd);//false
print(odd.runtimeType);//bool
print(even);//true
print(even.runtimeType);//bool
// 算术运算符
// 加法运算符
print(10 + 2);//12
// 减法运算符
print(10 -2);//8
// 除法运算符
print(10/3);//3.333333
// 取模运算符 求余数
print(10%3);//1
// 取整运算符~/
print(10~/3);// 除法取整数部分
double
类型
double类型是用户保存小数的,double类型的数据可以再次用整数赋值。虽然可以再次用整数赋值,但是数据还是double数据类型。
// 语法错误,变量未赋值
// print(a);
a = 3.14;
print(a);
print(a.runtimeType);
// double类型赋值整数类型的数据
a = 30;
a = 20.123;
类型转换
x.toInt()
将double类型转成int类型。
d.toDouble()
将int类型转成double类型。
int a = 20;
double b = 30.4;
print(a.toDouble());//20.0
print(a.toDouble().runtimeType);//double
print(b.toInt());//30
print(b.toInt().runtimeType);//int
字符串String
// String类型
// 字符串可以用双引号
String str1 = "Hello world";
// 字符串也可以用单引号
String str2 = 'Hello flutter';
// 双引号可以换行,内容会合并,Dart Flutter
String str3 = "Dart "
"flutter";
// 三个单引号
String str4 = '''Hello
to
flutter''';
// 原始字符串r"",里面的字符串内容会原样输出,比如\n不会被解释成换行
String str5 = r"hello \n world";
// 字符串方法
// 取第一个字符
var first = str1[0];
print(first);//H
print(first.runtimeType);//String
// 如果超出范围,则闪退,后续的代码都无法继续执行
// var at100 = str1[100];
// print(at100);
// print(at100.runtimeType);
// 字符串拼接
var all = str1 + str2;
print(all);//Hello worldHello flutter
print(all.runtimeType);//String
// 字符串重复
var repeat = str1 * 2;
print(repeat);//Hello worldHello world
print(repeat.runtimeType);//String
// 模板字符串 ${变量} 或者$变量 或者 ${表达式}
var strWithStr = "${str1} + str2 = ${str1 + str2}";
print(strWithStr);//Hello world + str2 = Hello worldHello flutter
print(strWithStr.runtimeType);//String
// 字符串开头
print(str1.startsWith("H"));//true
// 字符串是否以XX结尾
print(str1.endsWith("world"));
// 字符串长度
print(str1.length);//11
可变列表
// 可变列表
var list = [1,2,3.14,"Hello"];
print(list);//[1, 2, 3.14, Hello]
print(list.runtimeType);//List<Object>
// 可变列表可以根据索引修改数据
list[0] = "你好";
print(list);//[你好, 2, 3.14, Hello]
// 插入数据
list.insert(0, "李白");
print(list);//[李白, 你好, 2, 3.14, Hello]
print(list.length);//5
// add的字符在print输出的时候,会根据逗号分隔,如果某个元素本身有逗号,则看起来会多了一个元素,
list.add("猥琐发育, 别浪");
print(list);//[李白, 你好, 2, 3.14, Hello, 猥琐发育,别浪]
print(list.length);//6
// 删除数据
list.remove(3.14);
print(list);//[李白, 你好, 2, Hello, 猥琐发育, 别浪]
list.removeAt(4);
print(list);//[李白, 你好, 2, Hello]
list.removeLast();
print(list);//[李白, 你好, 2]
不可变列表
// 不可变列表
var list = const [1,2,3.14,"Hello"];
print(list);//[1, 2, 3.14, Hello]
print(list.runtimeType);//List<Object>
// 语法错误,因为list指向的是常量区内存
// list[0] = "Hello";
// list本身是可以变的
list = [1,4,100];
print(list);
map和不可变map
// map 保存键值对,
//
// 常用的key为String类型
var map = {"name":"阿珂","职业":"专业打野30年"};
print(map);//{name: 阿珂, 职业: 专业打野30年}
print(map.runtimeType);//_InternalLinkedHashMap<String, String>
// 如果value为多种数据类型,则泛型为Object
var map1 = {"name":"妲己","age":18};
print(map1);//{name: 妲己, age: 18}
print(map1.runtimeType);//<String, Object>
// 如果key为多种数据类型,则key对应的泛型也是Object,且类型变成了LinkedMap
var map2 = {1:"蒙恬","2":"老夫子",3:2022};
print(map2);//{1: 蒙恬, 2: 老夫子, 3: 2022}
print(map2.runtimeType);//LinkedMap<Object, Object>
//不可变的map
var map3 = const {1:23};
print(map3);//{1: 23}
print(map3.runtimeType);//ImmutableMap<int, int>
// 语法错误Cannot modify unmodifiable map
// map3[2] = 20;
// 变量是可变的,但是变量对应的内存区间是不可变的
// 语法错误,如果变量声明的时候初始化,则数据类型必须保持一致,比如上面的key和valye都是int类型
map3 = {1:222};
map3[0] = 19;
// 常用方法
// 键值对的数量
print(map2.length);//3
// 获取所有的key
print(map2.keys);//(1, 2, 3)
// 获取所有的值
print(map2.values);//(蒙恬, 老夫子, 2022)
print(map2.values.runtimeType);//_JSMapIterable<Object>
list转map
// list
var list = ["阿珂","猴子","镜"];
// map
var map = {"name":"阿珂","age":"18"};
// list转map, 通过输出类型发现该map并没有key对应的泛型.
// key为对应的索引值,value为list中的值
var listToMap = list.asMap();
print(listToMap);//{0: 阿珂, 1: 猴子, 2: 镜}
print(listToMap.runtimeType);//ListMapView<String>
??=
和??
// ??= 如果有值,则不进行操作,如果没有值,则进行赋值操作
// a??b 条件表达式, 如果a不为null,则返回a;如果a为null,则返回b
// ??=
var a ;
print(a);//null
a ??= 10;
print(a);//10
// 此时因为a有值了,所以赋值无效
a ??= 20;
print(a);//10
// ??
var b;
var b1 = b??100;
print(b1);//100
print(b1.runtimeType);//int
b = 20;
b1 = b??100;
print(b1);//20
print(b1.runtimeType);//int
函数
定义函数的格式。
返回值类型 函数名称(参数列表){
方法体
}
定义无参数无返回值的函数。
void main() {
// 使用函数的时候,函数名称加括号
sayHello();
sayHello1();
}
// 定义函数
void sayHello(){
print("Hello");
}
如果函数体只有一行代码,可以使用箭头函数
void main()=> sayHello1();
// 如果函数只有一句,可以使用箭头
void sayHello1() => print("Hello");
带参数和返回值的函数。
void main() {
var res = sum(10, 20);
print(res);
// 语法错误。参数只能是int类型
// var res1 = sum(10.0 , 11.2);
// print(res1);
var res2 = sum1(10.2, 20.3);
print(res2);//30.5
print(res2.runtimeType);//double
// int类型可以自动类型转成double,如果整数可以保存,则返回结果会被优化成整数
var res3 = sum1(10.0, 20.0);
print(res3);//30
print(res3.runtimeType);//int
// 两个double类型的值返回值是int。注意
var res4 = sum1(10.3, 20.7);
print(res4);//31
print(res4.runtimeType);//int
}
泛型函数,不指定类型,只要符合语法格式就可以。
void main() {
var res1 = sum(10, 20);
var res2 = sum(10.1 , 20);
var res3 = sum(10.2 , 19.3);
var res4 = sum("hello", " world");
print(res1);//30
print(res1.runtimeType);//int
print(res2);//30.1
print(res2.runtimeType);//double
print(res3);//29.5
print(res3.runtimeType);//double
print(res4);//hello world
print(res4.runtimeType);//String
}
// 只要该类型支持+运算就可以传递
sum( a, b) {
return a + b;
}
可选参数
void main() {
optionFunction(10);
optionFunction(10,b:3);
optionFunction(10,c:3);
optionFunction(10,b:3,c:4);
}
// 可选参数
// int类型不能为空
// void optionFunction(int a,{int b, int c}){
void optionFunction(int a,{var b, var c}){
b ??= 0;
c ??= 0;
print("total is ${a + b + c}");
}
可变参数
void main() {
changeableFunction(10);
changeableFunction(10,20);
changeableFunction(10,20,30);
// 最多只能参数规定的数量
// changeableFunction(10,20,30,40);
}
// 可变参数
void changeableFunction(int a,[int b = 0, int c = 0]){
print("total is ${a + b + c}");
}
闭包:闭包是一个特殊的函数,可以保存上下文
void main() {
var closure = func1();
closure();//11
closure();//12
closure();//13
}
// 闭包 , 可以保存上下文
func1(){
int a = 10;
return (){
a++;
print(a);
};
}
面向对象
class
声明类。
void main() {
var p = Person();
p.name = "小白";
p.age = 18;
p.des();
}
// 面向对象
class Person {
String? name;
int? age;
des(){
print("name is $name age is $age");
}
}
默认构造函数
void main() {
var p = Person();
p.name = "小白";
p.age = 18;
}
// 面向对象
class Person {
String? name;
int? age;
// 默认构造函数,
Person(){
print("我是构造函数");
}
}
自定义构造函数,构造函数只能有一个。
void main() {
var p = Person("李白",18);
p.name = "小白";
p.age = 18;
}
// 面向对象
class Person {
String? name;
int? age;
Person(String name, int age){
this.name = name;
this.age = age;
}
}
构造函数的简介形式
void main() {
var p = Person("李白",18);
p.name = "小白";
p.age = 18;
}
// 面向对象
class Person {
String? name;
int? age;
Person(this.name, this.age);
}
构造函数只能有一个,但是命名构造函数可以有多个
void main() {
var p = Person("李白",13,"打野");
p.name = "小白";
p.age = 18;
var jungle = Person.assassin();
jungle.name = "兰陵王";
jungle.age = 20;
var middle = Person.master();
middle.name = "安其拉";
middle.age = 10;
}
// 面向对象
class Person {
String? name;
int? age;
String?job;
Person(this.name, this.age, this.job);
// 命名构造函数可以有多个
Person.assassin(){
this.job = "刺客";
}
Person.master(){
this.job = "法师";
}
}
类和文件
//student.dart
class Student{
// 成员变量下划线开头为私有成员变量
int? _age;
String? name;
// 方法名称下划线开头为私有方法
void _innerPrint(){
print("我是内部方法");
}
void commonPrint(){
print("我是通用方法");
}
}
void insideMethod(){
Student s = Student();
// 同一个文件可以访问私有成员变量
s._age = 100;
s.name = "张三丰";
// 同一个文件可以访问私有方法
s._innerPrint();
s.commonPrint();
}
// main.dart
import "student.dart";
void main() {
insideMethod();
var s = Student();
// 语法错误 无法访问私有成员变量
// s._age = 10;
// 语法错误,无法访问私有成员方法
// s._innerPrint()
s.name = "大白";
}
计算属性:获取值使用get,设置值使用set。
void main() {
var s = Person();
// 计算属性不能使用函数的形式传递参数调用
// s.setAge(100);
s.setAge = 20;
print("age is ${s.getAge}");
}
// 面向对象
class Person {
int? age;
// 计算属性
// 计算属性使用关键字+属性名称,注意此时没有括号
get getAge{
return this.age;
}
set setAge(value){
this.age = value;
}
}
默认初始化
void main() {
var s = Person();
print("${s.age} ${s.height}");
}
// 面向对象
class Person {
int? age;
int? height;
Person():age=10,height=108{
}
}
静态属性和静态方法:都是属于类,不属于对象。
void main() {
var s = Person();
// 语法错误:静态属性不属于对象
// s.maxAge = 30;
Person.run();
Person.maxAge = 200;
Person.run();
}
// 面向对象
class Person {
// static 声明静态属性
static int maxAge = 130;
int? age;
int? height;
// 类方法使用static
static void run(){
print(maxAge);
}
}
对象操作符1: 条件运算符?
t:如果对象不为空则调用方法,否则啥也不做。
void main() {
var p;
// 语法错误:对象是null,不能使用run方法
// p.run();
// 使用问号操作符,如果对象不存在,则不会调用方法
p?.run();
p = Person();
// 此时p不为空,可以调用该方法
p?.run();
}
// 面向对象
class Person {
void run(){
print("跑呀跑");
}
}
对象操作符2:连接运算符..
void main() {
var p = Person();
p.age = 20;
p.height = 170;
p.run();
// 使用连接..可以在一行多次给属性赋值或者调用方法
var p1 = Person();
p1..run()..height=30..age=100..run();
// 也可以分开多行
var p2 = Person();
p1..run()
..height=30
..age=100
..run();
}
对象操作符3: 类型判断is
// 3和4可以打印
void main() {
var p;
if (p is Person) {
print("1p is person");
}
if (p is Object){
print("2p is Object");
}
p = Person();
if (p is Person) {
print("3p is person");
}
if (p is Object){
print("4p is Object");
}
}
对象操作符:as
类型转换。
void main() {
var p = Object();
// p.run();
// 运行错误
// (p as Person).run();
p = Person();
print(p.runtimeType);//Person
// 由于p定义的时候直接初始化,因为类型已经确定,Object没有run方法,因此编译报错。虽然runtimeType是Person,但是本质还是Object对象类型
// p.run();
(p as Person).run();
// 但是如果添加了一个判断,则又可以编译通过
if (p is Person){
p.run();
}
}
class Person {
void run(){
print("person run");
}
}
初始化列表: 可以给final赋值,数据校验。
void main() {
var p = Person(100,200);
}
class Person {
int age;
final int maxAge;
// 初始化列表
Person(this.age,int max): maxAge=max,assert(max >0),assert(age > 0) {
}
}
单例1: 工厂方法
void main() {
var p = Person();
var p1 = Person();
if(p == p1){
print("是同一个对象");
}
}
class Person {
// 定义一个私有的命名构造函数
Person._onlyone();
// 定义一个静态变量
static final _instance = Person._onlyone();
factory Person(){
return _instance;
}
}
单例2:直接使用静态变量
void main() {
var p = Person.instance;
var p1 = Person.instance;
if(p == p1){
print("是同一个对象");
}
}
class Person {
static final instance = Person();
}
单例3:因此静态变量,通过静态方法获取
void main() {
var p = Person.instance();
var p1 = Person.instance();
if(p == p1){
print("是同一个对象");
}
}
class Person {
static final _instance = Person();
static instance(){
return _instance;
}
}
单例4:通过计算属性获取
void main() {
var p = Person.instance;
var p1 = Person.instance;
if(p == p1){
print("是同一个对象");
}
}
class Person {
static final _instance = Person();
static get instance{
return _instance;
}
}
继承:子类拥有父类的成员变量和成员方法
void main() {
var s = Student();
s.run();
s.show();
}
class Person {
int? age;
void run(){
print("person run");
}
void show(){
print("person show");
}
}
//属性和方法都是可以继承的,也可以被重写
class Student extends Person{
@override
void show() {
// TODO: implement show
print("Student show");
}
}
抽象类:用于定义接口,不能直接初始化。
void main() {
People s = Student();
s.run();
Doctor d = Doctor();
d.run();
d.speak();
}
// abstract class用于定义抽象类,抽象类不能直接初始化
abstract class People{
void run();
}
abstract class MakeVoice{
void speak();
}
class Student extends People{
@override
void run() {
print("Student run");
}
}
// 可以实现多个抽象类
class Doctor implements People,MakeVoice{
@override
void speak() {
print("Doctor speak");
}
@override
void run() {
print("Doctor run");
}
}
maxin:可以实现多继承,但是被继承的只能是Object的子类,被继承的类不能实现构造方法。
void main() {
Instance a = Instance();
a.a();
a.b();
a.c();
}
class A{
a() => print("a");
}
class B{
a() => print("b class a");
b() => print("b");
}
class C{
c() => print("c");
a() => print("c class a");
}
运算符重载
void main() {
Student s = Student(20);
Student s1 = Student(18);
Object o = Object();
print(s > s1);
print(s == s1);
print(s < s1);
print(s > s1);
}
class Student{
int age;
Student(this.age);
@override
bool operator == (Object other) {
if (other is Student){
return this.age == other.age;
}
return false;
}
bool operator > (Student other) {
return this.age > other.age;
}
bool operator < (Student other) {
return this.age < other.age;
}
}