DartFlutter基础学习日记
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有两种:StatelessWidget和StatefulWidget两种
- 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基础学习日记相关推荐
- C语言零基础学习日记
scanf是C语言提供的 scanf_s是非标准的C语言代码,是由VS编译器提供的,不具有可移植性/跨平台性 #define _CRT_SECURE_NO_WARNINGS 1 Notepad++进行 ...
- JAVA基础学习日记-----持续更新
第一节,计算 1. System.out.println() // 输出带有回车System.out.print() // 不带有回车 2. +号可以连接两个字符串 3. 有几个in.nextInt( ...
- Java学习日记1——基础认知
Java学习日记1--基础认知 学习Java阶段,如果发现不正确的描述,还请指正! 首先附上Java相关下载链接和配置教程链接 Java相关软件工具下载地址:官方下载 Java环境配置(win10配置 ...
- Python学习日记-day1基础篇 字符 输出 注释
Python学习日记-day1基础篇 字符 输出 注释 by北栀一刺 # -*- coding: utf-8 -*- """ Spyder EditorThis is a ...
- 210学习日记(18)_ARM基础知识
210学习日记(18) --ARM基础知识 注意: 以下大部分类容都来自网上现成的(直接拷贝过来的,然后经整理)!!!! 问1:ARM处理器工作模式有几种?各种工作模式下分别有什么特点? 答1:ARM ...
- 10.16 my学习日记 (XPath的基础语法,lxml库的应用)
10.16 my学习日记(XPath的基础语法,lxml库的应用) XPath的基础语法 XPath查找标签 XPath谓语 lxml库在爬虫中的应用 etree库etree_Element对象 使用 ...
- 托攻击检测基础知识-----WZW托攻击学习日记(一)
( 注:开始写这个学习日记的目的,有三个:一,检测自己学习的质量:二,与人交流,如果学习中出现什么错误欢迎指正:三,提供即将或以后想要学习相关知识的同志一丢丢帮助,也给自己的复习提供资料. 再注:本人 ...
- 深度学习日记 2 - 概率论与信息论基础
深度学习日记 2 - 概率论与信息论基础: 1.随机变量(random variable):是可以随机地取不同值的变量.我们通常用打印机 体的小写字母来表示随机变量本身,而用脚本字体中的小写字母来表示 ...
- 微信开发学习日记(一):快速阅读5本书,掌握整体背景
2015年1月开始学习微信开发. 已经有多年开发经验了,从网上文章来看,微信开发主要是接口,然后是业务逻辑,不是很难.所以,我比较强调学习效率.一天学一点,是不能满足我的快速学习欲望的. ...
最新文章
- java集合总结_Java中集合总结
- 你还是只会用数组吗?不懂Java集合,还想找工作?(集合概述)
- vuex 基本入门和使用(二)
- MySQL的SQL Profiler性能分析器
- Windows Server 2003 R2中的“分布式文件系统”案例应用
- 小米回应将放弃“MI”字logo:不存在停止使用
- bzoj1878: [SDOI2009]HH的项链
- springcloud(二):注册中心Eureka
- char怎么比较_为什么阿里巴巴Java开发手册中强制要求整型包装类对象值用 equals 方法比较?...
- 考清华计算机研究生数学看什么,一位考上清华计算机研究生的悲壮历程(数学考了满分).doc...
- Java Script 基本知识点
- 没有鼠标怎么打开笔记本的触摸板
- 如何测试网站服务器大小,如何测试服务器宽带网络大小
- Linux内核启动过程和Bootloader
- activiti学习之排他网关
- php中的or die,php or die() 语句,exit()
- java web全栈_web全栈java开发哪个前景好
- python学习笔记之word文档提取
- 支持html邮件,HTML邮件兼容问题与解决方案
- 三方演化博弈复制动态方程matlab仿真——matlab2016a版本