小部件
StatefulWidget 和 StatelessWidget
StatefulWidget具有可变状态的窗口部件,也就是你在使用应用的时候就可以随时变化,比如我们常见的进度条StatelessWidget不可变状态窗口部件,也就是你在使用时不可以改变,比如固定的文字
init 先初始父类 再初始子类dispose 先销毁子类 再销毁父类
主题自定义配置
theme: ThemeData(
primaryIconTheme: IconThemeData(
color: Colors.white
),
brightness: Brightness.light,
primaryColor: Color.fromARGB(255, 30, 46, 69),
accentColor: Colors.blue
),
primaryColor主要部分的背景颜色(工具栏,标签栏等)primaryIconTheme与主要颜色形成颜色鲜明对比的图标主题accentColor小部件的前景色(旋钮、文本、过渡滚动边缘效果等)
去掉 DeBug 图标
在 return 的 MaterialApp()里加入 debugShowCheckedModeBanner: false
body: Center(
child:Container(
child: Text(
'Hello World!AAAAAA',
textAlign: TextAlign.center,
maxLines: 1,
overflow:TextOverflow.ellipsis,
style: TextStyle(
fontSize: 25.0,
color: Color.fromARGB(255, 255, 150, 150),
decoration: TextDecoration.underline,
decorationStyle: TextDecorationStyle.solid,
)
),
alignment: Alignment.center,
width: 500.0,
height: 400.0,
// color: Colors.lightBlue,
padding: const EdgeInsets.fromLTRB(50.0,10.0,50.0,10.0),
margin: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors:[Colors.lightBlue,Colors.greenAccent,Colors.purple]
),
border: Border.all(width:2.0,color: Colors.red)
),
)
),
Text Widget 文字控件
TextAligin属性,文本对齐方式,center/left/right/start/endmaxLines属性,设置最多显示的行数overflow属性,设置文本溢出时的处理,clip直接切断,elipsis在后面显示省略号,fade溢出部分进行一个渐变消失的效果style属性
Text.rich(
TextSpan(
text: 'AAAA',
children: [
TextSpan(text:'1'),
TextSpan(text:'2'),
]
)
)
Container 容器控件
alignment属性,对容器内child的对齐方式,bottomCenter/bottomLeft/bottomRight/center/centerLeft/centerRight/topLeft/topCenter/topRightwidth属性,宽height属性,高color属性,容器颜色属性padding属性,内边距
const EdgeInsets.all(10.0)
const EdgeInsets.fromLTRB(10.0,10.0,0,0)
const EdgeInsets.only(bottom:10.0)
margin属性,外边距deciration属性,容器的修饰器,主要功能是设置背景和边框
Image 图片控件
Image.asset(
'images/car_red.jpg',
width: 600.0,
height: 240.0,
fit: BoxFit.cover,
color: Colors.greenAccent,
colorBlendMode: BlendMode.darken,
repeat: ImageRepeat.repeat,
),
Image.asset加载资源图片,加载项目资源目录中的图片,加入图片后会增大打包的包体体积,用的相对路径Image.file加载本地图片,加载本地文件中的图片,绝对路径,跟包体无关Image.network网络资源图片,需要加入一段http://xxx的网络路径地址Image.memory加载Uint8List资源图片 如果想配置项目资源文件,就需要使用pubspec.yaml文件,需要把资源文件在这里声明
fit属性
可以控制图片的拉伸和挤压,这些都是根据图片的父级容器来的
BoxFit.fill全图显示,图片会被拉伸,并充满父容器BoxFit.contain全图显示,显示原比例,可能会有空隙BoxFit.fitWidth宽度充满(横向充满),显示可能拉伸,可能裁切BoxFit.fitHeight高度充满(竖向充满),显示可能拉伸,可能裁切BoxFit.scaleDown效果和contain差不多,但是此属性不允许显示超过源图片大小,可小不可大
colorBlendMode
图片混合模式,和color属性配合使用,能让图片改变颜色
repeat图片重复
imageRepeat.repeat横向和纵向都进行重复imageRepeat.repeatX横向重复imageRepeat.repeatY纵向重复
ListView列表控件
class MyList extends StatelessWidget{
@override
Widget build(BuildContext context){
return new ListView(
children: <Widget>[
Image.network('https://xxx.png'),
new ListTile(
leading: new Icon(Icons.accessible_forward),
title: new Text('accessible_forward')
),
new ListTile(
leading: new Icon(Icons.adb),
title: new Text('adb')
),
],
);
}
}
使用ListView,在他内部的children中,使用了widget数组,因为是一个列表,所以他接受一个数组,使用ListTile控件,在控件中放置图标和文字
scrollDirection:Axis.vertical滚动方向reverse是否反向显示数据addAutomaticKeepAlives自动保存视图缓存addRepaintBoundaries添加重绘边界listView.builder()
横向列表
Center(
child: Container(
height: 200.0,
child: new ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
new Container(
width: 180.0,
color: Colors.redAccent,
),
new Container(
width: 180.0,
color: Colors.orangeAccent,
),
new Container(
width: 180.0,
color: Colors.yellow,
),
new Container(
width: 180.0,
color: Colors.greenAccent,
),
],
)
),
),
ListView组件的scrollDirection属性
Axis.horizontal水平方向滚动Axis.vertical垂直方向滚动
动态列表
List是Dart的集合类型之一
var myList = List() // 非固定长度的声明
var myList = List(2) // 固定长度的声明
var myList = List<String>() // 固定类型的声明
var myList = [1,2,3] // 对List直接赋值
List中的generate方法进行生产List里的元素
import 'package:flutter/material.dart';
void main () => runApp(MyApp(
items: new List<String>.generate(1000, (i)=> "Item $i")
));
class MyApp extends StatelessWidget{
final List<String> items;
MyApp({Key key, @required this.items}):super(key:key);
@override
Widget build(BuildContext context ){
return MaterialApp(
title:'ListView widget',
home:Scaffold(
body:new ListView.builder(
itemCount:items.length,
itemBuilder:(context,index){
return new ListTile(
title:new Text('${items[index]}'),
);
}
)
),
);
}
}
在main函数的runApp中调用了myApp类,再使用类的使用传递了一个items参数,并使用generate生成器对items复制,generate方法传递两个参数,第一个参数是生成的个数,第二个是方法 已经传递了参数,那myApp这个类是需要接收的,在构造函数里除了Key,我们增加了一个必传参数,这里的@required意思就是必传,:super如果父类没有无名无参数的默认构造函数,则子类必须手动调用一个父类构造函数ListView.builder() 调用动态列表进行生成
GridView 网格列表控件
body: GridView.count(
physics: NeverScrollableScrollPhysics(),
padding: const EdgeInsets.all(20.0),
crossAxisSpacing: 10.0,
crossAxisCount: 3,
childAspectRatio: 0.7,
mainAxisSpacing: 2.0,
children: <Widget>[
new Image.network('https://xxx',fit: BoxFit.cover),
new Image.network('https://xxx',fit: BoxFit.cover),
],
),
padding表示内边距crossAxisSpacing网格间的间距crossAxisCount网格的列数childAspectRatio宽高比mainAxisSpacing主轴间距
水平布局 Row
Row 空间可以分为灵活排列和非灵活排列两种
body: new Row(
children: <Widget>[
new RaisedButton(
onPressed: (){},
color: Colors.orangeAccent,
child: new Text('button')
),
Expanded(
child: new RaisedButton(
onPressed: (){},
color: Colors.redAccent,
child: new Text('buy')
),
),
new RaisedButton(
onPressed: (){},
color: Colors.orangeAccent,
child: new Text('button')
)
],
),
当都使用非灵活布局时,Row子元素不足会有空隙,子元素超出会有警告。如果想实现充满一行的效果就得使用到灵活水平布局,给包上Expanded就可以了
垂直布局 Column
body: Center(
child:Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text('aaa'),
Expanded(child: new Text('bbbbbb')),
new Text('aaa'),
],
),
)
main轴,用column垂直则是主轴,用row水平则是主轴cross轴,副轴
层叠布局 Stack
new Stack(
alignment: const FractionalOffset(0.5, 0.8),
children: <Widget>[
new CircleAvatar(
backgroundImage: new NetworkImage('https://xx'),
radius: 100.0,
),
new Container(
decoration: new BoxDecoration(
color: Colors.blueAccent
),
padding: const EdgeInsets.all(5.0),
child: new Text('电影TOP100',),
)
],
);
alignment属性是控制层叠位置的,建议在两个内容进行层叠时使用,它有两个值X轴距离和Y轴距离,值是从0到1的,都是从上层容器的左上角开始算起circleAvatar组件经常作头像的,组件里radius的值可以设置图片的弧度
body: Center(
child: new Stack(
children: <Widget>[
new CircleAvatar(
backgroundImage: NetworkImage('https://xxx'),
radius: 100.0,
),
new Positioned(
top:10.0,
left: 10.0,
child: new Text('AAA'),
),
new Positioned(
bottom: 10.0,
right: 10.0,
child: new Text('BBB'),
)
],
),
)
Positioned 组件
bottom/left/top/right距离层叠组件的距离width层叠定位组件的宽度height层叠定位组件的高度
Card 卡片组件
这种布局类似ViewList但是列表会以物理卡片的形态进行展示
new Card(
child: Column(
children: <Widget>[
ListTile(
title: new Text(
'aaaaa',
style: TextStyle(
fontWeight: FontWeight.w400
)
),
subtitle: new Text('bbbbb'),
leading: new Icon(Icons.access_alarm,color: Colors.blueAccent,),
),
new Divider(), // 水平线
...
],
)
);
流式布局
import 'package:flutter/material.dart';
class MyHomePage extends StatefulWidget {
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<Widget> imgList;
@override
void initState() {
imgList = List<Widget>()..add(buildAddButton());
super.initState();
}
@override
Widget build(BuildContext context) {
final width = MediaQuery.of(context).size.width;
final height = MediaQuery.of(context).size.height;
return Scaffold(
appBar: AppBar(
title: Text('Wrap')
),
body: Center(
child:Opacity(
opacity: 0.8,
child: Container(
width: width,
height: height /2,
color: Colors.grey,
child: Wrap(
children: imgList,
spacing: 26.0,
),
),
)
),
);
}
Widget buildAddButton(){
return GestureDetector(
onTap: (){
if(imgList.length < 9){
setState(() {
imgList.insert(imgList.length-1, buildPhoto());
});
}
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child:Container(
width: 80.0,
height: 80.0,
color: Colors.black54,
child: Icon(Icons.add),
)
),
);
}
Widget buildPhoto(){
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: 80.0,
height: 80.0,
color: Colors.amber,
child: Center(
child: Text('Photo')
),
),
);
}
}
GestureDetector
手势操作,没有任何的显示功能,用来触发事件的。如Padding、Container、Center这些组件是没有触发事件的,可在在它们外层增加一个GestureDetector,让它们有触发事件
ExpansionTile 控件
可以展开闭合的控件
title闭合时显示的标题leading标题左侧图标backgroundColor展开时背景颜色children子元素initiallyExpanded初始状态是否为展开
ExpansionTile(
title: Text('AAA Aa'),
leading: Icon(Icons.ac_unit),
backgroundColor: Colors.white12,
children: <Widget>[
ListTile(
title: Text('list title'),
subtitle: Text('subtitle'),
)
],
),
展开闭合列表 ExpansionPanelList可以实现展开闭合的列表功能,这个列表必须放在可滑动组件中使用expansionCallback点击和交互的回调事件,第一个参数是触发动作的索引,第二个是布尔类型的触发值
import 'package:flutter/material.dart';
class MyExpamsionPanelList extends StatefulWidget {
_MyExpamsionPanelListState createState() => _MyExpamsionPanelListState();
}
class _MyExpamsionPanelListState extends State<MyExpamsionPanelList> {
var currentPanelIndex = -1;
List<int> mList;
List<ExpansionState> expansionStateList;
_MyExpamsionPanelListState(){
mList = new List();
expansionStateList = new List();
for(int i=0;i<10;i++){
mList.add(i);
expansionStateList.add(ExpansionState(i,false));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('expanison panel list'),
),
body: SingleChildScrollView(
child: ExpansionPanelList(
expansionCallback: (int index, isExpand){
setState(() {
expansionStateList.forEach((item){
if(item.index == index){
item.isOpen = !isExpand;
}
});
});
},
children: mList.map((index){
return ExpansionPanel(
headerBuilder: (context, isExpanded){
return ListTile(
title: Text('This is No.$index'),
);
},
body: ListTile(
title: Text('expansion No.$index'),
),
isExpanded: expansionStateList[index].isOpen
);
}).toList(),
),
)
);
}
}
class ExpansionState{
var isOpen;
var index;
ExpansionState(this.index,this.isOpen);
}
贝塞尔曲线切割
clipPath控件可以把其内部的子控件切割 clipper属性,切割的路径,要和CustomClipper对象配合使用
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
ClipPath(
clipper: BottomCliperTest(),
child: Container(
color: Colors.deepPurpleAccent,
height: 200.0,
),
)
],
),
);
}
}
class BottomCliperTest extends CustomClipper<Path> {
@override
Path getClip(Size size){
var path = Path();
path.lineTo(0, 0);
path.lineTo(0, size.height - 50);
var firstControlPoint = Offset(size.width/2, size.height);
var firstEndPoint = Offset(size.width,size.height-50);
path.quadraticBezierTo(firstControlPoint.dx, firstControlPoint.dy, firstEndPoint.dx, firstEndPoint.dy);
path.lineTo(size.width,0);
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldCliper){
return false;
}
}
波浪式贝塞尔曲线
var path = Path();
path.lineTo(0, size.height-40);
var firstControlPoint = Offset(size.width/4, size.height);
var firstEndPoint = Offset(size.width/2.25,size.height - 30);
path.quadraticBezierTo(firstControlPoint.dx, firstControlPoint.dy, firstEndPoint.dx, firstEndPoint.dy);
var secondControlPoint = Offset(size.width/4 * 3, size.height-80);
var secondEndPoint = Offset(size.width,size.height - 40);
path.quadraticBezierTo(secondControlPoint.dx, secondControlPoint.dy, secondEndPoint.dx, secondEndPoint.dy);
path.lineTo(size.width, 0);
return path;
inkWell
点击效果墨水池
InkWell(
onTap:(){},
child:
)
Offstage
- 当
offstage为true时,当前控件不会被绘制在屏幕上,不会响应点击事件,不会占用空间 - 当
offstage为false时,当前控件跟平常用的空间一样渲染绘制
Offstage(
offstage: offstage,
child: Container(color: Colors.blue, height: 100.0),
)
OverflowBox
允许child超出parent的范围显示,最大尺寸大于child的时候,child可以完整显示,小于child的时候以最大尺寸为基准。
OverflowBox(
maxWidth: MediaQuery.of(context).size.width,
maxHeight: MediaQuery.of(context).size.height,
alignment: Alignment.topCenter,
child: Container(
width: 1200.0,
),
),
SizedBox
设置具体尺寸
SizedBox(
width: 200.0,
height: 200.0,
child: Container(
color: Colors.red,
width: 100.0,
height: 300.0,
),
),
LimitedBox
对最大宽高进行限制
LimitedBox(
maxWidth: 150.0,
child: Container(
color: Colors.blue,
width: 250.0,
),
),
长宽比控件
AspectRatio(
aspectRatio: 4/3,
child: Container(
color: Colors.green,
),
),
Drawer 抽屉
在Scaffold中加入drawer项可以使用抽屉
Drawer(
child: ListView(
children: <Widget>[
UserAccountsDrawerHeader(
accountName: null,
accountEmail: null,
currentAccountPicture: null,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: AssetImage('images/bg_account_switcher.png')
)
),
),
ListTile(
title: Text(
'首页',
style: TextStyle(
color: Colors.blue[600],
fontSize: ScreenUtil().setSp(32)
),
),
leading: ImageIcon(
AssetImage('images/quantum_ic_home_black_24.png'),
color: Colors.blue[600],
),
onTap: () {},
),
],
),
延时
Future.delayed(Duration.zero,()=>_futrue());
controller.animateTo(
controller.position.maxScrollExtent,
duration: new Duration(milliseconds: 150),
curve: Curves.easeIn,
);
TextField
TextEditingController controller = TextEditingController();
Widget buildTextField(TextEditingController controller) {
return TextField(
//控制正在编辑的文本。通过其可以拿到输入的文本值
//获取方式 String value=controller.text
controller: controller,
//给TextField设置装饰(形状等)
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
borderSide: BorderSide(color: Colors.transparent)
),
//输入内容距离上下左右的距离 ,可通过这个属性来控制 TextField的高度
contentPadding: EdgeInsets.all(10.0),
fillColor: Colors.white, filled: true,
// labelText: 'Hello',
// 以下属性可用来去除TextField的边框
disabledBorder: InputBorder.none,
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
),
//键盘类型
keyboardType: TextInputType.text,
//控制键盘的功能键 指enter键,比如此处设置为next时,enter键
//显示的文字内容为 下一步
textInputAction: TextInputAction.next,
//键盘大小写的显示 Only supports text keyboards 但是好像不起作用?
//characters 默认为每个字符使用大写键盘
//sentence 默认为每个句子的第一个字母使用大写键盘
//word 默认为每个单词的第一个字母使用大写键盘。
//none 默认使用小写
textCapitalization: TextCapitalization.words,
//是否是密码
obscureText: false,
//文本对齐方式(即光标初始位置)
textAlign: TextAlign.center,
//输入文本的样式
style: TextStyle(fontSize: 30.0, color: Colors.blue),
//最大行数
maxLines: 1,
//最大长度,设置此项会让TextField右下角有一个输入数量的统计字符串
//这种情况一般是不符合我们设计的要求的,但是有需要限制其输入的位数
// 这时候我们可以使用下方的属性来限制
maxLength: 9,
// 跟最大长度限制对应,如果此属性设置为false,当输入超过最大长度
// 限制时,依然会显示输入的内容,为true不会(默认为 true)
maxLengthEnforced: false,
// 限制输入的 最大长度 TextField右下角没有输入数量的统计字符串
// inputFormatters: [LengthLimitingTextInputFormatter(11)],
//允许的输入格式 下方的模式指只允许输入数字
// inputFormatters: [WhitelistingTextInputFormatter.digitsOnly],
//控制此小部件是否具有键盘焦点。
focusNode: FocusNode(),
//是否自动更正
autocorrect:false,
//是否自动获得焦点
autofocus: false,
//是否禁用textfield:如果为false, textfield将被“禁用”
enabled: true,
//光标颜色
cursorColor: Colors.red,
//光标宽度
cursorWidth: 5.0,
//光标圆角弧度
cursorRadius: Radius.circular(5.0),
//内容改变的回调
onChanged: (text) {
print('change $text');
},
//内容提交(按回车)的回调
onSubmitted: (text) {
print('submit $text');
},
//按回车时调用
onEditingComplete: (){
print('onEditingComplete');
},
);
}
