Flutter基础Widget使用----Image和Icon
Image 和 Icon 都是Flutter中常用的 Widget,我们可以通过 Image Widget 来加载并显示图片,Image 的数据源可以是 asset 、文件、内存以及网络。 Icon 将图标做成字体文件,然后通过指定不同的字符而显示不同的图片。在 Android 开发中 Image Widget 类似于 ImageView,但比 ImageView 更强大,可以直接显示网络上的图片,在 Android 开发中这需要使用第三方库或自行实现。
一、Image
const Image({
Key key,
@required this.image,
this.frameBuilder,
this.loadingBuilder,
this.semanticLabel,
this.excludeFromSemantics = false,
this.width,
this.height,
this.color,
this.colorBlendMode,
this.fit,
this.alignment = Alignment.center,
this.repeat = ImageRepeat.noRepeat,
this.centerSlice,
this.matchTextDirection = false,
this.gaplessPlayback = false,
this.filterQuality = FilterQuality.low,
})
image ---- 要显示的图像, ImageProvider 类型
frameBuilder ---- 一个构建器函数,负责创建代表该图像的 Widget
loadingBuilder ---- 一个构建器,用于指定在图像仍在加载时向用户显示的 Widget
excludeFromSemantics ---- 是否从语义中排除此图像,对于不向应用程序提供有意义信息的图像很有用
color ---- 如果不为null,则使用 [colorBlendMode] 将这种颜色与每个图像像素混合
colorBlendMode ---- 颜色混合模式
fit ---- 如何将图像画在分配的空间中
repeat ---- 如何绘制图像未覆盖的布局边界的任何部分
centerSlice ---- .9 图像的中心切片
matchTextDirection ---- 是否沿 [TextDirection] 方向绘制图像
gaplessPlayback ---- 当 ImageProvider 更改时,是继续显示旧图像(true),还是暂时不显示任何图像(false)
filterQuality ---- 用于设置图像的 [FilterQuality]
/// 图像滤镜的质量等级
enum FilterQuality {
// 该列表来自 Skia 的 SkFilterQuality.h,值(顺序)应保持同步。
/// 最快的过滤,尽管质量也最低。
///
/// 通常,这意味着邻近插值。
none,
/// 质量比 [none] 好,比 [medium] 快。
///
/// 通常,这意味着双线性插值。
low,
/// 质量比 [low] 好,比 [high] 快。
///
/// 通常,这意味着将双线性插值和 pyramidal parametric pre-filtering(Mipmap)结合起来。
medium,
/// 最佳质量过滤,尽管速度也最慢。
///
/// 通常,这意味着双三次插值或更好。
high,
}
1.1 ImageProvider
ImageProvider 是一个抽象类,主要定义了图片数据获取的接口 load(),从不同的数据源获取图片需要实现不同的 ImageProvider,如 AssetImage 是实现了从 Asset 中加载图片的 ImageProvider,而 NetworkImage 实现了从网络加载图片的 ImageProvider。
1.2 从 Asset 中加载图片
1.工程根目录新建images文件夹,将image.jpg图片复制到images文件夹下;
2.在 pubspec.yaml 中的 flutter 部分添加图片;
flutter:
assets:
- images/image.jpg
3.代码中使用。
Image(image: AssetImage("images/image.jpg"), width: 100.0),
1.3 从网络加载图片
由于框架已经封装好了,我们只需要将图片URL写到对应位置即可。
// 方法一
Image(
image: NetworkImage(
"url"),
width: 100.0,
)
// 方法二
Image.network(
"url",
width: 100.0,
)
下面是使用示例代码。
var imageUrl =
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1583665145895&di=bcb225ca90a68654d2d1699caa9eeaf4&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201802%2F20%2F20180220165946_RiGPS.thumb.700_0.jpeg";
......
Image(image: NetworkImage(imageUrl), width: 100.0),
Image.network(imageUrl,width: 100.0,),
1.4 colorBlendMode
混合模式非常多,至于每一种效果源码中都包含有链接路径,我们通过网络的方式把这些图片全部加载过来看看。
/// 在画布上绘画时要使用的算法。
enum BlendMode {
// 此列表来自 Skia 的 SkXfermode.h,值(顺序)应保持同步。
// 见:https://skia.org/user/api/skpaint#SkXfermode
clear,
src,
dst,
srcOver,
dstOver,
srcIn,
dstIn,
srcOut,
dstOut,
srcATop,
dstATop,
xor,
plus,
modulate,
screen,
overlay,
darken,
lighten,
colorDodge,
colorBurn,
hardLight,
softLight,
difference,
exclusion,
multiply,
hue,
saturation,
color,
luminosity,
}
下面是示例代码,根据url的规则生成所有Image。
class ImageBlendModeRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
var i = 0;
var images = <Image>[];
for (i = 0; i < 29; i++) {
var modeStr = BlendMode.values[i].toString();
images.add(Image.network(
"https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_${modeStr.substring(10)}.png",
width: 100.0,
));
}
i = 0;
return Scaffold(
appBar: AppBar(
title: Text("ImageBlendModeRoute"),
),
body: SingleChildScrollView(
child: Column(
children: images.map((e) {
return Row(
children: <Widget>[
Padding(
padding: EdgeInsets.all(3.0),
child: SizedBox(
width: 100,
child: e,
),
),
Text(
BlendMode.values[i++].toString(),
style: TextStyle(
fontSize: 15,
color: Colors.black,
),
)
],
);
}).toList()),
));
}
}
1.5 fit
如何将图像画在分配的空间中,在android开发中也有类似的属性。
enum BoxFit {
fill,
contain,
cover,
fitWidth,
fitHeight,
none,
scaleDown,
}
fill ---- 会拉伸填充满显示空间,图片本身长宽比会发生变化,图片会变形。
cover ---- 会按图片的长宽比放大后居中填满显示空间,图片不会变形,超出显示空间部分会被剪裁。
contain ---- 这是图片的默认适应规则,图片会在保证图片本身长宽比不变的情况下缩放以适应当前显示空间,图片不会变形。
fitWidth ---- 图片的宽度会缩放到显示空间的宽度,高度会按比例缩放,然后居中显示,图片不会变形,超出显示空间部分会被剪裁。
fitHeight ---- 图片的高度会缩放到显示空间的高度,宽度会按比例缩放,然后居中显示,图片不会变形,超出显示空间部分会被剪裁。
none ---- 图片没有适应策略,会在显示空间内显示图片,如果图片比显示空间大,则显示空间只会显示图片中间部分。
scaleDown ---- 将源对准目标框(默认情况下居中),并在必要时按比例缩小源以确保源适合框内。
来看示例代码,运行起来看效果就会很好理解上面的每种类型。
class ImageFitRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
var img = AssetImage("images/image.jpg");
return Scaffold(
appBar: AppBar(
title: Text("ImageBlendModeRoute"),
),
body: SingleChildScrollView(
child: Column(
children: <Image>[
Image(
image: img,
height: 50.0,
width: 100.0,
fit: BoxFit.fill,
),
Image(
image: img,
height: 50,
width: 50.0,
fit: BoxFit.contain,
),
Image(
image: img,
width: 100.0,
height: 50.0,
fit: BoxFit.cover,
),
Image(
image: img,
width: 100.0,
height: 50.0,
fit: BoxFit.fitWidth,
),
Image(
image: img,
width: 100.0,
height: 50.0,
fit: BoxFit.fitHeight,
),
Image(
image: img,
width: 100.0,
height: 50.0,
fit: BoxFit.scaleDown,
),
Image(
image: img,
height: 50.0,
width: 100.0,
fit: BoxFit.none,
),
].map((e) {
return Row(
children: <Widget>[
Padding(
padding: EdgeInsets.all(16.0),
child: SizedBox(
width: 100,
child: e,
),
),
Text(e.fit.toString())
],
);
}).toList()),
));
}
}
1.6 repeat
下面我们看一个在X轴上重复的例子。看图就马上理解了。
Image(
image: AssetImage("images/image.jpg"),
width: 300.0,
height: 100.0,
repeat: ImageRepeat.repeatX,
),
1.7 filterQuality
想要看出插值效果,原图大小为 100px * 100px,我们将图片显示空间固定为 200 * 200,如此图片就需要放大才能完全显示。我们看到设置为 none 时,因为使用了邻近插值,显示效果不佳。
class ImageFilterQualityRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
var img = AssetImage("images/image.jpg");
return Scaffold(
appBar: AppBar(
title: Text("ImageFilterQualityRoute"),
),
body: SingleChildScrollView(
child: Column(
children: <Image>[
Image(
image: img,
height: 200.0,
width: 200.0,
fit: BoxFit.fill,
filterQuality: FilterQuality.none,
),
Image(
image: img,
height: 200.0,
width: 200.0,
fit: BoxFit.fill,
filterQuality: FilterQuality.low,
),
Image(
image: img,
height: 200.0,
width: 200.0,
fit: BoxFit.fill,
filterQuality: FilterQuality.medium,
),
Image(
image: img,
height: 200.0,
width: 200.0,
fit: BoxFit.fill,
filterQuality: FilterQuality.high,
),
].map((e) {
return Row(
children: <Widget>[
Padding(
padding: EdgeInsets.all(16.0),
child: SizedBox(
width: 200,
child: e,
),
),
Text(e.filterQuality.toString())
],
);
}).toList()),
));
}
}
二、Icon
Icon使用非常简单。
const Icon(
this.icon, {
Key key,
this.size,
this.color,
this.semanticLabel,
this.textDirection,
})
这些属性基本都已经见过。比如下面使用了三个图标。
Icon(Icons.add, color: Colors.red,),
Icon(Icons.pages, color: Colors.red,),
Icon(Icons.face, color: Colors.red,),
2.1 自定义字体图标
iconfont.cn 上有很多字体图标素材,我们可以选择自己需要的图标打包下载后,在 Flutter 中,我们使用ttf格式。网站如何下载这个图标,请自行查找方法。
1.下载需要使用的字体图标;
2.将下载后的iconfont.ttf文件拷贝到assets/fonts文件夹;
3.在 pubspec.yaml 中的 fonts 部分添加ttf;
flutter:
......
fonts:
......
- family: customIcon
fonts:
- asset: assets/fonts/iconfont.ttf
4.使用
先新建一个 CustomIcons.dart 类,把我们下载的字体图标都定义进来。至于 IconData 的 codePoint 可以在 iconfont.cn 下载的时候看到,如上图。
CustomIcons.dart
import 'package:flutter/material.dart';
class CustomIcons {
static const IconData auto = const IconData(0xe6eb,
fontFamily: 'customIcon', matchTextDirection: true);
static const IconData all = const IconData(0xe6ef,
fontFamily: 'customIcon', matchTextDirection: true);
static const IconData bussiness_man = const IconData(0xe6f0,
fontFamily: 'customIcon', matchTextDirection: true);
}
自定义的字体图标都显示为蓝色图标。
Icon(CustomIcons.auto, color: Colors.blue),
Icon(CustomIcons.all, color: Colors.blue),
Icon(CustomIcons.bussiness_man, color: Colors.blue),
最后老规矩,把完整Demo代码附上。
import 'package:flutter/material.dart';
import 'CustomIcons.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var imageUrl =
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1583665145895&di=bcb225ca90a68654d2d1699caa9eeaf4&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201802%2F20%2F20180220165946_RiGPS.thumb.700_0.jpeg";
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home Page"),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
Image(image: AssetImage("images/image.jpg"), width: 100.0),
]),
Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
Image(image: NetworkImage(imageUrl), width: 100.0),
]),
Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
Image.network(
imageUrl,
width: 100.0,
),
]),
RaisedButton(
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return ImageBlendModeRoute();
}));
},
child: Text("ImageBlendModeRoute"),
),
RaisedButton(
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return ImageFitRoute();
}));
},
child: Text("ImageFitRoute"),
),
RaisedButton(
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return ImageFilterQualityRoute();
}));
},
child: Text("ImageFilterQualityRoute"),
),
Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
Image(
image: AssetImage("images/image.jpg"),
width: 300.0,
height: 100.0,
repeat: ImageRepeat.repeatX,
),
]),
Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
Icon(
Icons.add,
color: Colors.red,
),
Icon(
Icons.pages,
color: Colors.red,
),
Icon(
Icons.face,
color: Colors.red,
),
Icon(CustomIcons.auto, color: Colors.blue),
Icon(CustomIcons.all, color: Colors.blue),
Icon(CustomIcons.bussiness_man, color: Colors.blue),
]),
],
));
}
}
class ImageBlendModeRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
var i = 0;
var images = <Image>[];
for (i = 0; i < 29; i++) {
var modeStr = BlendMode.values[i].toString();
images.add(Image.network(
"https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_${modeStr.substring(10)}.png",
width: 100.0,
));
}
i = 0;
return Scaffold(
appBar: AppBar(
title: Text("ImageBlendModeRoute"),
),
body: SingleChildScrollView(
child: Column(
children: images.map((e) {
return Row(
children: <Widget>[
Padding(
padding: EdgeInsets.all(3.0),
child: SizedBox(
width: 100,
child: e,
),
),
Text(
BlendMode.values[i++].toString(),
style: TextStyle(
fontSize: 15,
color: Colors.black,
),
)
],
);
}).toList()),
));
}
}
class ImageFitRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
var img = AssetImage("images/image.jpg");
return Scaffold(
appBar: AppBar(
title: Text("ImageBlendModeRoute"),
),
body: SingleChildScrollView(
child: Column(
children: <Image>[
Image(
image: img,
height: 50.0,
width: 100.0,
fit: BoxFit.fill,
),
Image(
image: img,
height: 50,
width: 50.0,
fit: BoxFit.contain,
),
Image(
image: img,
width: 100.0,
height: 50.0,
fit: BoxFit.cover,
),
Image(
image: img,
width: 100.0,
height: 50.0,
fit: BoxFit.fitWidth,
),
Image(
image: img,
width: 100.0,
height: 50.0,
fit: BoxFit.fitHeight,
),
Image(
image: img,
width: 100.0,
height: 50.0,
fit: BoxFit.scaleDown,
),
Image(
image: img,
height: 50.0,
width: 100.0,
fit: BoxFit.none,
),
].map((e) {
return Row(
children: <Widget>[
Padding(
padding: EdgeInsets.all(16.0),
child: SizedBox(
width: 100,
child: e,
),
),
Text(e.fit.toString())
],
);
}).toList()),
));
}
}
class ImageFilterQualityRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
var img = AssetImage("images/image.jpg");
return Scaffold(
appBar: AppBar(
title: Text("ImageFilterQualityRoute"),
),
body: SingleChildScrollView(
child: Column(
children: <Image>[
Image(
image: img,
height: 200.0,
width: 200.0,
fit: BoxFit.fill,
filterQuality: FilterQuality.none,
),
Image(
image: img,
height: 200.0,
width: 200.0,
fit: BoxFit.fill,
filterQuality: FilterQuality.low,
),
Image(
image: img,
height: 200.0,
width: 200.0,
fit: BoxFit.fill,
filterQuality: FilterQuality.medium,
),
Image(
image: img,
height: 200.0,
width: 200.0,
fit: BoxFit.fill,
filterQuality: FilterQuality.high,
),
].map((e) {
return Row(
children: <Widget>[
Padding(
padding: EdgeInsets.all(16.0),
child: SizedBox(
width: 200,
child: e,
),
),
Text(e.filterQuality.toString())
],
);
}).toList()),
));
}
}
上一篇: SVG基础|SVG坐标系统和图形转换