flutter基础 dart语言学习笔记

1.JIT(Just-In-Time 动态编译) 即时编译为什么能大幅度提升性能

JIT,即Just-in-time,动态(即时)编译,边运行边编译;

https://book.flutterchina.club/chapter1/dart.html flutter中文网

或者让演员表演两分钟,再看两分钟脚本,思考一下,再表演两分钟,再看一会脚本,思考一下。。。恩。。。

这就是 JIT 的原理。批量的把一件事情先整理成为目标能够直接接受的方式,然后直接运行。

2.AOT (Ahead-Of-Time-预先编译 静态编译)

AOT,Ahead Of Time,指运行前编译,是两种程序的编译方式

假定你是个导演,写了个脚本,让演员表演。

先让演员把整个脚本都背下来,吃透,到脑子里,然后连续的表演一个小时。

3.这两种编译方式的主要区别在于是否在“运行时”进行编译
4.跨平台自绘引擎

Flutter与用于构建移动应用程序的其它大多数框架不同,因为Flutter既不使用WebView,也不使用操作系统的原生控件。 相反,Flutter使用自己的高性能渲染引擎来绘制widget。这样不仅可以保证在Android和iOS上UI的一致性,而且也可以避免对原生控件依赖而带来的限制及高昂的维护成本。

Flutter使用Skia作为其2D渲染引擎,Skia是Google的一个2D图形处理函数库,包含字型、坐标转换,以及点阵图都有高效能且简洁的表现,Skia是跨平台的,并提供了非常友好的API,目前Google Chrome浏览器和Android均采用Skia作为其绘图引擎。

目前Flutter默认支持iOS、Android、Fuchsia(Google新的自研操作系统)三个移动平台。但Flutter亦可支持Web开发(Flutter for web)和PC开发,本书的示例和介绍主要是基于iOS和Android平台的,其它平台读者可以自行了解。

5.高性能

Flutter高性能主要靠两点来保证,首先,Flutter APP采用Dart语言开发。Dart在 JIT(即时编译)模式下,速度与 JavaScript基本持平。但是 Dart支持 AOT,当以 AOT模式运行时,JavaScript便远远追不上了。速度的提升对高帧率下的视图数据计算很有帮助。其次,Flutter使用自己的渲染引擎来绘制UI,布局数据等由Dart语言直接控制,所以在布局过程中不需要像RN那样要在JavaScript和Native之间通信,这在一些滑动和拖动的场景下具有明显优势,因为在滑动和拖动过程往往都会引起布局发生变化,所以JavaScript需要和Native之间不停的同步布局信息,这和在浏览器中要JavaScript频繁操作DOM所带来的问题是相同的,都会带来比较可观的性能开销。

6.采用Dart语言开发

这是一个很有意思,但也很有争议的问题,在了解Flutter为什么选择了 Dart而不是 JavaScript之前我们先来介绍两个概念:JIT和AOT。

目前,程序主要有两种运行方式:静态编译与动态解释。静态编译的程序在执行前全部被翻译为机器码,通常将这种类型称为AOT (Ahead of time)即 “提前编译”;而解释执行的则是一句一句边翻译边运行,通常将这种类型称为JIT(Just-in-time)即“即时编译”。AOT程序的典型代表是用C/C++开发的应用,它们必须在执行前编译成机器码,而JIT的代表则非常多,如JavaScript、python等,事实上,所有脚本语言都支持JIT模式。但需要注意的是JIT和AOT指的是程序运行方式,和编程语言并非强关联的,有些语言既可以以JIT方式运行也可以以AOT方式运行,如Java、Python,它们可以在第一次执行时编译成中间字节码、然后在之后执行时可以直接执行字节码,也许有人会说,中间字节码并非机器码,在程序执行时仍然需要动态将字节码转为机器码,是的,这没有错,不过通常我们区分是否为AOT的标准就是看代码在执行之前是否需要编译,只要需要编译,无论其编译产物是字节码还是机器码,都属于AOT。在此,读者不必纠结于概念,概念就是为了传达精神而发明的,只要读者能够理解其原理即可,得其神忘其形。

现在我们看看Flutter为什么选择Dart语言?笔者根据官方解释以及自己对Flutter的理解总结了以下几条(由于其它跨平台框架都将JavaScript作为其开发语言,所以主要将Dart和JavaScript做一个对比):

  1. 开发效率高

    Dart运行时和编译器支持Flutter的两个关键特性的组合:

    基于JIT的快速开发周期:Flutter在开发阶段采用,采用JIT模式,这样就避免了每次改动都要进行编译,极大的节省了开发时间;

    基于AOT的发布包: Flutter在发布时可以通过AOT生成高效的ARM代码以保证应用性能。而JavaScript则不具有这个能力。

  2. 高性能

    Flutter旨在提供流畅、高保真的的UI体验。为了实现这一点,Flutter中需要能够在每个动画帧中运行大量的代码。这意味着需要一种既能提供高性能的语言,而不会出现会丢帧的周期性暂停,而Dart支持AOT,在这一点上可以做的比JavaScript更好。

  3. 快速内存分配

    Flutter框架使用函数式流,这使得它在很大程度上依赖于底层的内存分配器。因此,拥有一个能够有效地处理琐碎任务的内存分配器将显得十分重要,在缺乏此功能的语言中,Flutter将无法有效地工作。当然Chrome V8的JavaScript引擎在内存分配上也已经做的很好,事实上Dart开发团队的很多成员都是来自Chrome团队的,所以在内存分配上Dart并不能作为超越JavaScript的优势,而对于Flutter来说,它需要这样的特性,而Dart也正好满足而已。

  4. 类型安全

    由于Dart是类型安全的语言,支持*静态类型检测,*所以可以在编译前发现一些类型的错误,并排除潜在问题,这一点对于前端开发者来说可能会更具有吸引力。与之不同的,JavaScript是一个弱类型语言,也因此前端社区出现了很多给JavaScript代码添加静态类型检测的扩展语言和工具,如:微软的TypeScript以及Facebook的Flow。相比之下,Dart本身就支持静态类型,这是它的一个重要优势。

  5. Dart团队就在你身边

    看似不起眼,实则举足轻重。由于有Dart团队的积极投入,Flutter团队可以获得更多、更方便的支持,正如Flutter官网所述“我们正与Dart社区进行密切合作,以改进Dart在Flutter中的使用。例如,当我们最初采用Dart时,该语言并没有提供生成原生二进制文件的工具链(这对于实现可预测的高性能具有很大的帮助),但是现在它实现了,因为Dart团队专门为Flutter构建了它。同样,Dart VM之前已经针对吞吐量进行了优化,但团队现在正在优化VM的延迟时间,这对于Flutter的工作负载更为重要。”

