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, ), ] ) ), ); } }