Flutter第二章(Image,ListView,GridVi
版权声明:本文为作者原创书籍。转载请注明作者和出处,未经授权,严禁私自转载,侵权必究!!!
情感语录: 请你相信,岁月会成就最好的自己,时光也必将打磨出你独一无二的美丽。
哈喽!大家好,上一章节介绍了自定义Widget、Center组件、Text组件、MaterialApp组件、Scaffold组件
的简单应用你都掌握了吗?上章知识回顾 戳 Flutter基础第一章相信你在上一章节的练习中已经感受到了 ' Hot Reload
的魅力,Flutter舍弃xml
使用代码描述布局,布局的变动能立刻反映出变化,告别了原生中的重新编译安装的过程,你觉得怎样呢?反正我是觉得很爽 O(∩_∩)O
本章简要:
本章主要讲解图片组件(Image
)、列表组件(ListView
)、网格列表组件(GridView
)。做过原生开发的同学对这三个组件感觉应该是亲切至极。像极了原生中的ImageView 和ListView以及 GridView控件,在Flutter中用法也是极其相似。
一、图片组件(Image)
Image组件是显示图像的组件,用法和原生ImageView大相径庭,但在原生中需要借助Glide或者其他框架才能很方便的加载图片,在Flutter中自身就能实现加载。Image 组件有很多构造函数:
Image.asset:用来加载当前应用资源图片
Image.network:用来加载网络图片
Image.file:用来加载SD卡(File文件)图片
Image.memory:用来加载 byte[] 字节数组图片
Image:通过ImageProvider来加载图片
1、Image.asset
加载一个本地资源图片同 IOS 一样,分为 1x,2x,3x ...
,具体做法是在项目的根目录下创建倍图文件夹,一倍图直接放入Images
目录下,如下图所示:
实例:在屏幕上加载一个宽高300的本地资源图片,代码如下:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('呆萌')),
body: HomeContent(),
));
}
}
class HomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Container(
child:Image.asset('images/mm.jpg',
),
height: 300,
width: 300,
)
);
}
}
本篇文章篇幅可能会很长,这里就不贴效果图了,本章的实战效果会在最后贴出,喜欢尝试的同学可以复制体验下,需要注意的是记得修改你的图片名称哟!!!!
2、Image.network
Flutter中加载的无论是普通网络图片还是Gif
使用都是如加载本地图片一样,没有原生中那么显得复杂:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('呆萌')),
body: HomeContent(),
));
}
}
class HomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Container(
//加载本地资源
//child:Image.asset('images/mm.jpg'),
//加载网络图片
child:Image.network('https://upload.jianshu.io/users/upload_avatars/3030564/2789e9ea-9856-456f-be2a-e00ed5992c26.png?imageMogr2/auto-orient/strip|imageView2/1/w/300/h/300/format/webp'),
height: 300,
width: 300,
)
);
}
}
3、Image.file
加载本地磁盘图片文件,相比加载网络图片要复杂一些,首先在Android目录下的 AndroidManifest.xml 配置读写权限 如下:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
读文件当然需要IO包 ,引入 import 'dart:io';,
import 'dart:io';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('呆萌')),
body: HomeContent(),
));
}
}
class HomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Container(
//加载本地资源
//child:Image.asset('images/mm.jpg'),
//加载网络图片
//child:Image.network('https://upload.jianshu.io/users/upload_avatars/3030564/2789e9ea-9856-456f-be2a-e00ed5992c26.png?imageMogr2/auto-orient/strip|imageView2/1/w/300/h/300/format/webp'),
//加载磁盘图片文件
child:Image.file(File('/storage/emulated/0/Pictures/mm.jpg')),
// child:Image.memory(byteList),
height: 300,
width: 300,
)
);
}
}
Duang???磁盘图片文件加载是不是遇到问题了?热加载起来怎么一片空白,图片也不显示,客官别急,先将程序卸载掉,然后重新运行安装便可显示了。这个问题希望后期Flutter会优化吧,如果还是不显示,可能就是android版本是6.0以上的问题,需要动态申请运行权限。还有需要注意的是你需要替换成你本地图片文件的地址哟。
4、Image.memory
用来将一个 byte 数组加载成图片,这个使用场景相对较少,后面章节中会有应用到。这里就不做详细介绍了:
new Image.memory(bytes)
5、ImageProvider 加载占位图
有的时候我们需要像Android那样使用一个占位图或者图片加载出错时显示某张特定的图片,这时候需要用到 FadeInImage 这个组件:
import 'dart:io';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('呆萌')),
body: HomeContent(),
));
}
}
class HomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Container(
child: new FadeInImage.assetNetwork(
placeholder: 'images/mm.jpg', //目标图片没显示或者加载失败 显示 该图片
image: "https://upload.jianshu.io/users/upload_avatars/3030564/2789e9ea-9856-456f-be2a-e00ed5992c26.png?imageMogr2/auto-orient/strip|imageView2/1/w/300/h/300/format/webp",
width: 300,
fit: BoxFit.fitWidth,
),
height: 300,
width: 300,
)
);
}
}
二、Image组件中的常用属性
名称 说明
alignment 图片的对齐方式
color和colorBlendMode 设置图片的背景颜色,通常和 colorBlendMode 配合一起
使用,这样可以是图片颜色和背景色混合。
fit fit 属性用来控制图片的拉伸和挤压,这都是根据父容器来的。
BoxFit.fill:全图显示,图片会被拉伸,并充满父容器。
BoxFit.contain:全图显示,显示原比例,可能会有空隙。
BoxFit.cover:显示可能拉伸,可能裁切,充满(图片要
充满整个容器,还不变形)。
BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸,
可能裁切。
BoxFit.fitHeight :高度充满(竖向充满),显示可能拉
伸,可能裁切。
BoxFit.scaleDown:效果和 contain 差不多,但是此属
性不允许显示超过源图片大小,可小不可大。
repeat 平铺 ImageRepeat.repeat : 横向和纵向都进行重复,直到铺满整
个画布。
ImageRepeat.repeatX: 横向重复,纵向不重复。
ImageRepeat.repeatY:纵向重复,横向不重复。
width 宽度 一般结合 ClipOval 才能看到效果
height 高度 一般结合 ClipOval 才能看到效果
三、ListView组件
Flutter中的列表组件一样具有Android原生中的ListView控件或者RecyclerView的功效,在写法上我觉的比原生使用更加简单,下面来看ListView组件中常用的一些属性:
名称 类型 说明
scrollDirection Axis Axis.horizontal 水平列表
Axis.vertical 垂直列表
padding EdgeInsetsGeometry 内边距
resolve bool 组件反向排序
children List<Widget> 列表元素
1、水平布局
在Flutter中水平布局相比Android 原生控件简单太多,尤其是在Android 发布RecyclerView 控件之前,你可能要重写一个横向的自定义ListView控件去实现横向菜单,这对于刚入手的同学来说就很难实现了。下面我们利用Flutter ListView组件来实现一个简易的banner
实例代码:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('呆萌')),
body: BannerView(),
));
}
}
class BannerView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(3),
height: 120, // 限制Container 的高度,不让子元素listView填充整个屏幕
child: ListView(
padding: EdgeInsets.all(10), //10个单位的内边距,离屏幕四周分隔开一点
scrollDirection: Axis.horizontal, // 指定为水平样式
children: <Widget>[
Container(
width: 120.0,
height: 120.0,
decoration: BoxDecoration( //设置成圆角卡片样式
color: Colors.red,
borderRadius: BorderRadius.circular(10),
),
),
SizedBox( // 分割线 其实就是给点里右边的距离
width: 10,
height: 10,
),
Container(
width: 120.0,
height: 120.0,
decoration: BoxDecoration(
color: Colors.orange,
borderRadius: BorderRadius.circular(10),
),
),
SizedBox(
width: 10,
height: 10,
),
Container(
width: 120.0,
height: 120.0,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(10),
),
),
SizedBox(
width: 10,
height: 10,
),
Container(
width: 120.0,
height: 120.0,
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(10),
),
),
SizedBox(
width: 10,
height: 10,
),
Container(
width: 120.0,
height: 120.0,
decoration: BoxDecoration(
color: Colors.deepPurpleAccent,
borderRadius: BorderRadius.circular(10),
),
),
],
),
);
}
}
【温馨提示】 在Flutter 列表组件中不能使用 margin
属性的情况下,可以使用SizedBox
组件,或者Container
组件都能满足我们的需求。效果如下:
2、垂直布局
延用上面横向banner这个例子,简单修改下,做成一个垂直的菜单栏,代码如下:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('呆萌')),
body: BannerView(),
));
}
}
class BannerView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(3),
child: ListView(
padding: EdgeInsets.all(10), //10个单位的内边距,离屏幕四周分隔开一点
scrollDirection: Axis.vertical, // 指定为垂直样式
children: <Widget>[
Container(
alignment: Alignment.center,
child: new Text(
"Java专栏",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 30.0,
color: Colors.white,
),
),
height: 120.0,
decoration: BoxDecoration( //设置成圆角卡片样式
color: Colors.red,
borderRadius: BorderRadius.circular(10),
),
),
SizedBox( // 分割线
width: 10,
height: 10,
),
Container(
alignment: Alignment.center,
child: new Text(
"Kotlin专栏",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 30.0,
color: Colors.white,
),
),
height: 120.0,
decoration: BoxDecoration( //设置成圆角卡片样式
color: Colors.orange,
borderRadius: BorderRadius.circular(10),
),
),
SizedBox(
height: 10,
),
Container(
alignment: Alignment.center,
child: new Text(
"Flutter专栏",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 30.0,
color: Colors.white,
),
),
height: 120.0,
decoration: BoxDecoration( //设置成圆角卡片样式
color: Colors.blue,
borderRadius: BorderRadius.circular(10),
),
),
SizedBox(
height: 10,
),
Container(
alignment: Alignment.center,
child: new Text(
"Swift专栏",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 30.0,
color: Colors.white,
),
),
height: 120.0,
decoration: BoxDecoration( //设置成圆角卡片样式
color: Colors.green,
borderRadius: BorderRadius.circular(10),
),
),
SizedBox(
height: 10,
),
Container(
alignment: Alignment.center,
child: new Text(
"Object C",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 30.0,
color: Colors.white,
),
),
height: 120.0,
decoration: BoxDecoration( //设置成圆角卡片样式
color: Colors.deepPurpleAccent,
borderRadius: BorderRadius.circular(10),
),
),
],
),
);
}
}
呆萌效果,也是分分钟见高逼格菜单,O(∩_∩)O
menue.gif细心的你肯定发现了一个问题,上面的数据都是提前写死在程序里的,对于常规的固定菜单自然是没问题,但假如我们的数据是动态从服务器请求获取的呢?
3、ListView组件之ListView.builder
在Android原生中ListView、RecyclerView是通常是继承BaseAdapter、RecyclerView.Adapter 去实列表布局显示的,在Flutter中也能很轻松去实现同样的效果。
3.1 列表 item 之 ListTile
在讲解ListView.builder的用法之前,先看下常配合ListView使用的ListTile,查看源码有如下这么多属性可以配置。
const ListTile({
Key key,
this.leading, // item 前置图标
this.title, // item 标题
this.subtitle, // item 副标题
this.trailing, // item 后置图标
this.isThreeLine = false, // item 是否三行显示
this.dense, // item 直观感受是整体大小
this.contentPadding, // item 内容内边距
this.enabled = true, // itme 状态是否启用
this.onTap, // item onTap 点击事件
this.onLongPress, // item onLongPress 长按事件
this.selected = false, // item 是否选中状态
})
下面我们来看下适配器之 ListView.builder
的简单实例:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('呆萌')),
body: HomeContent(),
));
}
}
class HomeContent extends StatelessWidget {
//自定义方法
Widget _getListData(context,index){
return new Container(
child: Column(
children: <Widget>[
ListTile(
title: Text(musicData[index]["music"]), //设置标题文本内容
leading:Image.network(musicData[index]["url"]), //在文本前显示网络图片
subtitle:Text(musicData[index]["author"]), // 设置二级标题
),
//添加一条分割线
Divider(
color: Colors.grey,
height: 1,
)
],
),
);
}
@override
Widget build(BuildContext context) {
return ListView.builder(
//返回数据长度
itemCount:musicData.length,
//将_getListData 的结果绑定到当前Item上,注意这里没有加(),加()表示执行该方法
itemBuilder:this._getListData
);
}
}
// 假设这是从服务器端请求下来的数据
List musicData=[
{
"music": '爱情转移',
"author": '陈奕迅',
"url": 'http://qukufile2.qianqian.com/data2/pic/af739e0109798366b9419230be5253ce/541222074/541222074.jpg',
},
{
"music": '说谎',
"author": '林宥嘉',
"url": 'http://qukufile2.qianqian.com/data2/pic/11fd3062a07e029325e152aac593531a/533505799/533505799.jpg',
},
{
"music": '后来',
"author": '刘若英',
"url": 'http://qukufile2.qianqian.com/data2/pic/246708144/246708144.jpg',
},
{
"music": '暖暖',
"author": '梁静茹',
"url": 'http://qukufile2.qianqian.com/data2/pic/ad87603bb29cc5ac6ef5945582d56cdd/580337204/580337204.jpg',
},
{
"music": '数天数',
"author": '龚玥',
"url": 'http://qukufile2.qianqian.com/data2/pic/246584942/246584942.jpg',
},
{
"music": '大美青海',
"author": '琼雪卓玛',
"url": 'http://qukufile2.qianqian.com/data2/pic/318191ab36d37cdf1b7a8f12d7c633ed/611246006/611246006.jpg',
},
{
"music": '爱在心里',
"author": '刘思嫒',
"url": 'http://qukufile2.qianqian.com/data2/pic/baf889dd9e0e36efdde4d8c23b1ff944/607931693/607931693.jpg',
}
];
上面代码中用到了 Column
组件,这个组件会在后面的章节中讲到,这里看不懂没关系,你可以理解成原生中的 LinearLayout
垂直布局,添加到该容器中的View以此往下排版。Divider
组件就是用来显示分割线的,你可以很轻松的配置分割线高度,以及颜色值,当然你也可以不用它,或者使用ListView.separated 带分隔符的列表
甚至是Container
组件来代替它的使用。
由于模拟器有点垃圾,一些分割线显示不出来,这里使用手机截屏,效果大致如下:
红红的截屏.jpg
你可能会问可不可以不使用 ListView.builde
这玩意去实现吗?查看源码 发现 ListView 的children 接收的是一个 List<Widget>
。这个List<Widget>其实就是我们每个Item视图,下面我们通过循环去组装一个列表视图,实现上面一样的效果。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('呆萌')),
body: HomeContent(),
));
}
}
class HomeContent extends StatelessWidget {
//通过循环添加
List<Widget> _getData(){
List<Widget> list=new List();
for(var i=0;i<musicData.length;i++){
list.add(Column(
children: <Widget>[
ListTile(
title: Text(musicData[i]["music"]), //设置标题文本内容
leading:Image.network(musicData[i]["url"]), //在文本前显示网络图片
subtitle:Text(musicData[i]["author"]), // 设置二级标题
),
//添加一条分割线
new Container(
color: Colors.yellow,
height: 1,
)
],
),
);
}
return list;
}
@override
Widget build(BuildContext context) {
return ListView(
children: this._getData(),
);
}
}
// 假设这是从服务器端请求下来的数据
List musicData=[
{
"music": '爱情转移1',
"author": '陈奕迅',
"url": 'http://qukufile2.qianqian.com/data2/pic/af739e0109798366b9419230be5253ce/541222074/541222074.jpg',
},
{
"music": '说谎',
"author": '林宥嘉',
"url": 'http://qukufile2.qianqian.com/data2/pic/11fd3062a07e029325e152aac593531a/533505799/533505799.jpg',
},
{
"music": '后来',
"author": '刘若英',
"url": 'http://qukufile2.qianqian.com/data2/pic/246708144/246708144.jpg',
},
{
"music": '暖暖',
"author": '梁静茹',
"url": 'http://qukufile2.qianqian.com/data2/pic/ad87603bb29cc5ac6ef5945582d56cdd/580337204/580337204.jpg',
},
{
"music": '数天数',
"author": '龚玥',
"url": 'http://qukufile2.qianqian.com/data2/pic/246584942/246584942.jpg',
},
{
"music": '大美青海',
"author": '琼雪卓玛',
"url": 'http://qukufile2.qianqian.com/data2/pic/318191ab36d37cdf1b7a8f12d7c633ed/611246006/611246006.jpg',
},
{
"music": '爱在心里',
"author": '刘思嫒',
"url": 'http://qukufile2.qianqian.com/data2/pic/baf889dd9e0e36efdde4d8c23b1ff944/607931693/607931693.jpg',
}
];
运行后你发现效果是和上面一模一样的,这里就不贴效果图了,当然在开发中我们还是建议使用 ListView.builde 因为它在内部做了些优化,这就像Android 原生中 ListView控件 使用ViewHolder是一样的,显示条目复用,能节约很大一部分内存和性能。
四、GridView 组件
在Flutter中网格布局和Android原生同名依然是采用的GridView命名,用法也是极其简单,这和我们上面讲到的ListView极为相似,下面我们先来看下它的常用属性:
名称 类型 说明
scrollDirection Axis 滚动方法
padding EdgeInsetsGeometry 内边距
resolve bool 组件反向排序
crossAxisSpacing double 水平子 Widget 之间间距
mainAxisSpacing double 垂直子 Widget 之间间距
crossAxisCount int 一行的 Widget 数量
childAspectRatio double 子 Widget 宽高比例
children <Widget>[ ]
gridDelegate SliverGridDelegateWithFixedCrossAxisCount 控制布局主要用在GridView.builder 里面
SliverGridDelegateWithMaxCrossAxisExtent
4.1 GridView 创建网格列表有多种方式,其中常用的构造函数
1、GridView.builder
2、GridView.count
3、GridView.custom
4、GridView.extent
下面我们主要介绍两种:GridView.builder
和 GridView.count
嘿哈,看到没?GridView.builder也同样具有ListView.builder
一样的适配器,我们来将上面的音乐列表换成网格列表来显示看看 代码如下:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('呆萌')),
body: HomeContent(),
));
}
}
class HomeContent extends StatelessWidget {
Widget _getListData(context, index) {
return Container(
margin: EdgeInsets.all(10),
width: 110,
height: 110,
child: Column(
children: <Widget>[
SizedBox(height: 10),
Image.network(musicData[index]["url"],width: 110,height: 100,fit: BoxFit.fitWidth),
SizedBox(height: 10),
Text(musicData[index]["author"], textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
color: Colors.white
)),
],
),
//绘制圆角背景
decoration: BoxDecoration(
//圆角10个单位
borderRadius: BorderRadius.all(Radius.circular(10)),
//背景色
color: Colors.deepPurple,
//绘制边框
border: Border.all(
color: Colors.deepPurple,
width: 1.0,
)
),
);
}
@override
Widget build(BuildContext context) {
return GridView.builder(
//item总数量
itemCount: musicData.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
//横轴元素个数
crossAxisCount: 2,
//纵轴间距
mainAxisSpacing: 10.0,
//横轴间距
crossAxisSpacing: 10.0,
//子组件宽高长度比例
childAspectRatio: 1.0
),
itemBuilder: this._getListData,
);
}
}
// 假设这是从服务器端请求下来的数据
List musicData=[
{
"music": '爱情转移',
"author": '陈奕迅',
"url": 'http://qukufile2.qianqian.com/data2/pic/af739e0109798366b9419230be5253ce/541222074/541222074.jpg',
},
{
"music": '说谎11',
"author": '林宥嘉',
"url": 'http://qukufile2.qianqian.com/data2/pic/11fd3062a07e029325e152aac593531a/533505799/533505799.jpg',
},
{
"music": '后来',
"author": '刘若英',
"url": 'http://qukufile2.qianqian.com/data2/pic/246708144/246708144.jpg',
},
{
"music": '暖暖',
"author": '梁静茹',
"url": 'http://qukufile2.qianqian.com/data2/pic/ad87603bb29cc5ac6ef5945582d56cdd/580337204/580337204.jpg',
},
{
"music": '数天数',
"author": '龚玥',
"url": 'http://qukufile2.qianqian.com/data2/pic/246584942/246584942.jpg',
},
{
"music": '大美青海',
"author": '琼雪卓玛',
"url": 'http://qukufile2.qianqian.com/data2/pic/318191ab36d37cdf1b7a8f12d7c633ed/611246006/611246006.jpg',
},
{
"music": '爱在心里',
"author": '刘思嫒',
"url": 'http://qukufile2.qianqian.com/data2/pic/baf889dd9e0e36efdde4d8c23b1ff944/607931693/607931693.jpg',
}
];
image.png
上面通过 GridView.builder 实现网格布局,接下来通过使用 GridView.count
实现上面一样的效果:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('呆萌')),
body: HomeContent(),
));
}
}
class HomeContent extends StatelessWidget {
List<Widget> _getListData() {
var tempList = musicData.map((value){
return Container(
child:Column(
children: <Widget>[
SizedBox(height: 10),
Image.network(value["url"],width: 110,height: 100,fit: BoxFit.fitWidth),
SizedBox(height: 10),
Text(value["author"], textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
color: Colors.white
)),
],
),
//绘制圆角背景
decoration: BoxDecoration(
//圆角10个单位
borderRadius: BorderRadius.all(Radius.circular(10)),
//背景色
color: Colors.deepPurple,
//绘制边框
border: Border.all(
color: Colors.deepPurple,
width: 1.0,
)
),
);
});
return tempList.toList();
}
@override
Widget build(BuildContext context) {
return GridView.count(
//水平子 Widget 之间间距
crossAxisSpacing:10.0 ,
//垂直子 Widget 之间间距
mainAxisSpacing: 10.0,
padding: EdgeInsets.all(10),
//一行的 Widget 数量
crossAxisCount: 2,
//宽度和高度的比例
// childAspectRatio:0.7,
children: this._getListData(),
);
}
}
// 假设这是从服务器端请求下来的数据
List musicData=[
{
"music": '爱情转移',
"author": '陈奕迅',
"url": 'http://qukufile2.qianqian.com/data2/pic/af739e0109798366b9419230be5253ce/541222074/541222074.jpg',
},
{
"music": '说谎11',
"author": '林宥嘉',
"url": 'http://qukufile2.qianqian.com/data2/pic/11fd3062a07e029325e152aac593531a/533505799/533505799.jpg',
},
{
"music": '后来',
"author": '刘若英',
"url": 'http://qukufile2.qianqian.com/data2/pic/246708144/246708144.jpg',
},
{
"music": '暖暖',
"author": '梁静茹',
"url": 'http://qukufile2.qianqian.com/data2/pic/ad87603bb29cc5ac6ef5945582d56cdd/580337204/580337204.jpg',
},
{
"music": '数天数',
"author": '龚玥',
"url": 'http://qukufile2.qianqian.com/data2/pic/246584942/246584942.jpg',
},
{
"music": '大美青海',
"author": '琼雪卓玛',
"url": 'http://qukufile2.qianqian.com/data2/pic/318191ab36d37cdf1b7a8f12d7c633ed/611246006/611246006.jpg',
},
{
"music": '爱在心里',
"author": '刘思嫒',
"url": 'http://qukufile2.qianqian.com/data2/pic/baf889dd9e0e36efdde4d8c23b1ff944/607931693/607931693.jpg',
}
];
从代码上来看两者的使用其实都很简单,没什么难点。但需要注意的两点是:
1.
GridView.builder 中需要指定条目的数量,itemBuilder 接收是一个方法,至于该方法何时被调用,我们无需关心,因为这一切都在内部帮你实现了,所以再写的时候一定得注意。
2.
GridView.count 虽然无需要指定item数量,因为Item的数量完全取决于 children返回的List<Widget>的长度,所以要显示网格列表,需要提前组装好 children 包裹的内容。
本章实战
通过对上面Image组件
、ListView组件
、GridView组件
的大致了解,本章实战内容就是利用上面学到的组件实现一个图文列表,点击按钮可以从单列表变成网格列表,网格列表又能变成单列表的效果。
import 'package:flutter/material.dart';
import 'res/TestData.dart';
void main() => runApp(MyApp());
bool isGrid = false;
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new ViewStatefulWidget(),
);
}
}
//自定义Widget 实现有状态StatefulWidget
class ViewStatefulWidget extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return HomeContent();
}
}
class HomeContent extends State<ViewStatefulWidget> {
void changeView() {
// 通过 setState() 更新数据,组件树自动刷新
setState(() {
if (isGrid) {
isGrid = false;
} else {
isGrid = true;
}
});
}
List<Widget> _getListData() {
var tempList = musicData.map((value) {
return Container(
child: Column(
children: <Widget>[
SizedBox(height: 10),
Image.network(value["url"],
width: 110, height: 100, fit: BoxFit.fitWidth),
SizedBox(height: 10),
Text(value["author"],
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20, color: Colors.white)),
],
),
//绘制圆角背景
decoration: BoxDecoration(
//圆角10个单位
borderRadius: BorderRadius.all(Radius.circular(10)),
//背景色
color: Colors.deepPurple,
//绘制边框
border: Border.all(
color: Colors.deepPurple,
width: 1.0,
)),
);
});
return tempList.toList();
}
//通过循环添加
List<Widget> _getData() {
List<Widget> list = new List();
for (var i = 0; i < musicData.length; i++) {
list.add(
Column(
children: <Widget>[
ListTile(
title: Text(musicData[i]["music"]), //设置标题文本内容
leading: Image.network(musicData[i]["url"]), //在文本前显示网络图片
subtitle: Text(musicData[i]["author"]), // 设置二级标题
),
//添加一条分割线
new Container(
color: Colors.black12,
height: 1,
)
],
),
);
}
return list;
}
// 根据状态返回不同的Widget
Widget getWidget() {
if (isGrid) {
return ListView(
children: this._getData(),
);
} else {
return GridView.count(
//水平子 Widget 之间间距
crossAxisSpacing: 10.0,
//垂直子 Widget 之间间距
mainAxisSpacing: 10.0,
padding: EdgeInsets.all(10),
//一行的 Widget 数量
crossAxisCount: 2,
//宽度和高度的比例
// childAspectRatio:0.7,
children: this._getListData(),
);
}
}
@override
Widget build(BuildContext context) {
return new Container(
child: new Scaffold(
appBar: AppBar(
title: Text("呆萌"),
backgroundColor: Colors.red, //appBar 背景色
centerTitle: true, //标题居中显示
actions: <Widget>[
IconButton(
icon: Icon(Icons.menu),
onPressed: () { //添加按下事件
changeView();
},
)
],
),
body: new Container(
child: getWidget(),
)),
);
}
}
这里只是演示只简单的写了下样式,你也可以仔细的调整下页面这也是知识点的巩固嘛,最终效果:
shizhan.gif
在本章实战中使用到了有状态StatefulWidget
以及点击事件onPressed
,点击事件很好理解,至于StatefulWidget
现在看不懂没关系,先尝试用嘛,这会在后面的章节中详细讲到。
实战源码地址: https://github.com/zhengzaihong/flutter_learn
好了本章节就此结束,又到了说再见的时候了,如果你喜欢请留下你的小红星,你们的支持才是创作的动力。谢谢大家观看,下章再会 O(∩_∩)O