总结

本节主要介绍了一下Flutter的特点,如果你感到有些点还不是很好理解,不用着急,随着日后对Flutter细节的了解,再回过头来看,相信你会有更深的体会。

1.2.2 Flutter框架结构

本节我们先对Flutter的框架做一个整体介绍,旨在让读者心中有一个整体的印象,这对初学者来说非常重要。如果一下子便深入到Flutter中,就会像是一个在沙漠中没有地图的人,即使可以找到一个绿洲,但是他也不会知道下一个绿洲在哪。因此,无论学什么技术,都要先有一张清晰的“地图”,而我们的学习过程就是“按图索骥”,这样我们才不会陷于细节而“目无全牛”。言归正传,我们看一下Flutter官方提供的Flutter框架图,如图1-1所示:

图1-1

Flutter Framework

这是一个纯 Dart实现的 SDK,它实现了一套基础库,自底向上,我们来简单介绍一下:

  • 底下两层(Foundation和Animation、Painting、Gestures)在Google的一些视频中被合并为一个dart UI层,对应的是Flutter中的dart:ui包,它是Flutter引擎暴露的底层UI库,提供动画、手势及绘制能力
  • Rendering层,这一层是一个抽象的布局层,它依赖于dart UI层,**Rendering层会构建一个UI树,当UI树有变化时,会计算出有变化的部分,然后更新UI树,最终将UI树绘制到屏幕上,这个过程类似于React中的虚拟DOM。**Rendering层可以说是Flutter UI框架最核心的部分,它除了确定每个UI元素的位置、大小之外还要进行坐标变换、绘制(调用底层dart:ui)。
  • Widgets层是Flutter提供的的一套基础组件库,在基础组件库之上,Flutter还提供了 Material 和Cupertino两种视觉风格的组件库。而我们Flutter开发的大多数场景,只是和这两层打交道

Flutter Engine

这是一个纯 C++实现的 SDK,其中包括了 Skia引擎、Dart运行时、文字排版引擎等。在代码调用 dart:ui库时,调用最终会走到Engine层,然后实现真正的绘制逻辑。

总结

Flutter框架本身有着良好的分层设计,本节旨在让读者对Flutter整体框架有个大概的印象,相信到现在为止,读者已经对Flutter有一个初始印象,在我们正式动手之前,我们还需要了解一下Flutter的开发语言Dart

1.4.1 变量声明

1.var

类似javascript里面的 var 但是 dart里面的 一旦使用 类型就会固定不能改变 否则会报错

var t;
t = "hi world";
// 下面代码在dart中会报错,因为变量t的类型已经确定为String,
// 类型一旦确定后则不能再更改其类型。
t = 1000;

2.dynamic和Object

是Dart所有对象的根基类,也就是说所有类型都是Object的子类(包括Function和Null),所以任何类型的数据都可以赋值给Object声明的对象. dynamicvar一样都是关键词,声明的变量可以赋值任意对象。 而dynamicObject相同之处在于,他们声明的变量可以在后期改变赋值类型。

 dynamic t;
 Object x;
 t = "hi world";
 x = 'Hello Object';
 //下面代码没有问题
 t = 1000;
 x = 1000;

dynamicObject不同的是,dynamic声明的对象编译器会提供所有可能的组合, 而Object声明的对象只能使用Object的属性与方法, 否则编译器会报错。如:

 dynamic a;
 Object b;
 main() {
     a = "";
     b = "";
     printLengths();
 }   

 printLengths() {
     // no warning
     print(a.length);
     // warning:
     // The getter 'length' is not defined for the class 'Object'
     print(b.length);
 }

变量a不会报错, 变量b编译器会报错

dynamic的这个特性与Objective-C中的id作用很像. dynamic的这个特点使得我们在使用它时需要格外注意,这很容易引入一个运行时错误.

3.final和const

如果您从未打算更改一个变量,那么使用 finalconst,不是var,也不是一个类型。 一个 final 变量只能被设置一次,两者区别在于:const 变量是一个编译时常量,final变量在第一次使用时被初始化。被final或者const修饰的变量,变量类型可以省略,如:

常量的值可以赋值给变量 变量的值可以被修改 常量的值不可以被更改

final name = "Bob";

main() {
  var t = name;
  t = '123';
  name = "123" //报错
  print(t);
}
//可以省略String这个类型声明
final str = "hi world";
//final String str = "hi world"; 
const str1 = "hi world";
//const String str1 = "hi world";

重要的概念

