2.1 基础语法介绍
使用VScode学习Dart语言,需要安装几个插件
- Dart
- Flutter
- codeRunner 方便运行Dart
必须有一个main入口函数
//执行dart语法必须要有一个main函数
main(List<String> args) {
//在终端运行dart的时候可以参入参数
//这个参数就是通过args传递的
//终端命令 dart 名称 参数
// List<String> args 支持泛型 <String>
print('hello word');
}
2.1.1 变量
变量的声明有2种大的方式
1.根据类型直接声明
String name = "CC"
2.自动推导类型
var age1 = 20; *//类型自动推导,int
var
声明变量
const
声明常量,声明的时候必须赋值
final
也是声明常量,但是可以声明的时候不赋值,一旦赋值就不可以改变了
//声明变量
// 1.明确的声明
// 变量类型 变量名称 = 赋值;
String name = "cc";
int age = 18;
//拼接字符串
print('我的名字是:${name},年龄是:${age}');
//2.自动推导类型(var/final/const )
var age1 = 20; //类型自动推导,int
print(age1);
// 3.final const声明常量
final str = "AA";
const str1 = "BB";
print('${str},${str1}');
// 区别:
// const 必须赋值 常量值,赋值的内容必须是在编译期间就确定下来的
// final 在赋值时, 可以动态获取, 比如赋值一个函数
// final可以先声明,后赋值
final person = Person("CC");
print(person);
//如果是用const来声明类的话
// 必须是 构造器方法是const修饰才可以
//类的内部属性必须是 final修饰
const stu = Student("TTT");
const stu1 = Student("TTT");
//此时 stu stu1是同一个对象
//但是当构造器 初始化的时候 name不相同就不是同一个对象
print(identical(stu,stu1)); // true 说明返回的是同一个对象
//定义一个类Pseron
class Person {
String name;
Person(String name){
this.name = name;
}
}
class Student {
final String name;
const Student(this.name);
}
2.1.2 数字类型
对于数值来说,我们也不用关心它是否有符号,以及数据的宽度和精度等问题。只要记着整数用int
,浮点数用double
就行了。
不过,要说明一下的是Dart中的int
和double
可表示的范围并不是固定的,它取决于运行Dart的平台。
// 1.整数类型
int age = 18;
int hexAge = 0x12;
print(age);
print(hexAge);
// 2.浮点类型
double height = 1.88;
print(height);
字符串和数字之间的转化:
// 字符串和数字转化
// 1.字符串转数字
var one = int.parse('111');
var two = double.parse('12.22');
print('${one} ${one.runtimeType}'); // 111 int
print('${two} ${two.runtimeType}'); // 12.22 double
// 2.数字转字符串
var num1 = 123;
var num2 = 123.456;
var num1Str = num1.toString();
var num2Str = num2.toString();
var num2StrD = num2.toStringAsFixed(2); // 保留两位小数
print('${num1Str} ${num1Str.runtimeType}'); // 123 String
print('${num2Str} ${num2Str.runtimeType}'); // 123.456 String
print('${num2StrD} ${num2StrD.runtimeType}'); // 123.46 String
2.1.3 布尔类型
布尔类型中,Dart提供了一个bool的类型, 取值为true和false
// 布尔类型
var isFlag = true;
print('$isFlag ${isFlag.runtimeType}');
注意:Dart中不能判断非0即真, 或者非空即真
Dart的类型安全性意味着您不能使用if(非booleanvalue)或assert(非booleanvalue)之类的代码。
var message = 'Hello Dart';
// 错误的写法
if (message) {
print(message)
}
2.1.4 字符串类型
Dart字符串是UTF-16编码单元的序列。您可以使用单引号或双引号创建一个字符串:
// 1.定义字符串的方式
var s1 = 'Hello World';
var s2 = "Hello Dart";
var s3 = 'Hello\'Fullter';
var s4 = "Hello'Fullter";
可以使用三个单引号或者双引号
表示多行字符串:
// 2.表示多行字符串的方式
var message1 = '''哈哈哈呵呵呵嘿嘿嘿''';
var str = """
多个字符串,换行操作,
1.这是一个字符串
2.你是谁
3.哈哈哈
""";
字符串和其他变量或表达式拼接: 使用`${expression}, 如果表达式是一个标识符, 那么{}可以省略
// 3.拼接其他变量
var name = 'coderwhy';
var age = 18;
var height = 1.88;
print('my name is ${name}, age is $age, height is $height');
2.2集合类型
2.2.1 List列表
类型推导定义List列表
// 类型推导
var list1 = ['a','b','c'];
//里面装的事字符串
print(list1);
明确的类型定义List列表
// 明确的类型
//明确的int类型
List<int> list2 = [1,2,3,4];
print(list2);
2.2.2 Set集合
Set:无序,元素不能重复的集合
//自动推导
var lset = {'q','w','g'};
print('$lset,${lset.runtimeType}');
//明确类型
Set<int> set2 = {1,2,3,4,5};
print(set2);
利用
Set
的特性给list去重
//创建一个String的列表
List<String> names = ["张三","李四","王二","张三","王二"];
//利用Set迭代器 泛型String
names = Set<String>.from(names).toList();
print(names);
// [张三, 李四, 王二]
2.2.3Map映射
Map类似iOS中的字典
//自动推动
var dic = {'name':'cc','age':'18'};
print('$dic,${dic['name']}');
//明确类型 里面字符串和对象
Map<String,Object> info = {'name':'TT'};
print(info);
2.2.4 几个中常见的操作
长度的获取
//1.获取长度 length
print('lsit的长度:${list1.length}');
print('Set的长度:${lset.length}');
print('Map的长度:${info.length}');
增删改查
// 增加的元素的时候 注意类型要一致
list1.add('cvb');
print(list1);
//删除
list1.remove('a');
list1.removeAt(0);
print(list1);
//是否包含摸个元素
var isn = list1.contains('cvb');
print(isn);
//Map映射的
//根据key获取元素
print(dic['name']);
// 获取所有的 entries
print(dic.entries); // (MapEntry(name: cc), MapEntry(age: 18))
//获取第一个
var entry = dic.entries.first;
print(entry.key);
2.3 函数
函数的基本定义
返回值 函数名称(参数列表) {函数体}
无参数 无返回值
void test1(){
print("无参数 无返回值");
}
int sum(num num1, num num2) {
return num1 + num2;
}
2.3.1参数类型
函数的参数可以分成两类:
必须参数
可选参数:命名可选参数 和 位置可选参数
命名可选参数: {param1, param2, ...}
位置可选参数: [param1, param2, ...]
位置可选参数 [类型 参数名]
位置可选参数的
类型 必须是一致的
void test4(String name,[int age,double height]){
print('姓名:${name},年龄:${age},身高:${height}');
}
//位置可选参数, 位置可选参数的 类型 必须是一致的
test4('CC',18,17.0);
命名可选参数 {类型 参数名}
void test5(String name,{int age, double height}){
print('姓名:${name},年龄:${age},身高:${height}');
}
//
test5("TT",age:18,height:1.70);
默认值
只有可选参数,才可以设置默认值
// 3.3函数默认值, 只有可选参数才可以设置默认值
void test6([String name = "TT"]){
print("name:${name}");
}
test6();
将函数 赋值给一个变量
void test1(){
print("test函数");
}
var fn = test1;
fn();
函数作为参数
//函数作为参数
void test1(){
print("test函数");
}
void fn1(Function func){
func();
}
fn1(test1);
函数作为参数,其函数有返回值 有参数
main(List<String> args) {
//调用函数 函数有返回值 可以省略的
test((int num1, int num2) {
return num1 + num2;
});
}
//重命名
typedef Calculate = int Function(int num1, int num2);
void test (Calculate calc){
// var result = calc (20,30);
final result = calc (20,30);
print(result);
}
匿名函数
(){
print("匿名函数");
};
//函数作为参数
void fn1(Function func){
func();
}
//穿入一个匿名函数
fn1((){
print('匿名函数作为参数');
});
函数作为返回值
//函数作为返回值
void getRutern(){
print("函数作为返回值");
}
// 返回值
Function getFunc(){
return getRutern;
}
var fun1 = getFunc();
fun1();
var movies = ['盗梦空间', '星际穿越', '少年派', '大话西游'];
// 2.使用forEach遍历: 有名字的函数
printElement(item) {
print(item);
}
movies.forEach(printElement);
// 3.使用forEach遍历: 匿名函数
movies.forEach((item) {
print(item);
});
// 箭头函数 函数体里面只执行一句代码,在dart里面才可以使用箭头函数
movies.forEach((item) => print(item));
2.4运算符
赋值运算符 ??=
如果name在声明的时候 没有赋值 是nil ,那么在使用 ??= 就可以赋值成功
如果name有值, ??= 就使用原来的值
var name = "cc";
// 如果name在声明的时候 没有赋值 是nil ,那么在使用 ??= 就可以赋值成功
// 如果name有值, ??= 就使用原来的值
name ??= "TT";
print(name); // 打印的结果是:cc
name = null;
name ??= "BB";
print(name); // 打印的结果是: BB
特殊运算符 ??
// ?? 运算符类似swift中的 空盒运算符??
//如果 temp有值 就使用temp的值
//如果temp为null,就使用BMO
var temp = "MM";
var str = temp ?? "BMO";
print(str);
级联语法
final p = Person()
..name = "CC"
..eat()
..test1();
print(p);
class Person {
String name;
void test1(){
print("test1...");
}
void eat(){
print("eat ----");
}
//重写描述性方法
@override
String toString() {
return 'name:${this.name}';
}
}
2.5类和实例
定义类
使用 class
main(List<String> args) {
//类的定义 class
var p = Person();
print(p.name);
}
// 注意: 在定义类的时候,默认会有一个 构造器方法,没有参数
//一旦重写构造器方法,系统自带的就失效
class Person {
//属性
String name;
//方法
void eat(){
print('吃东西');
}
}
2.5.1 构造器
main(List<String> args) {
//创建实例对象,自定义 构造函数
var p = Person("CC",18);
print(p); // name:CC,age:18,height:null
//通过 命名构造函数创建对象
var p2 = Person.withArgement("tt", 18, 1.70);
print(p2); // name:tt,age:18,height:1.7
//通过命名构造函数 Map映射 创建对象
var per = Person.fromMap({"name":"BB","age":17,"height":1.8});
print(per); // name:BB,age:17,height:1.8
}
自定义构造器方法 和 命名构造器函数
class Person {
String name;
int age;
double height;
//自定义构造函数
Person(String name, int age) {
this.name = name;
this.age = age;
}
//语法唐 构造函数
// Person(this.name,this.age);
// 命名构造函数
Person.withArgement(String name, int age,double height){
this.name = name;
this.age = age;
this.height = height;
}
// 命名构造函数2
Person.fromMap(Map<String,Object> map){
this.name = map["name"];
this.age = map["age"];
this.height = map["height"];
}
@override
String toString() {
return 'name:$name,age:$age,height:$height';
}
}
2.5.1初始化表
在 构造函数后面使用
:
跟上一个语句,来完成初始化列表
// 类的 初始化列表
class Person {
// final修饰的成员变量,只能赋值一次,赋值会后不能在修改
final String name;
final int age;
//final 修饰的变量,在构造函数 初始之前应该是有的值的
// 初始化列表就是在 构造函数后面 使用 : 号,来初始化没有 赋值的变量
Person(this.name,{int age}):this.age = age ?? 10 ;
}
注意:上面的构造函数,如果按照下面的方式写,是会报错的
原因: 构造函数在执行大括号{}
,里面的内容时,已经初始变化完毕,和final
是有冲突的
Person(this.name){
this.age = age;
}
2.5.2重定构造函数
重定向函数 就是 用一个构造函数 调用另外一个 构造函数
在一个构造函数中,去调用另外一个构造函数(注意:是在
冒号
后面使用this
调用)
main(List<String> args) {
// 自定义构造函数
var per1 = Person("CC", 17);
print(per1); // name:CC,age:17
// 命名构造函数
var per2 = Person.formName("BB");
print(per2); // name:BB,age:18
}
class Person {
String name;
int age;
//自定义构造函数
Person(String name, int age) {
this.name = name;
this.age = age;
}
//重定向函数 就是 用一个构造函数 调用另外一个 构造函数
// 命名构造函数
// name传入 在定义的构造函数, 18赋值给age
Person.formName(String name):this(name,18);
@override
String toString() {
// TODO: implement toString
return 'name:$name,age:$age';
}
}
2.5.3 常量构造函数
常量构造函数 是当 通过构造方法创建对象,传入的参数相同,返回的事同一个对象
条件:
1.属性变量用 final修饰*
2.构造器函数用const修饰*
3.在创建 对象时使用const创建 对象*
4.有const修饰声明的变量时,在创建对象的时候 const Person()中的const可以省略
main(List<String> args) {
//
var per1 = const Person("cc");
var per2 = const Person("cc");
// identical 函数 判断2个对象是否为 同一个对象
print(identical(per1, per2));
// 有const修饰声明的变量时,在创建对象的时候 const Person()中的const可以省略
const stu1 = Student("CC");
const stu2 = Student("CC");
print(identical(stu1, stu2));
}
class Person {
final String name;
const Person(this.name);
}
class Student {
final String name;
const Student(this.name);
}
2.5.4 工厂构造函数
使用 factory
修饰,可以手动 返回一个 对象
//工厂构造函数
class Person {
String name;
// 声明一个静态的 Map映射 字典
static final Map<String,Person> _cache = <String,Person>{};
//声明工厂方法
factory Person(String name){
//判断字典里面 有没有存储
if (_cache.containsKey(name)) {
return _cache[name];
}else {
final p = Person._instance(name);
_cache[name] = p;
return p;
}
}
//命名构造函数
Person._instance(this.name);
}
2.5.5 get和set方法
main(List<String> args) {
var p = Person();
// p.name = "tt"; 不会调用 set 方法
p.setName = "CC";
print(p.getName);
var stu = Student();
stu.name = "SS";
print(stu.name);
stu.eat();
}
class Person {
String name;
//get方法 返回值是String 没有()
String get getName {
print("进入get方法");
return name;
}
//Set方法
set setName(String name){
print("进入set方法");
this.name = name;
}
void eat(){
print("person");
}
}
// 继承 extends
class Student extends Person {
@override
void eat() {
// TODO: implement eat
super.eat();
print("student");
}
}
2.5.6 继承
关键字:
extends
格式:class A类 extends B类 {}
A继承与B
class Animal {
int age;
//父类有自定义的构造函数方法
Animal(this.age);
}
class Person extends Animal {
String name;
// 1.子类的构造方法在执行前,将隐含调用父类的无参默认构造方法(没有参数且与类同名的构造方法) (父类没有自定义构造函数)
//2.如果父类没有无参默认构造方法,则子类的构造方法必须在 初始化列表 中通过super显式调用父类的某个构造方法。(父类有自定义构造函数)
Person(String name, int age): name = name,super(age);
}
2.5.7 抽象类
其实就是定义了方法的声明,没有实现,用abstract
声明一个抽象类
抽象类不能实例化
抽象类中的抽象方法必须被子类实现, 抽象类中的
已经被实现方法
, 可以不被子类重写
//声明一个Person抽象类
abstract class Person {
//声明了方法没有实现
getName();
}
class Student extends Person {
//给抽象类实现方法
@override
getName() {
return "CC";
}
}
//抽象类 不能实例化,但是可以间接实现
// 使用 工厂函数 初始化 它的一个子类
在前面的介绍中,我们知道
abstract
抽象类,不能实例化
,但是如果可以让他实例化呢?
1.使用 工厂函数factory
,手动返回一个
2.external
方法的声明 和实现分离,声明,可以不实现
3.注解 @patch
, 可以在要实现的地方,实现external
声明的方法
//声明一个抽象类
abstract class Person {
//声明了方法没有实现
getName();
// 可以返回一个 抽象实例
//external 标识方法声明,可以实现
external factory Person();
}
class Student extends Person {
//给抽象类实现方法
@override
getName() {
return "CC";
}
//会报错 这个就是告诉 可以在需要实现的地方 实现工厂函数
@patch factory Person()=> Student();
}
隐式接口
每一个类相当于默认声明了一个接口,可以有其他的类来实现,给别人来实现的类要声明为 抽象类
//main函数
main(List<String> args) {
var sup = SuperMan();
sup.run();
sup.fly();
}
//声明一个抽象类
abstract class Person {
void run();
}
abstract class Flyer {
void fly();
}
// implements 实现 Person,Flyer 里面的方法,用逗号隔开
class SuperMan implements Person,Flyer {
@override
void run() {
print("超人会跑");
}
@override
fly() {
print("超人会飞");
}
}
2.5.7 mixin混入
在通过implements实现某个类时,类中所有的方法都必须被重新实现(无论这个类原来是否已经实现过该方法)
一个类可能希望直接复用之前类的原有实现 要通过 Mixin混入,因为Dart中没有 多继承
mixin可以定义声明一个类
通过
mixin
定义的类用于被其他类混入使用,通过with
关键字来进行混入
main(){
var p = SuperMan();
p.run();
p.fly();
}
//使用 mixin 定义类
mixin Runner {
void run(){
print("在奔跑");
}
}
//mixin 定义类
mixin Flyer{
void fly(){
print("会飞");
}
}
//superMan不用再实现 fly 和 run 方法 直接可以调用
class SuperMan with Runner,Flyer { }
2.5.8 类的成员和类的方法
main(List<String> args) {
Person.run();
Person.name = "CC";
}
class Person {
static String name;
static void run(){
print("pppppppp");
}
}
2.6 枚举
main(List<String> args) {
print(Point.x); // Point.x
// 枚举的属性
// 1.index 表示每个枚举常量的索引, 从0开始.
// 2.values: 包含每个枚举值的List.
print(Point.x.index); // 0
print(Point.values); // [Point.x, Point.y]
var list = Point.values;
print(list.first); // Point.x
}
// 枚举
// 使用 enum定义枚举
// 格式 enum name { } name是枚举的名称
enum Point {
x,y
}
2.7泛型
main(List<String> args) {
// 1.list重视用泛型
var list = [1,'CVB',1.50];
print(list.runtimeType); // List<Object> 泛型的是Object
// 限制类型
var list1 = <String>['as','ed'];
print(list1.runtimeType); // List<String> 明确类型是String
// 类的泛型
var p = Person("CC",18);
print(p.runtimeType); // Person<String, int> 泛型了String 和 int
//泛型方法的定义
T getName<T>(List<T> ts){
return ts.first;
}
var res = getName(['A','b']);
print(getName.runtimeType); // <T>(List<T>) => T
print(res);
}
// 泛型 :没有明确的类型,什么时候用到 什么时候确定类型
// T,M是泛型
class Person<T,M> {
T name;
M age;
Person(this.name,this.age);
}