项目场景:
本文仅介绍适用于插件
image_picker: ^0.6.7+17
版本下的写法。 Flutter项目中通过调用手机相机或图库得到的图片显示在Container组件中,通过Image.file进行图片的加载,通过参数file进行数据的传递。在image_picker: ^0.8.6
版本下的写法略有不同,本文仅给出完整代码,具体在本文不作说明。
具体image_picker插件版本的更新迭代以及API的变化介绍可以查看官方文档image_picker官方文档
问题描述
Flutter项目中代码无报错,在真机上运行时报错如下:
①null check operator used on a null value
②A value of type 'Null' can't be assigned to a variable of type 'File'
③Non-nullable instance field 'imagePath' must be initialized.
以及项目代码中报错如下:
④PickedFile和File类型不匹配
各种调试更改一直报错很是奔溃
原因探究
原因1分析:
原因1:项目代码中的字面意思,PickedFile和File类型不匹配,在新版本的Flutter中增加了PickedFile数据类型,当运用image_picker插件时调用手机相机或图库时会用到以下代码:
final ImagePicker picker = new ImagePicker();
var image = await picker.getImage(source: ImageSource.camera/gallery);
而此时得到的image对象的类型是PickedFile而非File类型,但是我们在展现我们得到的图片时一定需要使用到的语句是Image.file()
,而通过查看Image.file的原型,发现其中的参数是File类型的参数,因此我们不可以直接将image对象传入作为参数,这样会导致PickedFile和File类型不匹配的问题出现。
Image.file的原型
Image.file(
File file, {
super.key,
double scale = 1.0,
this.frameBuilder,
this.errorBuilder,
this.semanticLabel,
this.excludeFromSemantics = false,
this.width,
this.height,
this.color,
this.opacity,
this.colorBlendMode,
this.fit,
this.alignment = Alignment.center,
this.repeat = ImageRepeat.noRepeat,
this.centerSlice,
this.matchTextDirection = false,
this.gaplessPlayback = false,
this.isAntiAlias = false,
this.filterQuality = FilterQuality.low,
int? cacheWidth,
int? cacheHeight,
})
原因1解决方案:
通过查阅,发现可以通过
imagePath = File(image.path);
进行解决。其中imagePath为File类型的对象,image为PickedFile类型的对象,如此即可将PickedFile类型的数据转换为File类型的数据了。
原因2分析:
在新版本的Flutter中很多类型均不能为空,比如此处定义
File imagePath = null
这条语句在老版本中是可以执行通过的,而在新版本中项目会给你报错说不能为空或者该对象需要初始化的错误原因。所以我们需要知道的是新版本中新增了【类型?】的数据类型,该类型在不进行初始化时默认为空,当然你也可以自己初始化该对象为空。
原因2解决方案:
①定义和初始化时需要注意以下定义方式:
File? imagePath;//定义可以默认为空的File?类型对象
File? imagePath = null;//初始化imagePath对象为null
②通过Iamge.file()进行调用时需要注意以下方式:
Image.file(imagePath!,fit: BoxFit.contain)
注意第一个参数为File类型,但我们在初始定义时候他为【File?】类型,所以我们的第一个参数需要在参数后面加上一个感叹号,否则同样会报错。 根据我自身的理解,这个感叹号应该是和?进行相互抵消的作用。
完整代码:
image_picker: ^0.6.7+17
版本下的完整代码
File? imagePath;//定义可以为空的File?类型对象
//PickedFile file = PickedFile(null);
void showBottomMenu(context){
showModalBottomSheet(context: context, builder: (context){
return Container(
height: 200,
child: Column(
children: [
ListTile(
title: Text('拍照',textAlign: TextAlign.center,style: TextStyle(fontSize: 19),),
onTap: () async{
Navigator.of(context).pop();
//takePhotoFromCamera();
final ImagePicker picker = new ImagePicker();
var image = await picker.getImage(source: ImageSource.camera);
setState(() {
imagePath = File(image.path);//将PickedFile类型转化为File类型
});
}),
Divider(),
ListTile(
title: Text('从相册中选择',textAlign: TextAlign.center,style: TextStyle(fontSize: 19),),
onTap: () async{
Navigator.of(context).pop();
//print('00000');
//takePhotoFromGallery();
final ImagePicker picker = new ImagePicker();
var image = await picker.getImage(source: ImageSource.gallery);
setState(() {
imagePath = File(image.path);//将PickedFile类型转化为File类型
});
}),
Divider(),
ListTile(
title: Text('取消',textAlign: TextAlign.center,style: TextStyle(fontSize: 19),),
onTap: (){
Navigator.of(context).pop();
})
],
),
);
});
}
//将以上代码得到的图片传入Container组件中,其中child属性值为重点关注
Widget photo = Container(
height: MediaQuery.of(context).size.height/3.5,
width: MediaQuery.of(context).size.width,
color: Colors.black12,
child: GestureDetector(
//child: Icon(Icons.add,size: 120,color: Colors.red,),
//child: Image.file(imagePath!),
child: (imagePath != null) ? Image.file(imagePath!,fit: BoxFit.contain) : Icon(Icons.add,size: 120,color: Colors.red,),
onTap: (){
//print('1111111111111');
showBottomMenu(context);
},
),
);
image_picker: ^0.8.6
版本下的完整代码
XFile? imagePath;
//PickedFile file = PickedFile(null);
void showBottomMenu(context){
showModalBottomSheet(context: context, builder: (context){
return Container(
height: 200,
child: Column(
children: [
ListTile(
title: Text('拍照',textAlign: TextAlign.center,style: TextStyle(fontSize: 19),),
onTap: () async{
Navigator.of(context).pop();
//takePhotoFromCamera();
final ImagePicker picker = new ImagePicker();
final XFile? image = await picker.pickImage(source: ImageSource.camera);
setState(() {
imagePath = image;
//imagePath = image;
});
}),
Divider(),
ListTile(
title: Text('从相册中选择',textAlign: TextAlign.center,style: TextStyle(fontSize: 19),),
onTap: () async{
Navigator.of(context).pop();
//print('00000');
//takePhotoFromGallery();
final ImagePicker picker = new ImagePicker();
//var image = await picker.getImage(source: ImageSource.gallery);
final XFile? image = await picker.pickImage(source: ImageSource.gallery);
setState(() {
imagePath = image;
});
}),
Divider(),
ListTile(
title: Text('取消',textAlign: TextAlign.center,style: TextStyle(fontSize: 19),),
onTap: (){
Navigator.of(context).pop();
})
],
),
);
});
}
//将以上代码得到的图片传入Container组件中,其中child属性值为重点关注
Widget photo = Container(
height: MediaQuery.of(context).size.height/3.5,
width: MediaQuery.of(context).size.width,
color: Colors.black12,
child: GestureDetector(
//child: Icon(Icons.add,size: 120,color: Colors.red,),
//child: Image.file(imagePath!),
child: (imagePath != null) ? Image.file(imagePath!.path,fit: BoxFit.contain) : Icon(Icons.add,size: 120,color: Colors.red,),
onTap: (){
//print('1111111111111');
showBottomMenu(context);
},
),
);