在学习 Dart 语言时, 应该基于以下事实和概念:

  • 任何保存在变量中的都是一个 对象 , 并且所有的对象都是对应一个 的实例。 无论是数字,函数和 null 都是对象。所有对象继承自 Object 类。
  • 尽管 Dart 是强类型的,但是 Dart 可以推断类型,所以类型注释是可选的。 在上面的代码中, number 被推断为 int 类型。 如果要明确说明不需要任何类型, 需要使用特殊类型 dynamic
  • Dart 支持泛型,如 List <int> (整数列表)或 List <dynamic> (任何类型的对象列表)。
  • Dart 支持顶级函数(例如 main() ), 同样函数绑定在类或对象上(分别是 静态函数实例函数 )。 以及支持函数内创建函数 ( 嵌套局部函数 ) 。
  • 类似地, Dart 支持顶级 变量 , 同样变量绑定在类或对象上(静态变量和实例变量)。 实例变量有时称为字段或属性。
  • 与 Java 不同,Dart 没有关键字 “public” , “protected” 和 “private” 。 如果标识符以下划线(_)开头,则它相对于库是私有的。 有关更多信息,参考 库和可见性
  • 标识符 以字母或下划线(_)开头,后跟任意字母和数字组合。
  • Dart 语法中包含 表达式( expressions )(有运行时值)和 语句( statements )(没有运行时值)。 例如,条件表达式 condition ? expr1 : expr2 的值可能是 expr1expr2 。 将其与 if-else 语句 相比较,if-else 语句没有值。 一条语句通常包含一个或多个表达式,相反表达式不能直接包含语句。
  • Dart 工具提示两种类型问题:警告_和_错误。 警告只是表明代码可能无法正常工作,但不会阻止程序的执行。 错误可能是编译时错误或者运行时错误。 编译时错误会阻止代码的执行; 运行时错误会导致代码在执行过程中引发 [异常](#exception)。

void 函数没有返回值

void main(){
print('123')
}
变量 会类型自动推断 var int String 定义变量 值可以改变

var int String 定义变量 会自动推断类型 推断类型不能更改 否则报错;

var str= ‘’;
str = 123  //报错

String str = '';
str=1234  //报错 必须为字符串
int str = 123;
str='1234'  //报错 必须为数字
变量名
  • 标识符不能以数字开头

  • 变量名必须由数字,字母,下划线和美元($)组成;

  • 标识符不能是保留字关键字;

  • 变量名的名字是区分大小写的: age和Age是不同变量名 不建议使用;

  • 标识符(变量名)一点要见名思意:变量名建议用名词 ,方法名建议用动词;

常量 const 和 final
const PI = 3.14159;
PI = 123; // 常量的值不可以修改
final PI = 3.14159;
PI = 123; //常量的值不可以修改


两者区别在于:const 变量是一个编译时常量,final变量在第一次使用时被初始化。被final或者const修饰的变量,变量类型可以省略,如:

final a = new DateTime.now();
print(a) // 2020-7-22 16:00

const a = new DateTime.now();
print(a) // 2020-7-22 16:00 //报错
数据类型

1.常用数据类型

String 字符类型 字符串拼接 可以‘+’拼接 也可以 ${} $a 拼接。

String a = 'a' 
String b= ''' asda
asd
asd''';//字符串可以换行

String a = 'abc';
String c = 'aaa${abc}' // aaaabc  字符串拼接 

2**.int 和double数字类型**

double既可以是整形也可以是浮点型

int只能是整形 整形不能赋值为浮点型 小数

int a = 123;

double b = 1.1; 
double c = 2;

3.bool 布尔值 true和false

bool a = false
bool b = true

条件判断 ‘123’ 不等于 123 不会类型隐式转换

bool flag = true;
if(flag) {
print('真')
}else {
print('假')
}

4.list 定义方式 (array)

var a = [1,2,3]; //第一种 var a = [];
a.length;  //3
a[0]; //1
var l = new List();  //第二种创建list方式 new List();
l.add('张三');
l.add('李四');
l.add('王五');

定义List 指定类型

var a = new List<String>();  //List里面只能是String类型
var a = new List<int>();  //List里面只能是int类型

5.Maps 类型 类似 js对象

//第一种定义Maps的方式
var person = {
	"name":"张三",
	"age":20
}
print(person) //{"name":"张三","age":20} 
print(person["name"]) //张三 
print(person["age"]) //20
//第二种定义maps的方式
var p = new Map();
p["name"] = "张三";
p["age"] = 20;
print(p) //{"name":"张三","age":20} 
print(p["age"]) // 20

6.is 关键词来判断类型

var str = "123";
if( str == String) {
	print (String类型)
} else if ( str == int ){
print(int类型)
}else {
print(’其他‘)
}

运算符 条件判断 关系运行符 逻辑运算符
  • 算数运算符
int a= 13;
int b = 5;

print(a+b)//18
print(a-b)//8
print(a*b)//65
print(a/b)//2.6 
print(a%b)//3 余
print(a~/b)//2 取整
  • 关系运算符
int a= 13;
int b = 5;

print(a>b)//true
print(a<b)//false
print(a>=b)//true
print(a<=b)// false
print(a==b)// fasle
  • 逻辑运算符 !非 &&与|| 或
bool a = !fasle //true 
&& 两个都为真 才为真
|| 一个为真 就为真
  • 赋值 运算 = ??=
int b = 10或'';
b??=23;  //如果 b为空 b = 23 如果 b 不为空 b = 10;
print(b) //23
  • 复合运算符 += ,-= ,*=,/=
int a = a+ 
  • if else switch case
if(a>0) {
print('a大于零')
}else {
print('小于零')
}
  String sex = "123";
  switch (sex) {
    case "男":
      print(sex);
      break;
    case "女":
      print(sex);
      break;
    default:
      print('参数错误');
  }
}

bool a = true;
String c = a?"我是true":"我是false";

??用法

var a;
b = a || 20;//20
var a = 10;
b = a || 20;//10

类型转换 字符串转数值型 int.parse 或double.pares 数值转字符串 toString(); isEmpty 是否为空;

int 类型 只能转换 int 类型 不能转换 double 类型 会报错

double 可以转换 int类型 和double类型 但是不能转换 “”类型 会报错

字符串转数值型 int.parse  或double.pares
String a = '123';
var num = int.parse(a); //true
String a = '123.1';
var num = int.parse(a); //会报错

String a = '123.1';
var num = double.parse(a); //true
String a = '123.1';
var num = int.parse(a); //true 

假如后台传过来一个空 需要做判断  不会报错 try 转换失败 抛出异常
String price= '';
try{
	var num = double.parse(price);
	print(num)
}carch(err) {
print("传过来的为空");
}
数值转字符串  toString();
var a = 10;
var b = a.toString(); //数值转字符串

print(b is String) //true
isEmpty 是否为空; 只能判断String类型 不能判断int double 类型
var a = "";
if(a.isEmpty) {
	print('是为空')
}else {
print('不为空');
}
0除0返回NaN
var myNum = 0/0;
if(myNum.isNaN) {
print('Nan');
}


循环语句 for

如果++ --写在前面 先运算 在赋值 如果++ – 先赋值 后运算

