Dart语法

每段语气都需要逗号隔开

定义变量

Dart用两种定义变量的方式,第一种明确变量类型定义,第二种通过推导类型定义
通过数据类型定义变量,dart的数据类型有:
数字(int/double),字符串(String),布尔(Boolean),列表,集合(Set),映射(Map),符文。

一:使用变量类型定义

入口函数main

void main(List<String> args) {int firstFlutterCode = 12;print(firstFlutterCode);
}

二:类型推导定义

声明变量关键词:var、final、const

final:用于定义常量,可以通过计算或者函数进行赋值

const:用于什么常量,必须赋值,且值必须在编写时就确定,不可通过函数结果或者计算结果赋值

void main(List<String> args) {int firstFlutterCode = 12;var a = 12;const b = 'wosh';const ab = 12 / a; //这种方式不对,不可以这样final c = 12 / 3;final ac = 12 / a; //可以通过计算实现赋值print(firstFlutterCode);print(a);print(b);print(c);print(ab);
}

三:字符串拼接

通过${变量拼接}

void main(List<String> args) {final a = 12;final b = 'xiaozi';print("我的名字叫$b,今年有${a}岁");
}

如果是变量,可以省略{},如果是对象调动变量的形式,就不可以省略大括号。

四:集合相关

列表:

final list = ["12", '12'];

set集合:

final set = {'12', 12};

map映射:

final map = {'name': '小智', 'age': 13};

五:定义函数

void main(List<String> args) {print(sum(1, 2));
}
int sum(int a, int b) {return a + b;
}

函数定义有返回值必须明确放回类型,不能使用var,final,const定义,但是可以不写类型,可以推断出返回值类型,但是不推荐使用。

可选参数:

注意:可选参数必须给初始化值,如果不给,需要使用?

[参数1,参数2…]命名可选参数

void p(String name, [int? a, int sex = 0]) {}
p('小智', 12);

{参数1,参数2,…}

命名可选参数在调用的时候必须带参数名,可选参数必须带初始化值,如果不带,将会被报错。

void f(String name, {int? a, int b = 12}) {}
f('小智', a: 12);

注意:只有可选参数才能有形参默认值

函数作为函数的参数

void main(List<String> args) {p(f);
}
void p(Function foo) {foo();
}
void f() {print("aa");
}

参数作为匿名函数的时候:

  p(() {print("bbb");});
void p(Function foo) {foo();
}

箭头函数作为参数:

箭头函数:当函数体只有一行代码的时候可以使用

p(() => print('ccc'));
void p(Function foo) {foo();}

函数作为参数带参数:

void main(List<String> args) {p(fn);
}
void p(Function f) {f('12');
}
void fn(String a) {print(a);
}

这种写法我们使用的不多,我们一般直接将函数的返回值作为函数进行传参

void main(List<String> args) {p(f);
}
void p(void fn(int a, int b)) {fn(12, 23);
}
void f(int d, int c) {print("${d}+++${c}");
}

此时的阅读比较差,可以使用typedef定义函数参数

void main(List<String> args) {p(f);
}
typedef C = void Function(int a, int b);
void p(C c) {c(12, 23);
}
void f(int d, int c) {print("${d}+++${c}");
}

函数作为返回值

void main(List<String> args) {var a = demo();var b = a(12, 23);print(b);
}
demo() {return (num1, num2) {return num1 + num2;};
}

运算符

这部分值写和JavaScript不一样的运算符

整除:~/

3~\2:结果为1

赋值运算符:

??=:表示如果变量为null,赋值,不为null将不赋值。

void main(List<String> args) {var name = 'xiaozhi';name ??= '小红';print(name);
}

??:表示前面的变量不为null,就是用改变量,如果为null,将使用后面的值

void main(List<String> args) {var name = 'xiaozhi';var myName = name ?? '小丽';print(myName);
}

级联运算符

…类似JavaScript的链式调用

void main(List<String> args) {//一般情况下我是时这么调用的var p = Person();p.name='xiao';p.show();p.eat();//级联方式var p1 = Person()..name = 'xiaozhi'..eat()..show();
}class Person {var name;void show() {print("yifu");}void eat() {print("吃法");}
}

循环控制

if的条件必须是rue或者false,如果一个字符串为空或者为null的时候,不会将其转成boolean类型

for循环有两种,普通for循环和for…in循环

其他循环体和JavaScript一样的。

类和对象

使用class关键词定义

void main(List<String> args) {var p1 = Person('12');var p = Person.nameAndAge('12', '13');p.show();
}
class Person {var name;var age;//dart是没有构造方法重载的Person(this.name);//命名构造函数Person.nameAndAge(this.age, this.name);show() {print(name);print(age);}
}

我们可以使用构造函数和命名构造函数的方式实现方法重载

Object类型和dynamic类型

Object类型:通过多态实现的变量,编译期调用方法会报错

void main(List<String> args) {Object obj = '123';obj.substring(1);//报错
}

dynamic:表示的是动态类型,编译期调用方法不会报错,但是在运行阶段可能会报错,导致运行错误。

  dynamic obj = '123';obj.substring(1)

重写tostring方法

class Person {var name;var age;Person(this.name, this.age);String toString() {return "$name,$age";}
}

类的初始化列表

使用finale修饰的,不能再方法内进行赋值,可以通过以下方式赋值

