StatefulWidget表示为:带状态会变化的组件
StatelessWidget表示为:无状态不变化的组件
Hello World
敲一个简单的Hello World界面:
import 'package:flutter/material.dart';
void main()=>runApp(MyApp());
class MyApp extends StatelessWidget
{
@override
Widget build(BuildContext context)
{
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('Hello World'),
),
body: Center(
child: Text('Hello World!'),
),
),
);
}
}上述代码中,
MyApp是我们自定义的一个类, 继承StatelessWidget代表它是静态组件, 这个静态组件类内部已经有build(BuildContext context)这个方法, 需要重写, 所以用到了@override这种dart写法。
import引入到的material.dart是谷歌研发的材质样式包。
MatrialApp里的title属性: App的名称
Scaffold组件出现在MaterialApp组件中的home区域, 他代表一个脚手架。
appBar属性: 标题栏, 用到了AppBar组件, 组件中包含一个title属性。
body: 标题栏下方的身体区域, 后面的Center组件代表四方位居中组件,再在里面写代码接着填充东西。
文本组件Text
在flutter当中, 一切都是组件, TextWidget就是基本组件效果之一。
为了学习TextWidget, 我们在child: Text挂件里面接着填充更多的内容, 以展示Text内部调用style等众多方法的效果。
以下是案例:
import 'package:flutter/material.dart';
void main()=>runApp(MyApp());
class MyApp extends StatelessWidget
{
@override
Widget build(BuildContext context)
{
return MaterialApp(
title: 'TextWidget展示效果',
home: Scaffold(
appBar: AppBar(
title: Text('我是文本挂件的标题'),
),
body: Center(
child: Text(
'我是文本挂件的内容,内容包含一大堆如此这般,内容包含一大堆如此这般,内容包含一大堆如此这般,内容包含一大堆如此这般,内容包含一大堆如此这般,内容包含一大堆如此这般',
textAlign: TextAlign.left,
maxLines: 2,
overflow: TextOverflow.fade,
style: TextStyle(
fontSize: 25.0,
color: Color.fromARGB(255, 255, 150, 150),
decoration: TextDecoration.underline,
decorationStyle: TextDecorationStyle.solid,
),
),
),
),
);
}
}上述代码涉及到了文本对齐, 最大行数, 溢出处理和细节样式控制这几个组件方法。
其中:
fontSize属性表示文字大小,
maxtLines属性代表最大行数,
overflow属性代表文字溢出处理方式, 用到TextOverflow对象,
fontSize属性对应的值是float小数点精确1位, 否则填写其他将导致报错。
color属性当中, Color对象里的fromARGB, A代表透明度。
docoration属性是文字修饰类型, 一般都是下划线, 用到了TextDecoration对象
decorationStyle属性是修饰的样式, 这里用实线, 用到了TextDecorationStyle对象里的solid
层组件Container
相当于html标签p中的div标签,
代码案例:
import 'package:flutter/material.dart';
void main()=>runApp(MyApp());
class MyApp extends StatelessWidget
{
@override
Widget build(BuildContext context)
{
return MaterialApp(
title: 'TextWidget展示效果',
home: Scaffold(
appBar: AppBar(
title: Text('我是文本挂件的标题'),
),
body: Center(
child: Container(
child: Text(
'我是文本挂件的内容,内容包含一大堆如此这般,内容包含一大堆如此这般,内容包含一大堆如此这般,内容包含一大堆如此这般,内容包含一大堆如此这般,内容包含一大堆如此这般',
textAlign: TextAlign.left,
maxLines: 2,
overflow: TextOverflow.fade,
style: TextStyle(
fontSize: 25.0,
color: Color.fromARGB(255, 255, 150, 150),
decoration: TextDecoration.underline,
decorationStyle: TextDecorationStyle.solid,
),
),
alignment: Alignment.center,
width:400.0,
height:300.0,
color: Colors.lightBlue,
padding: const EdgeInsets.fromLTRB(10.0,60.0,40.0,0.0),
margin: const EdgeInsets.all(20.0),
),
),
),
);
}
}上述代码中, 出现了alignment, width, height, color这几个属性, 其中alignment用到了Alignment组件。
padding属性和css一样, 是child子模块和文本组件之间存在的内边距,
margin属性是child子模块和body组件之间存在的外边距
decoration属性(定义如何修饰)
这个decoration和css当中的text-decoration不一样, 是修饰这里的Container盒子模型的, 因为书写起来比css复杂, 所以单独拉出来记录一下。
用到decoration的语句就不要用color属性来定义盒子颜色了, 把color属性注释掉。
代码案例如下:
import 'package:flutter/material.dart';
void main()=>runApp(MyApp());
class MyApp extends StatelessWidget
{
@override
Widget build(BuildContext context)
{
return MaterialApp(
title: 'TextWidget展示效果',
home: Scaffold(
appBar: AppBar(
title: Text('我是文本挂件的标题'),
),
body: Center(
child: Container(
child: Text(
'我是文本挂件的内容,内容包含一大堆如此这般,内容包含一大堆如此这般,内容包含一大堆如此这般,内容包含一大堆如此这般,内容包含一大堆如此这般,内容包含一大堆如此这般',
textAlign: TextAlign.left,
maxLines: 2,
overflow: TextOverflow.fade,
style: TextStyle(
fontSize: 25.0,
color: Color.fromARGB(255, 255, 150, 150),
decoration: TextDecoration.underline,
decorationStyle: TextDecorationStyle.solid,
),
),
alignment: Alignment.topLeft,
width:400.0,
height:300.0,
// color: Colors.lightBlue,
padding: const EdgeInsets.fromLTRB(20.0,60.0,40.0,0.0),
margin: const EdgeInsets.all(20.0),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [
Colors.red,
Colors.greenAccent,
Colors.tealAccent
]
)
),
),
),
),
);
}
}上述代码当中:
gradient属性, 定义渐变的意思
LinearGradient组件, 是线性渐变组件的意思。
colors属性后面多了个s复数, 里面传入数组, 再用Colors组件挨个代入元素
图片组件 Image
Image组件可以通过以下方式来加载:
Image.asset 本地加载, 会导致安装包过大,
Image.network 常用的通过网络读图,
Image.file 本地图库加载, 加载图库地址xxx, 用户拍完照, 把图片读进来,
Image.memory 透过内存加载, 不常用
以下是代码案例:
import 'package:flutter/material.dart';
void main()=>runApp(MyApp());
class MyApp extends StatelessWidget
{
@override
Widget build(BuildContext context)
{
return MaterialApp(
title: 'TextWidget展示效果',
home: Scaffold(
appBar: AppBar(
title: Text('我是文本挂件的标题'),
),
body: Center(
child: Container(
child: Image.network(
'http://www.wkwkk.com/filepool/cover/cover/1804/180415200453_6863.jpg',
scale: 2.5,
color: Colors.greenAccent,
colorBlendMode: BlendMode.colorBurn,
fit: BoxFit.cover,
// repeat: ImageRepeat.repeatY,
),
color: Colors.lightBlue,
width: 400.0,
height: 300.0,
),
),
),
);
}
}上述代码当中, 在图片组件里涉及到了以下属性:
scale属性, 伸缩比例, 这里要注意, 相对于传统的css写法, scale属性指定的值越大, 图片反而会越小。
repeat属性, 和css一样, 图片重复方式, 如果写到Container组件里, 是要报错的, 它是图片组件里的属性
fit属性, 填充方式, 此时scale属性指定的值失效, 使用BoxFit组件, 也就是盒子模型内填充组件的意思, 一般用到的是BoxFit.cover, 这样通过不改变宽高比例的方式把图片刚好覆盖到图片上层的Container容器里, 如果是BoxFit.contain, 则刚刚接触到最大宽或者最大高的边界, 图片就不会膨胀下去。
colorBlendMode属性, 这个blend顺便查了一下字典, 有交融混合调配的意思, 那设定这个属性的大概目的也就明白了, 指定了color属性之后, colorBlendMode属性的值用到了BlendModel组件, 也就是混合模式, 目前1.0版本有那么十多种可供选择,这里也说不清楚,自己上IDE上调整来看吧。
ListView和ListTile组件
这两个组件通常成对出现, ListView组件相当于html5概念中的ul标签, ListTile组件相当于html5概念中的li标签。查字典时看到单词tile翻译成砖、瓦片,此时代码工作者就是搬这个砖的。
代码案例如下:
import 'package:flutter/material.dart';
void main()=>runApp(MyApp());
class MyApp extends StatelessWidget
{
@override
Widget build(BuildContext context)
{
return MaterialApp(
title: 'flutter app test',
home: Scaffold(
appBar: AppBar(
title: Text('ListView'),
),
body: ListView(
children: <Widget>[
ListTile(
leading: Icon(Icons.person),
title: Text('人物1'),
),
ListTile(
leading: Icon(Icons.person),
title: Text('人物2'),
),
ListTile(
leading: Icon(Icons.person),
title: Text('人物3'),
),
],
),
),
);
}
}上述代码中,涉及到了children: <Widget>[...]这种dart专用写法,习惯就好, 之后就在数组里依次写进各组件。
ListView组件内, 嵌套Image组件列表
就和html5书写一样, 我们想在ul里面写上一排图片, <li><img ... /></li>...<li><img ... /></li>如何做到的呢?
修改上边一部分代码片段, 改为如下:
body: ListView(
children: <Widget>[
Image.network('http://www.wkwkk.com/filepool/cover/img_17021914350735944.jpg'),
Image.network('http://www.wkwkk.com/filepool/cover/img_17021914350735944.jpg'),
Image.network('http://www.wkwkk.com/filepool/cover/img_17021914350735944.jpg'),
Image.network('http://www.wkwkk.com/filepool/cover/img_17021914350735944.jpg'),
],
),上述代码会实现竖向的图片列表, 有人会纠结于new不new这个Image组件, 无论如何写法,效果都雷同。
横向组件列表
ListView组件当中, 有scrollingDirection属性, 是定义滚动方向的, 其中:
Axis组件就是网格控制组件。
Axis.horizontal 代表横向滚动
Axis.vertical 代表纵向滚动
代码案例如下:
import 'package:flutter/material.dart';
void main()=>runApp(MyApp());
class MyApp extends StatelessWidget
{
@override
Widget build(BuildContext context)
{
return MaterialApp(
title: 'flutter app test',
home: Scaffold(
appBar: AppBar(
title: Text('ListView横向列表'),
),
body: Center(
child: Container(
height: 200.0,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
Container(
width: 180.0,
color: Colors.green
),
Container(
width: 180.0,
color: Colors.lightBlue,
),
Container(
width: 180.0,
color: Colors.yellowAccent
),
Container(
width: 180.0,
color: Colors.blueGrey
),
],
),
),
),
),
);
}
}上述代码中, 使用几个Container加载色块, 加以横向排列来生成了预期效果。
ListView.builder动态列表建立和传参
以下是代码案例:
import 'package:flutter/material.dart';
void main()=>runApp(MyApp(
item: new List<String>.generate(200, (i)=>"Item $i")
));
class MyApp extends StatelessWidget
{
final List<String> item;
MyApp({Key key, @required this.item}):super(key:key);
@override
Widget build(BuildContext context)
{
return MaterialApp(
title: 'flutter app test',
home: Scaffold(
appBar: AppBar(
title: Text('ListView动态列表'),
),
body: ListView.builder(
itemCount: item.length,
itemBuilder:(context, index){
return new ListTile(
title: new Text("{$item[index]}"),
);
}
),
),
);
}
}上述代码当中ListView.builer方法即是生成动态列表方法。
在该方法内,
itemCount属性传入参数的长度, 即参数名.length
itemBuilder属性固定写入两个参数, context代表上下文, index代表List数组索引, 此时, 值作用域return所返回的是ListTile砖块, 简单写了一个$item套索引的方式来打印出每个List索引指向的值。
GridView组件
以下是代码案例:
import "package:flutter/material.dart";
void main()=>runApp(MyApp());
class MyApp extends StatelessWidget
{
@override
Widget build(BuildContext context)
{
return MaterialApp(
title: '电影海报测试',
home: Scaffold(
appBar: AppBar(
title: Text("我是标题"),
),
body: GridView.count(
padding:const EdgeInsets.all(10.0),
crossAxisSpacing: 10.0,
crossAxisCount: 3,
children: <Widget>[
Text('It was mine'),
Text('It was mine'),
Text('It was mine'),
Text('It was mine'),
Text('It was mine'),
Text('It was mine'),
Text('It was mine'),
Text('It was mine'),
Text('It was mine'),
]
)
),
);
}
}上述代码当中GridView.count是其中一种写法,也可以直接写为GridView(...)这种方式。
crossAxisSpacing属性代表横向格子之间的间距, 可以理解成html的td左右间距,
crossAxisCount属性代表一行多少个元素,
另外还有一个mainAxisSpacing属性, 代表竖向(列所在)各个格子之间的间距, 可以理解成html的td上下间距。
这些都可以通过padding属性来定义格子的间距, 不过padding属性不允许直接传double类型值, 需要传类似EdgeInsets组件中的all方法, 如例子中那样。
接下来把用Text组件填充的部分做成图片:
import "package:flutter/material.dart";
void main()=>runApp(MyApp());
class MyApp extends StatelessWidget
{
@override
Widget build(BuildContext context)
{
return MaterialApp(
title: '电影海报测试',
home: Scaffold(
appBar: AppBar(
title: Text("我是标题"),
),
body: GridView.count(
// padding:const EdgeInsets.all(10.0),
crossAxisSpacing: 10.0,
crossAxisCount: 3,
mainAxisSpacing: 10.0,
childAspectRatio: 1.2,
children: <Widget>[
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
]
)
),
);
}
}上述代码中
childAspectRatio属性代表的是宽高比,1是正方形,值越大,越宽,反之图片范围越高。
GridView组件直接传值的写法(不使用count)
代码案例如下:
import "package:flutter/material.dart";
void main()=>runApp(MyApp());
class MyApp extends StatelessWidget
{
@override
Widget build(BuildContext context)
{
return MaterialApp(
title: '电影海报测试',
home: Scaffold(
appBar: AppBar(
title: Text("我是标题"),
),
body: GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
// padding:const EdgeInsets.all(10.0),
crossAxisSpacing: 10.0,
crossAxisCount: 3,
mainAxisSpacing: 10.0,
childAspectRatio: 1.2,
),
children: <Widget>[
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
Image.network(
'http://www.wkwkk.com/filepool/cover/img_17020900372744314.jpg',
fit: BoxFit.cover,
),
]
)
),
);
}
}