++ 自增写在前面和后面的区别 
var b = 10; // 11
var a = ++b; //11 写在前面 先 自增加一
print(b) //11
print(a) //11;
var a = b++; //10 写在后面 先 赋值 给b 所以b =10 再 b+1;
print(10) //11;
for (int i=1;i<=10;i++){
print(i); //1 -10;
}
//100求和
var sum =0;
for(int i=0;i<=100;i++){
sun +=i;
}
print(sum)//5050
//5的阶剩 1*2*3*4*5
var sum =1;
for(int i=1;i<=5;i++){
sun *=i;
}
print(sum)//120

1.打印数组 List 
var list = new List();
List list = ['张三','李四','王二'];
for(int i = 0;i<lsit.length-1;i++){
print(list[i]); //张三 李四 王二
}

2.打印数组 List;
var List = [
{"name":'冯上进'},
{"name":'张三'},
{"name":"李四"}
]


for(int i=0;i<List.length;i++){
 print(List[i]["name"]) //张三 李四 王二
}
3.打印数组List  双层for循环  list 必须是 为数组 字面量  List
List list = [
    {
      "title": '标题',
      "name": [
        {"age": 20},
        {"age": 20},
        {"age": 20}
      ]
    },
    {
      "title": '标题',
      "name": [
        {"age": 20},
        {"age": 20},
        {"age": 20}
      ]
    },
    {
      "title": '标题',
      "name": [
        {"age": 20},
        {"age": 20},
        {"age": 20}
      ]
    }
  ];
  for (int i = 0; i < list.length; i++) {
    print(list[i]["title"]);
    for (int j = 0; j < list[i]["name"].length; j++) {
      print(list[i]["name"][j]["age"]);
    }
  }
循环 while 和 do …while

语法格式

while(表达式/循环条件) {
	里面的语句会一直循环
	防止死循环
}
//先判断语句 再循环
do{
语句/循环体
} while('表达式/循环条件');
//先循环语句 在判断条件
//打印1-10
var i=1;
while(i<10){
	print(i);
	i++;
}
//while 计算 1-100 的和
var i=1;
var sum = 0;
while(i<=100) {
print(sun+=i);
i++;
}
//do...while 计算 1-100 的和
  var i = 1;
  var sum = 0;
  do {
    sum += i;
    i++;
  } while (i <= 100);
  print(sum);

while 和 do…while 的 区别 :

如果 条件不成立;

wihile 先判断条件 在执行 ;

do…while 先执行至少一次 再 判断条件;

break语句的使用continue

break可以在switch case 中 使用 也可以在for 循环 while中使用

for(var i=1;i<=10;i++){
if(i==4) {
	continue; // 跳出当前循环体 循环会继续执行 
}
print(i); //123 56789
}
for(var i=1;i<=10;i++){
if(i==4) {
	break; // 跳出当前循结束 只能跳出一层for循环
}
print(i);//123
}



List 属性和放法

常用属性 :

  • length 长度
  • reversed 翻转
  • isEmpty 是否为空
  • isNotEmpty 是否不为空

常用放法:

  • add 添加数组
  • addAll 拼接数组
  • indexOf 查找 传入具体值 找不到返回-1
  • remove 删除 传入具体值
  • removeAt 删除 传入索引值
  • fillRange 修改
  • insert(index,value); 指定位置插入
  • insertAll(index,list) 指定位置插入List
  • toList() 其他类型转换成List
  • join() List转换成字符串
  • split() 字符串转化成List
  • new Set 去重 set 添加重复的 只返回一个 类似去重后的 List
  • forEach
List list = ["苹果","香蕉","西瓜"];
  List list = ['苹果', '香蕉', '水果'];
  print(list.length); //3
  print(list.reversed.toList()); // [水果, 香蕉, 苹果]
  print(list.isEmpty); //false
  print(list.isNotEmpty); //true
  list.add("123"); 
  list.fillRange(1, 2, '111');
  list.remove('111');
  print(list);//[苹果, 水果, 123]
  var a = list.indexOf("1");
  print(a);//-1
  list.removeAt(1);
  print(list);//[苹果, 123]
  list.insert(1, "冯上进");
  print(list);//[苹果, 冯上进, 123]
  list.insertAll(1, ["我", "是", "冯"]);
  print(list);//[苹果, 我, 是, 冯, 冯上进, 123]
  var s = list.join();
  print(s);//苹果我是冯冯上进123
  var m = list.join(',');
  print(m);//苹果,我,是,冯,冯上进,123
  
  //new Set()数组去重 返回对象 .toList 转成List类型 
  var l = new Set();
  l.add("苹果");
  l.add("香蕉");
  l.add("苹果");
  print(l);{"苹果","香蕉"}
  print(l.toList());//["苹果","香蕉"];

 
maps(映射) 是无序的键值对:

常用属性:

  • keys 获取所有的key值
  • values 获取所有的value值
  • isEmpty 是否为空
  • isNotEmpty 是否不为空

常用放法:

  • remove(key) 删除指定key的数据
  • addAll({…}) 合并映射 给映射内添加属性
  • containsValue 查看映射内的值 返回True/False
  • forEach 循环遍历 返回每一项
  • map 循环遍历 返回数组
  • where 条件判断循环遍历 返回数组
  • any 只要集合里面有满足条件的就返回true
  • every 集合中每一个都满足条件 返回true
var person = {
"name" :'张三';
"age" :'李四';
}

var person = new Map();
perosn["name"] = "张三"


var person = {"name": "张三", "age": 20};
  print(person.keys.toList()); [name, age]
  print(person.values.toList());[张三, 20]
  print(person.isEmpty);false
  print(person.isNotEmpty);true
  person.addAll({"work": '敲代码', "身高": '160'});
  print(person); {name: 张三, age: 20, work: 敲代码, 身高: 160}
  person.remove("name");
  print(person);  {age: 20, work: 敲代码, 身高: 160}
  var s = person.containsValue("张三");查看value 是否存在
  print(s); //false  

List list = ["苹果","香蕉","西瓜"];
list.forEach((value){
	print(value); //苹果香蕉水果
})

 List list = [1, 2, 3];
  list.forEach((element) {
    print(element * 2); //2 4 6
  });
  
 List list = [1, 2, 3];
 var newList = list.map((value){
 return value*2;
 })
 print(newList); //(2,4,6);
 
  List list = [1, 2, 3,4,5,6,7,8,9];
  var newList = list.where((value){
  return value>5
  })
  print(newList); (6,7,8,9);