class Person {final String name;final int age;Person(this.name, {int age}):this.age=age??10{//不支持该方式,使用以上方式//this.age=age};
}

多个初始化列表使用逗号隔开。

下面这种方式叫做构造函数重定向:

class Person {String name;int age;Person(String name) : this._personConstructer(name, 0);Person._personConstructer(this.name, this.age);
}

常量构造函数:

构造函数使用const修饰,成员变量使用final修饰,保证每次创建对象都保证只创建一个对象

class Person {final String name;final String age;const Person(this.name, this.age);
}

工厂构造函数

class Person {String name;String age;Person(this.name, this.age);static final Map<String, Person> _nameCache = {};static final Map<String, Person> _ageCache = {};factory Person.withName(String name) {if (_nameCache.containsKey(name)) {return _nameCache[name];} else {final p = Person(name, '0');_nameCache[name] = p;return p;}}factory Person.withAge(String age) {if (_nameCache.containsKey(age)) {return _ageCache[age];} else {final p = Person('defualt', age);_nameCache[age] = p;return p;}}
}

代码都是对的,不知道为什么报错,不知道是不是语法变了。艹!!!!!!

get/set

class Person {String name;set setName(String name) {this.name = name;}String get getName {return name;}
}

代码还是报错,我真的服气了,写的代码和学习的一模一样都能报错!!必须先初始化值!!!!

类的继承

在子类的初始化列表内调用父类构造函数

class Animal {String name;Animal(this.name);
}
class Person extends Animal {String name;String age;Person(this.name, this.age) : super(name);
}

抽象类

// 注意二: 抽象类不能实例化
abstract class Shape {String getInfo() {return "形状";}//factory Shape() {//  return Rectangle();//}
}// 注意一:继承自抽象类后, 必须实现抽象类的抽象方法
class Rectangle extends Shape {@overrideint getArea() {return 100;}
}

隐式接口

Dart中没有哪一个关键字是来定义接口的,没有这些关键字interface/protocol,默认情况下所有的类都是隐式接口, Dart支持单继承,当将一个类当做接口使用时, 那么实现这个接口的类, 必须实现这个接口中所有方法

class Runner {void running() {}
}
class Flyer {void flying() {}
class Animal {void eating() {print("动物次东西");}void running() {print("running");}
}
class SuperMan extends Animal implements Runner, Flyer {@overridevoid eating() {super.eating();}@overridevoid flying() {}
}

mixin混入

使用mixin定义混入类,使用with引入混入

main(List<String> args) {final sm = SuperMan();sm.running();sm.flying();
}
mixin Runner {void running() {print("runner running");}
}
mixin Flyer {void flying() {print("flying");}
}
class Animal {void eating() {print("动物次东西");}void running() {print("animal running");}
}
class SuperMan extends Animal with Runner, Flyer {@overridevoid eating() {super.eating();}void running() {print("SuperMan running");}
}

类属性和类方法

class Person {// 成员变量String name;// 静态属性(类属性)static String courseTime;// 对象方法void eating() {print("eating");}// 静态方法(类方法)static void gotoCourse() {print("去上课");}
}

枚举类型

main(List<String> args) {final color = Colors.red;switch (color) {case Colors.red:print("红色");break;case Colors.blue:print("蓝色");break;case Colors.green:print("绿色");break;}print(Colors.values);print(Colors.red.index);
}
enum Colors {red,blue,green
}

使用类库

使用系统库

系统的库: import ‘dart:库的名字’;

// import 'dart:io';
// import 'dart:isolate';
// import 'dart:async';
// import 'dart:math';
import 'dart:math';
main(List<String> args) {final num1 = 20;final num2 = 30;print(min(num1, num2));
}

使用自定义库

就是引入自己创建的dart文件,使用里面的方法和变量等

  • 1.补充一: as关键字给库起别名

  • 2.补充二: 默认情况下载导入一个库时, 导入这个库中所有的内容

    • show: 执行要导入的内容
    • hide: 隐藏某个要导入的内容, 导入其它内容
  • 3.公共的dart文件的抽取: export


// import 'utils/math_utils.dart' as mUtils;
// import "utils/math_utils.dart" show sum, mul;
// import "utils/math_utils.dart" hide mul;
// import 'utils/date_utils.dart';
import "utils/utils.dart";
main(List<String> args) {// print(sum(20, 30));print(sum(20, 30));// print(mul(20, 30));print(dateFormat());min(20, 30);
}

date_utils.dart文件:

String dateFormat() {return "2020-12-12";
}

math_utils.dart文件:

int sum(int num1, int num2) {return num1 + num2;
}
int mul(int num1, int num2) {return num1 * num2;
}
int min(int num1, int num2) {return num1 > num2 ? num2: num1;
}

提取公共代码到一个文件内:utils.dart(看以上代码)

export 'math_utils.dart';
export 'date_utils.dart';

使用第三方库

导入方式一样,只是地址为第三方地址

import 'package:http/http.dart' as http;
main(List<String> args) async {var url = 'http://123.207.32.32:8000/home/multidata';var response = await http.get(url);print('Response status: ${response.statusCode}');print('Response body: ${response.body}');
}

flutter环境配置

环境配置地址:https://flutter.cn/docs/get-started/install/windows

按照这个环境配置即可,如需使用其他模拟器,请自行百度。

创建项目

flutter create 项目名     ----创建项目指令
flutter run              ----运行项目

文件

.dart_tool:用于存放第三方dart库使用,记录第三方库的相关信息,只要下载会自动记录

android:flutter工程

ios:ios工程包

web:web项目工程

window:windows桌面应用工程

build:打包目录

lib:工程源代码存放处,主入口文件存放处。

test:测试代码存放处。

第一个flutter工程

将main中的代码全部删除,写入:

import 'package:flutter/cupertino.dart';
void main(List<String> args) {runApp(app)
}

入口需要执行一个全局函数runApp,用于运行项目,runApp函数接收一个类型为Widget的参数。

注意:在flutter中,万物皆是Widget

在flutter中,几乎所有的组件都实现了Widget(按照前端的说法,就是一个组件),属于Widget类型,此时我们的app参数可以写入:

import 'package:flutter/cupertino.dart';
void main(List<String> args) {var app = Text("hello wolrd ");runApp(app);
}

这时候会报一个错。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v49dFA6Q-1652173929950)(\img\fristerror.PNG)]

这时候会发现,这个错误是没有设置文字布局的排版方向,因为flutter考虑到在很多国家的文字排版都不一样,所以将排版方向设置为必填项目。

重启项目,结果为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kNJDXFC6-1652173929959)(\img\helloworld.PNG)]

此时第一个项目运行成功!

模板脚手架

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main(List<String> args) {var app = MaterialApp(home: Scaffold(appBar: AppBar(title:Text("我的第一个应用"),) ,body: Center(child: Text("任性的代码",style: TextStyle(color:Colors.amber,           )        ),),),);runApp(app);
}

MaterialApp:App的风格为Material提供的风格,Material中的组件定义了自己的默认样式,

Scaffold:脚手架,主要有appBar和body两个属性,appBar表示应用的上头,body应用的内容。

widget

widget:按照前端的说法widget就是一个个个的组件,也就说flutter可以按照组件化编程,但是和react或者vue不同的是,flutter的相关样式或者其他代码是写在一块的。注意:全部的widget都是无状态的,只能通过定义类的形式来定义、获取、修改相关的状态。

组件有个widget属性,表示父组件的实例

widget有两种:StatelessWidgetStatefulWidget两种

  • StatelessWidget: 没有状态改变的Widget,通常这种Widget仅仅是做一些展示工作而已;
  • StatefulWidget: 需要保存状态,并且可能出现状态改变的Widget;

StatelessWidget

void main(List<String> args) {var app = MaterialApp(home: AppHomePage(),);runApp(app);
}
class AppHomePage extends StatelessWidget {@overrideWidget build(BuildContext context) {// TODO: implement build//throw UnimplementedError();return Scaffold(appBar: AppBar(title: Text("我的"),),body: AppBodyPage());}
}
class AppBodyPage extends StatelessWidget {@overrideWidget build(BuildContext context) {// TODO: implement buildreturn Center(child: Text("任性的代码",style: TextStyle(color: Colors.amber,)),);}
}

StatefulWidget

class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(debugShowCheckedModeBanner: false,home: HYHomePage());}
}
class HYHomePage extends StatelessWidget {@overrideWidget build(BuildContext context) {print("HYHomePage Build");return Scaffold(appBar: AppBar(title: Text("第一个Flutter程序"),),body: HYContentBody());}
}
// StatefullWidget: 继承自StatefulWidget的类(可以接收父Widget传过来的数据)/State类(状态)
// flag: 状态
// Stateful不能定义状态 -> 创建一个单独的类, 这个类负责维护状态
class HYContentBody extends StatefulWidget {@overrideState<StatefulWidget> createState() {return HYContentBodyState();}
}
class _HYContentBodyState extends State<HYContentBody> {var _flag = true;@overrideWidget build(BuildContext context) {print("HYContentBodyState Build");return Center(child: Row(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Checkbox(value: flag,onChanged: (value) {this.setState(() {flag = value;});},),Text("同意协议", style: TextStyle(fontSize: 20))],),);}
}

StatefulWidget本身是不能用于定义状态的,而是通过其内部的createState函数创建一个状态,该状态需要继承一个State类,在State类中可以使用setState来实现对状态的改变。

注意:StateWidget用于管理状态的类和其状态最好加‘_’,加’_'之后该类只能被StateWidget类使用。

widget生命周期

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ysn5YaCT-1652173929960)(\img\shengming.PNG)]

对于StatelessWidget无状态组件,其生命周期只有:constructor>build>deactivate>dispose这条路径。

对于StatefulWidget有状态组件,生命周期完整过程如上图所示(图缺少最开始的调用的constructor)。

生命周期完整介绍:

https://www.jianshu.com/p/6ed6f7de01ff

didChangeDependencies():当State对象的依赖发生变化时会被调用;didChangeDependencies 方法调用后,组件的状态变为 dirty,立即调用 build 方法。

reassemble():此回调是专门为了开发调试而提供的,在热重载(hot reload)时会被调用,此回调在Release模式下永远不会被调用。

didUpdateWidget():在父widget重新构建子widget时,子widget的didUpdateWidget可能会被调用

eactivate():当State对象从树中被移除时,会调用此回调。在一些场景下,Flutter framework会将State对象重新插到树中,如包含此State对象的子树在树的一个位置移动到另一个位置时(可以通过GlobalKey来实现)。如果移除后没有重新插入到树中则紧接着会调用dispose()方法。

dispose():当State对象从树中被永久移除时调用;通常在此回调中释放资源。

TextWidget

普通文本:Text

文本使用的组件叫做Text,在相关的属性可以通过点击该组件查看源码

  const Text(String this.data, {Key? key,this.style,this.strutStyle,this.textAlign,this.textDirection,this.locale,this.softWrap,this.overflow,this.textScaleFactor,this.maxLines,this.semanticsLabel,this.textWidthBasis,this.textHeightBehavior,}) :
  • 控制文本布局的参数: 如文本对齐方式 textAlign、文本排版方向 textDirection,文本显示最大行数 maxLines、文本截断规则 overflow 等等,这些都是构造函数中的参数;
  • 控制文本样式的参数: 如字体名称 fontFamily、字体大小 fontSize、文本颜色 color、文本阴影 shadows 等等,这些参数被统一封装到了构造函数中的参数 style 中。
Text(text,textAlign: TextAlign.center,maxLines: 3,overflow:TextOverflow.ellipsis,style: TextStyle(color:Colors.green,fontSize:30,fontStyle: FontStyle.italic,));

注意:在之前我们在runApp中直接使用Text需要设置文字方向,而在MaterialApp中不需要设置主要是MaterialApp已经有默认的文本方向了,而且,我们设置Text的文本方向不是问Text widget本身设置的而是为其RichText设置的,Text会渲染成一个RichText。

富文本Text.rich()

接收一个TextSpan类型的widget,在TextSpan内可以创建多个TextSpan

Text.rich(TextSpan(children: [TextSpan(text:'你好',style: TextStyle(color:Colors.red)),TextSpan(text: '好不好!', style: TextStyle(color: Colors.green)),TextSpan(text: '别呀!燕子!', style: TextStyle(color: Colors.black)),]));

Button Widget

RaisedButton:带有阴影的按钮

RaisedButton(child: Text("RaisedButton"),textColor: Colors.white,color: Colors.purple,onPressed: () => print("RaisedButton Click"),
),

FlatButton:扁平化按钮

FlatButton(child: Text("FlatButton"),color: Colors.orange,onPressed: () => print("FlatButton Click"),
),

OutlineButton:线框按钮

 OutlineButton(child: Text("OutlineButton"),onPressed: () => print("OutlineButton"),
),

FloatingActionButton:悬浮按钮

这个按钮可以和appBar和body同级,

自定义按钮:使用基础FlatButton组件自定义

    FlatButton(color: Colors.amberAccent,shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),child: Row(mainAxisSize: MainAxisSize.min,children: <Widget>[Icon(Icons.favorite, color: Colors.red,),Text("喜欢作者")],),onPressed: ()=>{},)

ButtonTheme:按钮风格,

定义按钮小部件的默认配置,

ButtonTheme(minWidth: 30,height: 10,child: FlatButton(padding: EdgeInsets.all(0),color: Colors.red,materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,child: Text("Flat Button1"),textColor: Colors.white,onPressed: () {},),
)

图标和字体图标

Icon(Icons.pets, size: 300, color: Colors.orange,);
Icon(IconData(0xe91d, fontFamily: 'MaterialIcons'), size: 300, color: Colors.orange,);
Text("\ue91d", style: TextStyle(fontSize: 100, color: Colors.orange, fontFamily: "MaterialIcons"),);