var s = new Set();
a.addAll([1,222,333,333]);
s.forEach((value){
print(value); //1,222,333 去重后的数组 List
})

var person = {
"name":'冯上进',
"age":18
}
person.forEach((keys,value){
print("$key---$value")
});

内置方法/函数
print() 内置方法

自定义方法:
	自定义方法的基本格式:
	返回类型 方法名称 (参数1,参数2,...){
	方法体;
	return 返回值;
	}
	void 表示函数没有返回值 函数写在入口方法外面 是全局的方法 
	void main(){
		String sum (a,b){
		c = a+b;
		print(c); //5
		};
		sum(2,3)
	}
	
	函数封装 1-n的和
	
	int sumNum(int n){
		int sum = 0;
		for(int i =0;i<=n;i++){
			sum+=i;
		}
		return sum;
	}
	int s = sumNum(60);
	print(s);
	
	1.定义一个方法 然后打印用户信息 
	String printUserInfo(String username,int age){
	return "姓名:$username ---年龄:$age";
	}
	String s = printUserInfo('冯上进',20);
	print(s);// 姓名:冯上进---年龄:20 
	
	2.//[int age,String sex] 可选参数 可传 可不传
	String printUserInfo(String username,[int age]){
	
	if(age!==null){
	return "姓名:$username ---年龄:$age";
	
	}
	return "姓名:$username ---年龄保密";
	
	}
	String s = printUserInfo('冯上进');//不传参数 
	print(s);// 姓名:冯上进---年龄保密
		String s = printUserInfo('冯上进',20);//传参数
	print(s);// 姓名:冯上进---年龄:20 
	
	3.默认参数 [int age,String sex="男"]
	String printUserInfo(String username,[int age,String sex="男"]){
	
	if(age!==null){
	return "姓名:$username ---年龄:$age--性别:$sex";
	
	}
	return "姓名:$username ---年龄保密---性别:$sex";
	
	}
	 String l = printUserInfo('冯上进', 20);
  	print(l); // 姓名:冯上进 ---年龄:20--性别:男
  	
  	4.命名参数  {int age,String sex="男"}  printUserInfo('冯上进', age:20,sex:'未知')
	String printUserInfo(String username,{int age,String sex="男"}){
	
	if(age!==null){
	return "姓名:$username ---年龄:$age--性别:$sex";
	
	}
	return "姓名:$username ---年龄保密---性别:$sex";
	
	}
	 String l = printUserInfo('冯上进', age:20,sex:'未知'); //命名参数 传参
  	print(l); // 姓名:冯上进 ---年龄:20--性别:未知
	
	5.实现一个把方法当做参数的方法
	fn1(){
	print('fn1');
	}
	fn2(fn){
	fn();
	}
	fn2(fn1); //'fn1'
箭头函数 代码只能写一行
List list =['香蕉','苹果','橘子'];
list.forEach((value)=>{print(value)});

1.匿名放法 把方法赋值给变量 printNum
var printNum = (){
	print(123);
}
2.自执行方法
((n){
print(n);
})(12) //12
3.方法的递归
int sum = 1;
fn(n){
	sum*=n;
	if(n== 1){
	return;
	}
	fn(n-1);
}
print(sum);
3.通过方法的递归 求1-100的和;
int sum = 0;
  fn(n) {
    sum += n;
    if (n == 0) {
      return;
    }
    fn(n - 1);
  }

  fn(100);
  print(sum);
}
闭包

1.全局变量特点:全局变量常驻内存,全局变量污染全局。

2.局部变量的特点:不常驻在内存 会被垃圾回收机制回收 ,不会污染全局。

想实现的功能:

1.常驻内存。

2.不污染全局。

​ 产生闭包,闭包可以解决这个问题…

​ 闭包 :函数嵌套函数,内部函数会调用外部函数的变量或参数,变量或参数不会被系统回收。

​ 闭包的写法:函数嵌套函数,并return里面的函数,这样就形成了闭包。

fn() {
    var a = 123; //不会全局污染 常驻内存;
    return () {
      a++;
      print(a);
    };
  }

  var b = fn();
  b(); //124
  b();//125
  b();//126
  b();//127
  b();//128
面向对象(oop)的三个基本特征 :封装 ,多态,继承;
  • 封装 :封装是对象和类概念的主要特性。封装,把客观事物封装成抽象的类,并且把自己的部分属性和放法提供给其他对象。
  • 继承 :面向对象编程(oop)语言的主要功能是"继承"。继承是指这样的一种能力:他可以使用现有类的功能,
  • 多态 :允许将子类类型的指针赋值给父类类型的指针,同一个函数调用会有不同的执行效果。

dart所有的东西都是对象,所有的对象都继承自Object类。

dart是一门使用类和单继承的面向对象语言,所有的对象都是类的实例,并且所有的类都是Object的子类

一个类通常是由属性和放法组成。

系统的类
void main (){
List list = new List();
list.isEmpty;
list.add('香蕉');
list.add('苹果');

Map m = new Map();
m["userName"] = '冯上进';
m.addAll({"age":20});
}
自定义类 定义里面的属性和放法 默认构造函数 实例化的时候调用
class Person{
String name = "张三";
int age = 20;
Person(){
print('这是构造函数的内容,这个方法在实例化时候被触发 默认构造函数');
};
void getInfo(){
	print("${this.name} --- ${this.age}");
	}
}
Person n = new Person();
print(n.name);//张三
n.getInfo(); // 张三--- 20

class Person{
String name ;
int age;
//默认构造函数
Person(String name,int age){
print('这是构造函数的内容,这个方法在实例化时候被触发 默认构造函数');
this.name = name;
this.age = age;
}; // 默认构造函数的简写Person(this.name,this.age);



void getInfo(){
	print("${this.name} --- ${this.age}");
	}
}
Person n = new Person('张1',20);
print(n.name);//张1
print(n.age);//20
Person n1 = new Person('李四',25); //类 可以实例化多次 

自定义类命名构造函数 命名构造函数可以写多个 默认构造函数只能写一个