Image组件

  • Image.assets:加载本地资源图片;
  • Image.network:加载网络中的图片;

加载网络图片

return Center(child: Container(child: Image.network("http://img0.dili360.com/ga/M01/48/3C/wKgBy1kj49qAMVd7ADKmuZ9jug8377.tub.jpg",alignment: Alignment.topCenter,repeat: ImageRepeat.repeatY,color: Colors.red,colorBlendMode: BlendMode.colorDodge,),width: 300,height: 300,color: Colors.yellow,),);

加载本地图片

需要现在pubspec.yaml配置路径,打开assets配置项。

return Center(child: Container(width: 300,height: 300,color: Colors.yellow,child: Image.asset("images/test.jpeg"),),);

圆角图像

方式一:CircleAvatar

 return Center(child: CircleAvatar(radius: 100,backgroundImage:               NetworkImage("https://tva1.sinaimg.cn/large/006y8mN6gy1g7aa03bmfpj3069069mx8.jpg"),child: Container(alignment: Alignment(0, .5),width: 200,height: 200,child: Text("兵长利威尔")),),);

方式二:ClipOval

return Center(child: ClipOval(child: Image.network("https://tva1.sinaimg.cn/large/006y8mN6gy1g7aa03bmfpj3069069mx8.jpg",width: 200,height: 200,),),);

圆角图片ClipRRect

 return Center(child: ClipRRect(borderRadius: BorderRadius.circular(10),child: Image.network("https://tva1.sinaimg.cn/large/006y8mN6gy1g7aa03bmfpj3069069mx8.jpg",width: 200,height: 200,),),);

文本输入TextField

重要属性:

controller:数据绑定控制器

onChanged:数据改变时回调函数

decoration:TextField相关装饰

final mytest = TextEditingController();//控制变量,用于将变量绑定到输入框
Widget build(BuildContext context) {return Column(children: [TextField(controller: mytest,onChanged: (value) => {print(value)},decoration: InputDecoration(icon: Icon(Icons.people),labelText: '用户名',hintText: "请输入用户名",hoverColor: Colors.blue,labelStyle: TextStyle(color: Colors.red,fontSize: 20,)),),FlatButton(child: Text("点击"),onPressed: () => {print(mytest.text)},)],);}

flutter布局

SizedBox

用于设置两组件间间隙

children: <Widget>[TextField(.....),SizedBox(height: 10,),TextField(.....),SizedBox(height: 10,),Container(....)],

单子布局

单子组件只能容纳一个组件,即属性child。

Align

该组件用于设置元素位置相关布局,其Center组件完全继承自该组件。

 Align(alignment: Alignment(1, 1),widthFactor: 5,heightFactor: 5,child: Icon(Icons.pets, size: 50));

Padding

该组件可用于设置内边距

Padding(padding: EdgeInsets.only(bottom: 10),child: Text("你好啊,李银河", style: TextStyle(fontSize: 30, backgroundColor: Colors.red),),);

Container

表示的是一个单子布局容器,该容器类似html的div标签,但容纳的组件只能是一个。

注意:属性color和decoration只能出现一个。

alignment:0.0为原点,距离边距为1,

Container(width: 200,height: 200,alignment: Alignment(0, 0),padding: EdgeInsets.all(20),margin: EdgeInsets.all(10),child: Text("Hello World"),decoration: BoxDecoration(color: Colors.red,border: Border.all(width: 5,color: Colors.purple),boxShadow: [BoxShadow(color: Colors.orange, offset: Offset(10, 10), spreadRadius: 5, blurRadius: 10),BoxShadow(color: Colors.blue, offset: Offset(-10, 10), spreadRadius: 5, blurRadius: 10),]),);

多子布局

Flex布局

事实上,我们即将学习的Row组件和Column组件都继承自Flex组件。

  • Flex组件和Row、Column属性主要的区别就是多一个direction。
  • 当direction的值为Axis.horizontal的时候,则是Row。
  • 当direction的值为Axis.vertical的时候,则是Column。

Row

水平方向尽可能占据比较大的空间,水平方向也是希望包裹内容, 那么设置mainAxisSize = min
垂直方向包裹内容
MainAxisAlignment:主轴

start: 主轴的开始位置挨个摆放元素(默认值)

end: 主轴的结束位置挨个摆放元素

center: 主轴的中心点对齐

spaceBetween: 左右两边的间距为0, 其它元素之间平分间距

spaceAround: 左右两边的间距是其它元素之间的间距的一半

spaceEvenly: 所有的间距平分空间

CrossAxisAlignment:交叉轴

start: 交叉轴的起始位置对齐

end: 交叉轴的结束位置对齐

center: 中心点对齐(默认值)

baseline: 基线对齐(必须有文本的时候才起效果)

stretch: 先Row占据交叉轴尽可能大的空间, 将所有的子Widget交叉轴的高度, 拉伸到最大

Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,children: <Widget>[Text("进击的巨人挺不错的",style: TextStyle(fontSize: 20, color: Colors.white),),IconButton(icon: Icon(Icons.favorite,color: _isFavor? Colors.red : Colors.white,),onPressed: () {setState(() {_isFavor = !_isFavor;});},
)

columns

这个和Row类似,但是是垂直布局

Column(mainAxisAlignment: MainAxisAlignment.spaceEvenly,crossAxisAlignment: CrossAxisAlignment.center,textBaseline: TextBaseline.alphabetic,verticalDirection: VerticalDirection.down,mainAxisSize: MainAxisSize.min,children: <Widget>[Container(width: 80,height: 60,color: Colors.red,child: Text("Hellxo",style: TextStyle(fontSize: 20),),),Container(width: 120,height: 100,color: Colors.green,child: Text("Woxrld",style: TextStyle(fontSize: 30),),),Container(width: 90,height: 80,color: Colors.blue,child: Text("abxc",style: TextStyle(fontSize: 12),),),Container(width: 50,height: 120,color: Colors.orange,child: Text("cxba",style: TextStyle(fontSize: 40),),),],);

Stack

默认的大小是包裹内容的,就是组件如何重叠

alignment: 从什么位置开始排布所有的子Widget

fit: expand(很少) 将子元素拉伸到尽可能大

overflow: 超出部分如何处理

Stack(children: <Widget>[Image.asset("assets/images/juren.jpeg"),Positioned(left: 0,right: 0,bottom: 0,child: Container(padding: EdgeInsets.symmetric(horizontal: 8),color: Color.fromARGB(150, 0, 0, 0),child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,children: <Widget>[Text("进击的巨人挺不错的",style: TextStyle(fontSize: 20, color: Colors.white),),IconButton(icon: Icon(Icons.favorite,color: _isFavor? Colors.red : Colors.white,),onPressed: () {setState(() {_isFavor = !_isFavor;});},)],),),)],);

Positioned

定位组件,

Positioned(left: 20,bottom: -50,child: Container(width: 150,height: 150,color: Colors.red,
)),

flutter滚动

ListView

ListView有三种方式可以实现

  • ListView():默认构造器
  • ListView.build:和默认方式用法没有什么差异,但是会增加首屏的渲染时间,
  • ListView.separated:可以生成列表项之间的分割器,它除了比ListView.builder多了一个separatorBuilder参数,该参数是一个分割器生成器。

ListView()

ListView(children: <Widget>[Padding(padding: const EdgeInsets.all(8.0),child: Text("人的一切痛苦,本质。", style: textStyle),),Padding(padding: const EdgeInsets.all(8.0),child: Text("人活在世界上,不可以有偏差;。", style: textStyle),),Padding(padding: const EdgeInsets.all(8.0),child: Text("我活在世上,遇见些有趣的事。", style: textStyle),)],);
ListTile的使用

在开发中,我们经常见到一种列表,有一个图标或图片(Icon),有一个标题(Title),有一个子标题(Subtitle),还有尾部一个图标(Icon)

ListView(children: <Widget>[ListTile(leading: Icon(Icons.people, size: 36,),title: Text("联系人"),subtitle: Text("联系人信息"),trailing: Icon(Icons.arrow_forward_ios),),ListTile(leading: Icon(Icons.email, size: 36,),title: Text("邮箱"),subtitle: Text("邮箱地址信息"),trailing: Icon(Icons.arrow_forward_ios),),ListTile(leading: Icon(Icons.message, size: 36,),title: Text("消息"),subtitle: Text("消息详情信息"),trailing: Icon(Icons.arrow_forward_ios),),ListTile(leading: Icon(Icons.map, size: 36,),title: Text("地址"),subtitle: Text("地址详情信息"),trailing: Icon(Icons.arrow_forward_ios),)],);
垂直方向滚动

我们可以通过设置 scrollDirection 参数来控制视图的滚动方向。

我们通过下面的代码实现一个水平滚动的内容:

  • 这里需要注意,我们需要给Container设置width,否则它是没有宽度的,就不能正常显示。
  • 或者我们也可以给ListView设置一个itemExtent,该属性会设置滚动方向上每个item所占据的宽度。
 ListView(scrollDirection: Axis.horizontal,itemExtent: 200,children: <Widget>[Container(color: Colors.red, width: 200),Container(color: Colors.green, width: 200),Container(color: Colors.blue, width: 200),Container(color: Colors.purple, width: 200),Container(color: Colors.orange, width: 200),],);

ListView.build

ListView.build适用于子Widget比较多的场景

该方法有两个重要参数:

  • itemBuilder:列表项创建的方法。当列表滚动到对应位置的时候,ListView会自动调用该方法来创建对应的子Widget。类型是IndexedWidgetBuilder,是一个函数类型。
  • itemCount:表示列表项的数量,如果为空,则表示ListView为无限列表。
 ListView.builder(itemCount: 100,itemExtent: 80,itemBuilder: (BuildContext context, int index) {return ListTile(title: Text("标题$index"), subtitle: Text("详情内容$index"));});

ListView.separated

ListView.separated可以生成列表项之间的分割器,它除了比ListView.builder多了一个separatorBuilder参数,该参数是一个分割器生成器。

下面我们看一个例子:奇数行添加一条蓝色下划线,偶数行添加一条红色下划线

 ListView.separated(itemBuilder: (BuildContext context, int index) {return ListTile(leading: Icon(Icons.people),title: Text("联系人${index+1}"),subtitle: Text("联系人电话${index+1}"),);},separatorBuilder: (BuildContext context, int index) {return index % 2 == 0 ? redColor : blueColor;},itemCount: 100);

生成多个元素

List.generate(100, (index) {return Container(color: Colors.purple,alignment: Alignment(0, 0),child: Text("item$index", style: TextStyle(fontSize: 20, color: Colors.white)),);});

GridView组件

GridView用于展示多列的展示,在开发中也非常常见,比如直播App中的主播列表、电商中的商品列表等等。

GridView构造函数

重要属性:

  • SliverGridDelegateWithFixedCrossAxisCount:一列个数
  • SliverGridDelegateWithMaxCrossAxisExtent:设置每个元素的宽度,通过宽度计算出每列的个数。
class MyGridCountDemo extends StatelessWidget {List<Widget> getGridWidgets() {returnList.generate(100, (index) {return Container(color: Colors.purple,alignment: Alignment(0, 0),child: Text("item$index", style: TextStyle(fontSize: 20, color: Colors.white)),);});}@overrideWidget build(BuildContext context) {return GridView(gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3,mainAxisSpacing: 10,crossAxisSpacing: 10,childAspectRatio: 1.0),children: getGridWidgets(),);}
}

-----------------------------------------------------分割线-----------------------------------------

class MyGridExtentDemo extends StatelessWidget {List<Widget> getGridWidgets() {returnList.generate(100, (index) {return Container(color: Colors.purple,alignment: Alignment(0, 0),child: Text("item$index", style: TextStyle(fontSize: 20, color: Colors.white)),);});}@overrideWidget build(BuildContext context) {return GridView(gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(maxCrossAxisExtent: 150,mainAxisSpacing: 10,crossAxisSpacing: 10,childAspectRatio: 1.0),children: getGridWidgets(),);}
}

两种方式也可以不设置delegate

可以分别使用:GridView.count构造函数GridView.extent构造函数实现相同的效果,这里不再赘述。

GridView.build

和ListView一样,使用构造函数会一次性创建所有的子Widget,会带来性能问题,所以我们可以使用GridView.build来交给GridView自己管理需要创建的子Widget。