class Person{
String name = "张三";
int age = 20;
Person.now(){
	print('命名构造函数');
}
Person(){
print('这是构造函数的内容,这个方法在实例化时候被触发 默认构造函数');
};
void getInfo(){
	print("${this.name} --- ${this.age}");
	}
}
Person n = new Person.now(); //命名构造函数   var time = new Datetime.now();  也属于命名构造函数

总结:1.默认构造函数 和命名构造函数 都在实例化的时候被触发。传参和默认构造函数一样

​ 2.构造函数封装 单独抽离 使用 import 引入

import '文件名/类文件夹';
class Person {
  String name = "冯上进";
  int age = 20;
  //默认构造函数
  Person(String name, int age) {
    this.name = name;
    this.age = age;
  }
  //命名构造函数
  Person.son(String name, int age) {
    this.name = name;
    this.age = age;
  }
}
封装好的类
import 'person.dart';  //最前面不用加点

void main() {
  var n = new Person('李四', 100);
  print(n.name);
  print(n.age);
  var n1 = new Person("冯大大", 1000);
  print(n1.name);
  print(n1.age);

  var n2 = new Person.son("江西", 100);
  print(n2.name);
  print(n2.age);
}
调用上面的类

dart中的是有方法和是有属性

Dart个其他面向对象不一样,Dart中没有public private protected 这些访问修饰符合。但是我们可以使用 " - " (下划线)把一个属性或方法定义成私有(必须在一个文件里面)。

class Person {
  String _name = "冯上进";//是有私属性
    getName(){//公有方法
  return this._name; //私有属性  通过公有方法可访问
  }
  _run (){
  print('123');
  }//是私有方法
  
  getrun(){//公有方法
  this._run(); 
  }
  int age = 20;
  //默认构造函数
  Person(String name, int age) {
    this.name = name;
    this.age = age;
  }
  //命名构造函数
  Person.son(String name, int age) {
    this.name = name;
    this.age = age;
  }

}
封装好的类
import 'person.dart';  //最前面不用加点

void main() {
 	var n = new Person();
 	n._name; //访问不到 _name 因为 _name 为上面 抽离类的是有方法。
 	n.getName(); // 通过公有的方法可以访问上面类的是有方法,获取私有属性。
 	n._run(); //访问不到上面类的私有方法。 
 	n.getrun();//通过公有方法 可以访问到 上面的私有方法。
 	
}

总结:通过公有方法间接调用私有属性和私有方法、

类中的getter 和setter修饰符用法。
class Per {
  int width;
  int height;

  sum(int width, int height) {
    return width * height;
  }
}

void main() {
  var n = new Per();
  var s = n.sum(100, 200);
  print(s); //40000
}// 第一种方法 非get访问类里面的方法
class Per {
  int width;
  int height;
   Per(this.width, this.height);

  get sum { //通过花括号计算属性得到的 sum的值
    return width * height;
  }
  set areaHeight(value){
  	this.height = value;
  }
  
}

void main() {
 var n = new Per(20,20);
 n.sum // 400  使用get的时候 访问类里面的方法是用带你的形式访问。  注意调用直接通过访问属性的方式访问 sum 类似计算属性;
 n.areaHeight = 10;  //set的计算属性 调用修改高度后 再计算面积
 print(n.sum); //调用修改高度后 再计算面积

}
dart中我们也可以在构造函数体运行之前初始化实例变量
class Rect(){
int height;
int width;
Rect():height=2,width=10{
print("${this.height}---${this.width}"); //2---10初始化实例变量
}  //dart中我们也可以在构造函数体运行之前初始化实例变量
getArea(){
return width*height;
}
}
void main(){
Reat r = new Reat();
print(r.getArea());
}

dart类中的静态成员静态方法

  • 使用Static 关键字来实现类级别的变量和函数。
  • 静态方法不能访问非静态成员,非静态方法可以访问静态成员。
  • 静态方法和静态属性直接通过类进行访问 Rect.height
class Rect(){
static int height = 10;
static int width = 20;
int age = 20;  //静态方法

static getArea(){
return width*height;  

}
static prints(){
	return age;  //报错 静态方法不可访非静态属性。
	printWdith(); //报错 静态方法不可访非静态方法。
}
//非静态方法
void printWidth(){
print(width); //非静态方法 可以访问静态成员和非静态成员。
print(this.age); //访问非静态属性
prints() //调用静态方法
 }
}
void main(){

print(Rect.height); //10
print(Rect.getArea());//200 //通过类访问静态属性和静态方法

Reat r= new Reat();
r.printWidth();//20 20

}
dart中的对象操作符

? 条件运算符(了解)

as 类型转换

is 类型判断

… 级联操作(连缀)(记住)

class Person {
  String name = "张三";
  int age = 20;
  printName() {
    print(this.name);
  }
}

void main() {
  Person p = new Person();
  if (p is Person) { //is的用法
    p.name = "李四";
  }
  p.printName(); //李四
  
  
  var p1;
  p1 ='';
  p1 = new Person('张三',20);
  p1.printInfo();  //老版本报错 新版本不报错
  (p1 as Person).printInfo(); //类型转换 as 字符串转换成Person 类
}
class Person {  //级联..的用法
  String name;
  int age;
  Person(this.name, this.age);
  printName() {
    print("${this.name}---${this.age}");
  }
}

void main() {
  Person p = new Person('张三', 20);
  p.printName();
  p.name = '李四'; //20
  p.age = 200;
  p.printName(); //200
//上面的简写下面的样子
  //用级联 ..
  p
    ..name = "李四222"
    ..age = 100
    ..printName(); //李四222---100
}
dart中类的继承:
  • 子类使用extends关键词来继承父类。
  • 子类会继承父类课件的属性和方法 但是不会继承构造函数。
  • 子类能复写父类的方法getter 和setter。
class Father {
  String name = '爸爸';
  int age = 20;
  void printName() {
    print(this.name);
  }
}

class Son extends Father {}

void main() {
  Son n = new Son();
  print(n.name); //爸爸 
}
//类的继承
//构造函数继承

class Father {
  String name = '爸爸';
  int age = 20;
  Father(this.name, this.age);
  void printName(String name, int age) {
    print("${this.name}---${this.age}");
  }
}