class _GridViewBuildDemoState extends State<GridViewBuildDemo> {List<Anchor> anchors = [];@overridevoid initState() {getAnchors().then((anchors) {setState(() {this.anchors = anchors;});});super.initState();}@overrideWidget build(BuildContext context) {return Padding(padding: const EdgeInsets.all(8.0),child: GridView.builder(shrinkWrap: true,physics: ClampingScrollPhysics(),gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2,mainAxisSpacing: 10,crossAxisSpacing: 10,childAspectRatio: 1.2),itemCount: anchors.length,itemBuilder: (BuildContext context, int index) {return Container(child: Column(crossAxisAlignment: CrossAxisAlignment.start,children: <Widget>[Image.network(anchors[index].imageUrl),SizedBox(height: 5),Text(anchors[index].nickname, style: TextStyle(fontSize: 16),),Text(anchors[index].roomName, maxLines: 1, overflow: TextOverflow.ellipsis,)],),);}),);}
}

Slivers

我们考虑一个这样的布局:一个滑动的视图中包括一个标题视图(HeaderView),一个列表视图(ListView),一个网格视图(GridView)。

我们怎么可以让它们做到统一的滑动效果呢?使用前面的滚动是很难做到的。

Flutter中有一个可以完成这样滚动效果的Widget:CustomScrollView,可以统一管理多个滚动视图。

在CustomScrollView中,每一个独立的,可滚动的Widget被称之为Sliver。

补充:Sliver可以翻译成裂片、薄片,你可以将每一个独立的滚动视图当做一个小裂片。

Slivers的基本使用

因为我们需要把很多的Sliver放在一个CustomScrollView中,所以CustomScrollView有一个slivers属性,里面让我们放对应的一些Sliver:

  • SliverList:类似于我们之前使用过的ListView;
  • SliverFixedExtentList:类似于SliverList只是可以设置滚动的高度;
  • SliverGrid:类似于我们之前使用过的GridView;
  • SliverPadding:设置Sliver的内边距,因为可能要单独给Sliver设置内边距;
  • SliverAppBar:添加一个AppBar,通常用来作为CustomScrollView的HeaderView;
  • SliverSafeArea:设置内容显示在安全区域(比如不让齐刘海挡住我们的内容)

简单演示一下:SliverGrid+SliverPadding+SliverSafeArea的组合

class HomeContent extends StatelessWidget {@overrideWidget build(BuildContext context) {return CustomScrollView(slivers: <Widget>[SliverSafeArea(sliver: SliverPadding(padding: EdgeInsets.all(8),sliver: SliverGrid(gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2,crossAxisSpacing: 8,mainAxisSpacing: 8,),delegate: SliverChildBuilderDelegate((BuildContext context, int index) {return Container(alignment: Alignment(0, 0),color: Colors.orange,child: Text("item$index"),);},childCount: 20),),),)],);}
}

Slivers的组合使用

使用官方的示例程序,将SliverAppBar+SliverGrid+SliverFixedExtentList做出如下界面:

class HomeContent extends StatelessWidget {@overrideWidget build(BuildContext context) {return showCustomScrollView();}Widget showCustomScrollView() {returnnew CustomScrollView(slivers: <Widget>[const SliverAppBar(expandedHeight: 250.0,flexibleSpace: FlexibleSpaceBar(title: Text('Coderwhy Demo'),background: Image(image: NetworkImage("https://tva1.sinaimg.cn/large/006y8mN6gy1g72j6nk1d4j30u00k0n0j.jpg",),fit: BoxFit.cover,),),),new SliverGrid(gridDelegate: new SliverGridDelegateWithMaxCrossAxisExtent(maxCrossAxisExtent: 200.0,mainAxisSpacing: 10.0,crossAxisSpacing: 10.0,childAspectRatio: 4.0,),delegate: new SliverChildBuilderDelegate((BuildContext context, int index) {returnnew Container(alignment: Alignment.center,color: Colors.teal[100 * (index % 9)],child: new Text('grid item $index'),);},childCount: 10,),),SliverFixedExtentList(itemExtent: 50.0,delegate: SliverChildBuilderDelegate((BuildContext context, int index) {returnnew Container(alignment: Alignment.center,color: Colors.lightBlue[100 * (index % 9)],child: new Text('list item $index'),);},childCount: 20),),],);}
}

监听滚动事件

对于滚动的视图,我们经常需要监听它的一些滚动事件,在监听到的时候去做对应的一些事情。

比如视图滚动到底部时,我们可能希望做上拉加载更多;

比如滚动到一定位置时显示一个回到顶部的按钮,点击回到顶部的按钮,回到顶部;

比如监听滚动什么时候开始,什么时候结束;

在Flutter中监听滚动相关的内容由两部分组成:ScrollController和ScrollNotification。

ScrollController

在Flutter中,Widget并不是最终渲染到屏幕上的元素(真正渲染的是RenderObject),因此通常这种监听事件以及相关的信息并不能直接从Widget中获取,而是必须通过对应的Widget的Controller来实现。

ListView、GridView的组件控制器是ScrollController,我们可以通过它来获取视图的滚动信息,并且可以调用里面的方法来更新视图的滚动位置。

另外,通常情况下,我们会根据滚动的位置来改变一些Widget的状态信息,所以ScrollController通常会和StatefulWidget一起来使用,并且会在其中控制它的初始化、监听、销毁等事件。

我们来做一个案例,当滚动到1000位置的时候,显示一个回到顶部的按钮:

  • jumpTo(double offset)animateTo(double offset,...):这两个方法用于跳转到指定的位置,它们不同之处在于,后者在跳转时会执行一个动画,而前者不会。
  • ScrollController间接继承自Listenable,我们可以根据ScrollController来监听滚动事件。