class Son extends Father {
  Son(String name, int age) : super(name, age) {}  //关键 处 继承默认构造函数
}

void main() {
  Son n = new Son('张三', 20);
  n.printName('冯上进', 20);
}
//构造函数继承 子类可以传自己的参数 定义自己的方法

class Father {
  String name = '爸爸';
  int age = 20;
  Father(this.name, this.age);
  Father.xxx(this.name, this.age);
  void printName(String name, int age) {
    print("${this.name}---${this.age}");
  }
  void age(){
  print(this.age);
  }
}

class Son extends Father {
  String sex;
  //继承父类的默认构造函数 和命名构造函数 和传参
  Son(String name, int age, String sex) : super.xxx(name, age) { //命名构造函数传参 
    this.sex = sex;
  }
  run() { //自己的方法
    print("${this.name}---${this.age}---${this.sex}");
    super.age(); // 在run方法里面调用父类的方法  age
  }
  @override  //复写父类的方法  @override可写 可不写
    void printName(String name, int age) {
    print("${this.name}-----${this.age}");
  }
}

void main() {
  Son n = new Son('张三', 20, '男');
  n.printName('冯上进', 20); //'冯上进'-----20
  n.run(); // 张三 --- 20 --- 男  20
 
}

总结: 1.类的继承 extends 继承父类的属性方法 继承不了 构造函数 2, 3为继承构造函数的方法

​ 2.默认构造函数的继承 Son(String name, int age) : super(name, age) {}

​ 3.命名构造函数的继承 Son(String name, int age) : super.xxx(name, age){}

​ 4.子类可以传自己的参数到构造函数中 Son(String name, int age, String sex) : super.xxx(name, age)

​ 5.复写父类的 方法 @override(可以省略) 父类的方法名

​ 6.子类方法中可以调用父类的方法 super.age(); // 在run方法里面调用父类的方法 age

dart中的抽象类 多态 和接口

Dart中抽象类:Dart抽象类主要用于定义标准,子类可以继承抽象类,也可以实现抽象类接口

  1. 抽象类通过 abstract 关键字来定义
  2. Dart 中的抽象方法不能用abstract声明,Dart中没有方法体的方法我们称之为抽象方法
  3. 如果子类继承抽象类必须得实现里面的抽象方法
  4. 如果把抽象类当做接口实现的话必须实现抽象类里面定义的所有属性和方法。
  5. 抽象类不能实例化,只有继承它的子类可以。

extends抽象类 和 implements的区别:

  1. 如果要复用抽象类里面的方法,并且要用抽象方法约束子类的话我们就用 extends继承抽象类。(只是复用抽象类里面的方法 并且使用抽象类约束方法);
  2. 如果 只是把抽象类当做标准的话 我们就用implements 实现抽象类。(只是作为约束的标准 作为接口)

案例:定义一个Animal 类 要求它的子类必须包含eat方法。

//定义一个 Animal的抽象类 

abstract class Animal {
  eat(); // 抽象方法
  run(); // 抽象方法
  printInfo() {
    //抽象类中的普通方法  子类的都可以继承
    print("抽象类中的普通方法"); 
  }
}

class Dog extends Animal {
  @override
  eat() {
    print('小狗吃骨头');
  }

  @override
  run() {
    print('小狗在跑');
  }
}

class Cat extends Animal {
  @override
  eat() {
    print('小猫在吃老鼠');
  }

  @override
  run() {
    print('猫在跑');
  }
}

void main() {
  Dog l = new Dog(); //抽象类的 子类可以实例化
  l.eat();
  l.run();
  l.printInfo();
  Cat c = new Cat();//抽象类的 子类可以实例化
  c.eat();
  c.run();
  c.printInfo();

  // Animal a = new Animal();  //抽象类不能被实例化 只有继承它的子类可以实例化
}

Dart中的多态
  1. 允许将子类的类型的指针赋值给父类类型的指针,同一个函数调用会有不同的执行结果。上面的代码就是 多态。
  2. 子类的实例赋值给父类的引用。
  3. 多态就是父类定义一个方法不去实现,让继承他的子类去实现,每个子类有不同的表现。

子类的实例赋值给父类的引用

//定义一个 Animal的抽象类

abstract class Animal {
  eat(); // 抽象方法

  printInfo() {
    //抽象类中的普通方法
    print("抽象类中的普通方法");
  }
}

class Dog extends Animal {
  @override
  eat() {
    print('小狗吃骨头');
  }

  run() {
    print('小狗在跑');
  }
}

class Cat extends Animal {
  @override
  eat() {
    print('小猫在吃老鼠');
  }

  run() {
    print('猫在跑');
  }
}

void main() {
  Animal d = new Dog(); //子类的实例赋值给父类的引用 多态
  d.eat(); //小狗在跑
  // d.run(); //会报错
  Animal c = new Cat();
  c.eat(); //猫在跑
  // c.run(); //会报错
}

Dart接口

和Java 一样,dart 也有接口 ,但是和java还是有区别的。

首先,dart的接口没有interface关键字第一接口,而是普通类或抽象类都可以作为接口被实现。

同样使用implement 关键字进行实现。

但是dart的接口有点奇怪,如果实现的类是普通类,会将普通类和抽象类中的属性的方法全部需要复写一遍。

而因为抽象类可以定义抽象方法,普通类不可以,所以一般如果要实现像java接口那样的方式,一般会使用抽象类。

建议使用抽象类(定义标准)定义接口。

//定义一个DB库 支持mysql mssql mongodb  mysql mssql mongodb三个类里面有同样的方法
abstract class Db {  //抽象类当做接口 定义标准 
  //接口
  String uri; //数据库的链接地址
  add(String data);
  save();
  delete();
}

//实现Mysql接口 类似继承标准
class Mysql implements Db {
  @override
  String uri;

  Mysql(this.uri);
  @override
  add(data) {
    print('这是mysql的add方法' + data);
  }

  @override
  delete() {
    // TODO: implement delete
    throw UnimplementedError();
  }

  @override
  save() {
    // TODO: implement save
    throw UnimplementedError();
  }
}

//实现ms接口
class ms implements Db {
  @override
  String uri;

  @override
  add(data) {
    // TODO: implement add
    throw UnimplementedError();
  }