class MyHomePage extends StatefulWidget {@overrideState<StatefulWidget> createState() => MyHomePageState();
}class MyHomePageState extends State<MyHomePage> {ScrollController _controller;bool _isShowTop = false;@overridevoid initState() {// 初始化ScrollController_controller = ScrollController();// 监听滚动_controller.addListener(() {var tempSsShowTop = _controller.offset >= 1000;if (tempSsShowTop != _isShowTop) {setState(() {_isShowTop = tempSsShowTop;});}});super.initState();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("ListView展示"),),body: ListView.builder(itemCount: 100,itemExtent: 60,controller: _controller,itemBuilder: (BuildContext context, int index) {return ListTile(title: Text("item$index"));}),floatingActionButton: !_isShowTop ? null : FloatingActionButton(child: Icon(Icons.arrow_upward),onPressed: () {_controller.animateTo(0, duration: Duration(milliseconds: 1000), curve: Curves.ease);},),);}
}

NotificationListener

如果我们希望监听什么时候开始滚动,什么时候结束滚动,这个时候我们可以通过NotificationListener

  • NotificationListener是一个Widget,模板参数T是想监听的通知类型,如果省略,则所有类型通知都会被监听,如果指定特定类型,则只有该类型的通知会被监听。
  • NotificationListener需要一个onNotification回调函数,用于实现监听处理逻辑。
  • 该回调可以返回一个布尔值,代表是否阻止该事件继续向上冒泡,如果为true时,则冒泡终止,事件停止向上传播,如果不返回或者返回值为false 时,则冒泡继续。

案例: 列表滚动, 并且在中间显示滚动进度

class MyHomeNotificationDemo extends StatefulWidget {@overrideState<StatefulWidget> createState() => MyHomeNotificationDemoState();
}class MyHomeNotificationDemoState extends State<MyHomeNotificationDemo> {int _progress = 0;@overrideWidget build(BuildContext context) {return NotificationListener(onNotification: (ScrollNotification notification) {// 1.判断监听事件的类型if (notification is ScrollStartNotification) {print("开始滚动.....");} elseif (notification is ScrollUpdateNotification) {// 当前滚动的位置和总长度final currentPixel = notification.metrics.pixels;final totalPixel = notification.metrics.maxScrollExtent;double progress = currentPixel / totalPixel;setState(() {_progress = (progress * 100).toInt();});print("正在滚动:${notification.metrics.pixels} - ${notification.metrics.maxScrollExtent}");} elseif (notification is ScrollEndNotification) {print("结束滚动....");}returnfalse;},child: Stack(alignment: Alignment(.9, .9),children: <Widget>[ListView.builder(itemCount: 100,itemExtent: 60,itemBuilder: (BuildContext context, int index) {return ListTile(title: Text("item$index"));}),CircleAvatar(radius: 30,child: Text("$_progress%"),backgroundColor: Colors.black54,)],),);}
}

flutter异步处理和发网络请求

异步处理可以参考这个地址:

https://www.jianshu.com/p/c0e30769ea7e

flutter实现网络请求

在Flutter中常见的网络请求方式有三种:HttpClient、http库、dio库;

dio库

官方提供的HttpClient和http都可以正常的发送网络请求,但是对于现代的应用程序开发来说,我们通常要求的东西会更多:比如拦截器、取消请求、文件上传/下载、超时设置等等;

这个时候,我们可以使用一个在Flutter中非常流行的三方库:dio;

官网有对dio进行解释:

dio是一个强大的Dart Http请求库,支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时、自定义适配器等…

使用dio三方库必然也需要先在pubspec中依赖它:

  dio:^3.0.1

代码演练:

import'package:dio/dio.dart';void dioNetwork() async {// 1.创建Dio请求对象final dio = Dio();// 2.发送网络请求final response = await dio.get("http://123.207.32.32:8000/api/v1/recommend");// 3.打印请求结果if (response.statusCode == HttpStatus.ok) {print(response.data);} else {print("请求失败:${response.statusCode}");}
}

dio库的封装

http_config.dart

class HTTPConfig {staticconst baseURL = "https://httpbin.org";staticconst timeout = 5000;
}

http_request.dart

import'package:dio/dio.dart';
import'package:testflutter001/service/config.dart';class HttpRequest {staticfinal BaseOptions options = BaseOptions(baseUrl: HTTPConfig.baseURL, connectTimeout: HTTPConfig.timeout);staticfinal Dio dio = Dio(options);static Future<T> request<T>(String url,{String method = 'get', Map<String, dynamic> params, Interceptor inter}) async {// 1.请求的单独配置final options = Options(method: method);// 2.添加第一个拦截器Interceptor dInter = InterceptorsWrapper(onRequest: (RequestOptions options) {// 1.在进行任何网络请求的时候, 可以添加一个loading显示// 2.很多页面的访问必须要求携带Token,那么就可以在这里判断是有Token// 3.对参数进行一些处理,比如序列化处理等print("拦截了请求");return options;},onResponse: (Response response) {print("拦截了响应");return response;},onError: (DioError error) {print("拦截了错误");return error;});List<Interceptor> inters = [dInter];if (inter != null) {inters.add(inter);}dio.interceptors.addAll(inters);// 3.发送网络请求try {Response response = await dio.request<T>(url, queryParameters: params, options: options);return response.data;} on DioError catch(e) {return Future.error(e);}}
}

代码使用:

HttpRequest.request("https://httpbin.org/get", params: {"name": "why", 'age': 18}).then((res) {print(res);
});HttpRequest.request("https://httpbin.org/post",method: "post", params: {"name": "why", 'age': 18}).then((res) {print(res);
});

路由、导航和事件处理

组件封装

评分五星封装

// ignore_for_file: prefer_const_literals_to_create_immutables, prefer_const_constructors, use_key_in_widget_constructors, prefer_const_constructors_in_immutables
import 'dart:ffi';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class YJBStarRating extends StatefulWidget {final double? rating; //得分final double maxRating; //满分final int count; //星星个数final double size; //大小final Color unsetColor;final Color selectColor;YJBStarRating({@required this.rating,this.maxRating = 10,this.count = 5,this.size = 30,this.unsetColor = Colors.grey,this.selectColor = Colors.red});@overrideState<YJBStarRating> createState() => _YJBStarRatingState();
}
class _YJBStarRatingState extends State<YJBStarRating> {@overrideWidget build(BuildContext context) {return Stack(children: [Row(children: buildunsetStar(),),Row(children: buildSelectStar())]);}List<Widget> buildunsetStar() {return List.generate(widget.count, (index) {return Icon(Icons.star_border,color: widget.unsetColor,size: widget.size,);});}List<Widget> buildSelectStar() {List<Widget> starList = [];final markStar = widget.maxRating / widget.count; //每一个多少分final fullStar = (widget.rating! / markStar).floor(); //满的个数final double otherStar =((widget.rating! / markStar) - fullStar) * widget.size;//满的个数for (int i = 0; i < fullStar; i++) {starList.add(Icon(Icons.star,color: widget.selectColor,size: widget.size,));}//裁剪不满的starList.add(ClipRect(clipper: MyClip(otherStar),child: Icon(Icons.star,color: widget.selectColor,size: widget.size,)));return starList;}
}
class MyClip extends CustomClipper<Rect> {final double clipSize;MyClip(this.clipSize);@overrideRect getClip(Size size) {return Rect.fromLTWH(0, 0, clipSize, size.height);}@overridebool shouldReclip(MyClip oldClipper) {return false;}
}

虚线封装

// ignore_for_file: prefer_const_constructors, sized_box_for_whitespace
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class YJBDashed extends StatelessWidget {final Axis axis; //方向final int count; //线点final double dashHeight; //高度final double dashWidth; //宽度final Color color; //颜色// ignore: use_key_in_widget_constructorsYJBDashed({@required this.axis = Axis.horizontal,this.count = 20,this.dashHeight = 2,this.color = Colors.grey,this.dashWidth = 4});@overrideWidget build(BuildContext context) {// ignore: avoid_unnecessary_containersreturn Flex(direction: axis,mainAxisAlignment:MainAxisAlignment.spaceBetween,children: List.generate(count, (index) {return SizedBox(width: dashWidth,height: dashHeight,child: DecoratedBox(decoration: BoxDecoration(color: color),),);}));}
}

底部导航栏

底部导航:和body同级的一个属性:bottomNavigationBar

路由切换:body属性使用IndexedStack

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import './home/home.dart';
import './mine/mine.dart';class YJBMainPage extends StatefulWidget {const YJBMainPage({Key? key}) : super(key: key);@overrideState<YJBMainPage> createState() => _YJBMainPageState();
}class _YJBMainPageState extends State<YJBMainPage> {int tabIndex = 0;@overrideWidget build(BuildContext context) {return Scaffold(body: IndexedStack(index: tabIndex,// ignore: prefer_const_literals_to_create_immutableschildren: [YJBHmoe(), YJBMine()],),bottomNavigationBar: BottomNavigationBar(currentIndex: tabIndex,onTap: (value) {// ignore: unnecessary_thissetState(() {this.tabIndex = value;});},// ignore: prefer_const_literals_to_create_immutablesitems: [BottomBarItem(Icons.home,Colors.red,'HOME'),BottomBarItem(Icons.person, Colors.red, 'MINE'),]));}// ignore: non_constant_identifier_namesBottomNavigationBarItem BottomBarItem(iconType,color,text) {return BottomNavigationBarItem(icon: Icon(iconType),activeIcon: Icon(iconType,color: color,),label: text);}
}

DartFlutter基础学习日记相关推荐

  1. C语言零基础学习日记

    scanf是C语言提供的 scanf_s是非标准的C语言代码,是由VS编译器提供的,不具有可移植性/跨平台性 #define _CRT_SECURE_NO_WARNINGS 1 Notepad++进行 ...

  2. JAVA基础学习日记-----持续更新

    第一节,计算 1. System.out.println() // 输出带有回车System.out.print() // 不带有回车 2. +号可以连接两个字符串 3. 有几个in.nextInt( ...

  3. Java学习日记1——基础认知

    Java学习日记1--基础认知 学习Java阶段,如果发现不正确的描述,还请指正! 首先附上Java相关下载链接和配置教程链接 Java相关软件工具下载地址:官方下载 Java环境配置(win10配置 ...

  4. Python学习日记-day1基础篇 字符 输出 注释

    Python学习日记-day1基础篇 字符 输出 注释 by北栀一刺 # -*- coding: utf-8 -*- """ Spyder EditorThis is a ...

  5. 210学习日记(18)_ARM基础知识

    210学习日记(18) --ARM基础知识 注意: 以下大部分类容都来自网上现成的(直接拷贝过来的,然后经整理)!!!! 问1:ARM处理器工作模式有几种?各种工作模式下分别有什么特点? 答1:ARM ...

  6. 10.16 my学习日记 (XPath的基础语法,lxml库的应用)

    10.16 my学习日记(XPath的基础语法,lxml库的应用) XPath的基础语法 XPath查找标签 XPath谓语 lxml库在爬虫中的应用 etree库etree_Element对象 使用 ...

  7. 托攻击检测基础知识-----WZW托攻击学习日记(一)

    ( 注:开始写这个学习日记的目的,有三个:一,检测自己学习的质量:二,与人交流,如果学习中出现什么错误欢迎指正:三,提供即将或以后想要学习相关知识的同志一丢丢帮助,也给自己的复习提供资料. 再注:本人 ...

  8. 深度学习日记 2 - 概率论与信息论基础

    深度学习日记 2 - 概率论与信息论基础: 1.随机变量(random variable):是可以随机地取不同值的变量.我们通常用打印机 体的小写字母来表示随机变量本身,而用脚本字体中的小写字母来表示 ...

  9. 微信开发学习日记(一):快速阅读5本书,掌握整体背景

    2015年1月开始学习微信开发. 已经有多年开发经验了,从网上文章来看,微信开发主要是接口,然后是业务逻辑,不是很难.所以,我比较强调学习效率.一天学一点,是不能满足我的快速学习欲望的.       ...

最新文章

  1. java集合总结_Java中集合总结
  2. 你还是只会用数组吗?不懂Java集合,还想找工作?(集合概述)
  3. vuex 基本入门和使用(二)
  4. MySQL的SQL Profiler性能分析器
  5. Windows Server 2003 R2中的“分布式文件系统”案例应用
  6. 小米回应将放弃“MI”字logo:不存在停止使用
  7. bzoj1878: [SDOI2009]HH的项链
  8. springcloud(二):注册中心Eureka
  9. char怎么比较_为什么阿里巴巴Java开发手册中强制要求整型包装类对象值用 equals 方法比较?...
  10. 考清华计算机研究生数学看什么,一位考上清华计算机研究生的悲壮历程(数学考了满分).doc...
  11. Java Script 基本知识点
  12. 没有鼠标怎么打开笔记本的触摸板
  13. 如何测试网站服务器大小,如何测试服务器宽带网络大小
  14. Linux内核启动过程和Bootloader
  15. activiti学习之排他网关
  16. php中的or die,php or die() 语句,exit()
  17. java web全栈_web全栈java开发哪个前景好
  18. python学习笔记之word文档提取
  19. 支持html邮件,HTML邮件兼容问题与解决方案
  20. 三方演化博弈复制动态方程matlab仿真——matlab2016a版本

热门文章

  1. WRF-Hydro模式
  2. 【stm32】stm32cubeIDE在freeRTOS无法printf float 浮点数
  3. sofa框架简单搭建
  4. html语言br怎么用,HTML br 标签如何使用
  5. Android 项目工程目录简单介绍
  6. sdm636-mtp驱动_NodeMCU简单驱动程序模型(SDM)展示:动态用户界面
  7. Java 8 腰斩!Java 17 暴涨 430%!!(文末福利)
  8. typeScript interface和type区别
  9. ST、SC、FC光纤接头区别
  10. STM32_TIM输出PWM波形