  @override
  delete() {
    // TODO: implement delete
    throw UnimplementedError();
  }

  @override
  save() {
    // TODO: implement save
    throw UnimplementedError();
  }
}

void main() {
  Mysql n = new Mysql('xxxxx');
  print(n.uri);
  n.add('冯上进');
}

Dart 中一个类实现多个接口 实现所有接口中的属性和方法 (可以实现多接口);
abstract class A {
  String name;
  printA();
}

abstract class B {
  printB();
}

class C implements A, B { //一个类继承多个接口
  @override
  String name = "张三";
  @override
  printA() {
    print(A);
  }

  @override
  printB() {
    print(B);
  }
}

void main() {
  C c = new C();
  print(c.name);//张三
  c.printA(); //A
  c.printB();//B
}

mixins 的中文意思是混入,就是在类中混入其他功能。

类是不可以继承的,但是在Dart中可以使用mixins实现类似多继承的功能

因为mixins使用的条件,随着Dart版本一直在变,这里讲的是Dart2.x中使用mixins的条件

  1. 作为mixins的类只能继承自Object,不能继承其他类(A类和B类不能继承其他类 否则会报错)。
  2. 作为mixins的类不能有构造函数
  3. 一个类可以mixins多个mixins类
  4. mixins绝不是继承,也不是接口,而是一种全新的特性
class A {
  String info = 'this is info';
  A();//不能有构造函数 
  printA() {
    print(A);
  }
}

class B {
  printB() {
    print(B);
  }
}



class Person {
  String name;
  int age;
  Person(this.name, this.age);
  printP() {
    print('${this.name}---${this.age}');
  }
}

 //A和B两个类 有相同的方法 那个类 ↓ 谁在后面 打印谁 
class C extends Person with A, B {  //继承了 Person的 构造函数 又继承了 Persond的属性和方法 
  C(String name, int age) : super(name, age) {}
}

void main(){
C c = new C('张三',20);
c.printP(); //张三 ---20
}

class C with A, B {}//可以使用mixins实现类似多继承的功能 通过 with继承

void main() {
  C c = new C();
  print(c.info);//this is info
  c.printA(); //A
  c.printB();//B
  
  
  print(c is C); true
print(c is A);true
print(c is B);true   //c为A和B的超类
A a = new A();
print(a is Object);true   //所有的类都继承自Object
}




泛型 泛型方法 泛型类 泛型接口(解决代码沉冗问题)。

通俗理解:泛型就是解决 类 接口 方法的复用性,以及对不特定数据类型的支持(类型校验)。

泛型方法

不指定类型放弃了类型检查。我们在想实现的是传入什么 返回什么。比如:传入number类型必须返回 num

T getData<T>(T value){ //对传入和返回的参数做校验  去除第一个 T 就是 不对返回参数做校验
	return value
}

void main(){
print(getData int(123))//123
print(getData String('xxx'))//xxx

}

泛型类

系统内置泛型类 List

void main() {
  List myList = new List<int>();  //<int>尖括号 里面为int类型添加的只能是数字  不能是别的类型 
  myList.add(123);
  myList.add('123');//错误写法
  print(myList);
}
//泛型类
class Sum<T> {
  List list = new List<T>();

  void printS(T value) {
    this.list.add(value);
  }
}

void main() {
  Sum s1 = new Sum<int>(); //实例化子类的类型为int  所以list不能add字符串。
  s1.list.add(123); 
  s1.list.add("你好"); //报错 因为s1实例传的;类型为int类型。

  print(s1.list);
}
泛型接口(可以是普通类 也是可以是抽象类)

Dart中的泛型接口

实现数据缓存的功能:有文件缓存,和内存缓存。内存缓存和文件缓存按照接口实现。

  1. 定义一个泛型接口 约束实现它的子类必须有getByKey(key) 和 setByKey(key,value)。
  2. 要求setByKey的时候的value的类型和实例化子类的时候指定的类型一致。
// 泛型接口
abstract class Cache<T>{
	getByKey( String key);
	void setByKey(String key,T value);
}



abstract class Person<T> {
  //泛型接口
  getByKey(String key);
  void setByKey(String key, T value);
}

class Son<T> implements Person<T> {
  @override
  getByKey(String key) {
    print('123');
  }

  @override
  void setByKey(String key, T value) {
    print("${key} ----${value}");
  }
}

void main() {
  Son s = new Son<int>(); //要求setByKey 中的value的类型 与子类型实例化参数一致
  s.setByKey('冯上进', 123);
  
}

dart中的库 自定义库 系统库 第三方库

在Dart中,库的使用是通过import 关键字引入。

library指令可以创建一个库,每个dart文件都是一个库,即使没有使用library指令来指定。

Dart中的库有三种:

  1. 我们自定义的库 import ’lib/xxx.dart‘;

  2. 系统内置库

    • import ‘dart:math’;
    • import ‘dart:io’;
    • import ‘dart:convert’;
  3. Pub包管理系统中的库

    • http://pub.dev/packages
    • http://pub.flutter-io.cn/packages
    • http://pub.dartlang.org/flutter/
    1. 需要在自己项目根目录新建一个pubspec.yal
    name:xxx
    dessciption:A new flutter module project.
    dependencies:
    	http:^0.12.0+2
    	date_format:^1.0.6
    
    1. 在pubspec.yaml文件 然后配置名称,描述,依赖等信息。
    2. 然后运行pub get 获取包下载到本地
    3. 项目中引入 import ‘package:http//http.dart’ as http; 看文档使用
void main() async {   //使用异步方法 必须加 async await 才可以使用异步方法;
  var result = await testAsync();
  print(result);
}

//异步方法
testAsync() async {  
  return '张三';
}

as 库的重命名

import 'lib/Person1.dart'
import 'lib/Person2.dart as lib'

main(List<String>args) {
Perosn p1 = new Person('张三',20);
p1.PrintInfo();
}
lib.Person p2 = new lib.Person('李四',20);
p2.PrintInfo();

部分导入

import 'lib/myMath.dart' show getName;  //只能用库里面的getName();
import 'lib/myMath.dart' hide getName;  //隐藏库里面的getName();
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呵呵的牛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值