颤振 - 从画廊/相机中选择图像不会更新包含所选择图像预览的容器
Posted
技术标签:
【中文标题】颤振 - 从画廊/相机中选择图像不会更新包含所选择图像预览的容器【英文标题】:Flutter - picking an image from gallery/camera doesn't update the container that holds the picked image preview 【发布时间】:2021-09-20 23:11:42 【问题描述】:我正在开发一个允许用户写帖子的屏幕(类似于 facebook 的添加帖子),用户可以在其中从图库中选择图像并可以在预览容器中看到该图像,问题是预览没有' t 在我选择图像后立即更新,而不是我必须离开屏幕并返回查看预览,我添加了屏幕截图以进一步解释,我还使用向上滑动面板作为底页,让用户能够选择一种媒体类型(图像、视频、音频...)。
使用的依赖项:
sliding_up_panel:^1.0.2
image_picker: ^0.6.6+5
这是我的完整代码:
MediaFilesServices mediaFilesServices = MediaFilesServices();
PanelController _panelController = new PanelController();
String mediaFileType = "NONE";
PickedFile _image;
final ImagePicker imagePicker = ImagePicker();
class AddPostScreen extends StatefulWidget
@override
_AddPostScreenState createState() => _AddPostScreenState();
class _AddPostScreenState extends State<AddPostScreen>
@override
Widget build(BuildContext context)
return Scaffold(
resizeToAvoidBottomInset: false,
body: SafeArea(
child: Container(
margin: const EdgeInsets.only(top: 20), child: AddPostBody()),
),
);
class AddPostBody extends StatefulWidget
@override
_AddPostBodyState createState() => _AddPostBodyState();
class _AddPostBodyState extends State<AddPostBody>
@override
Widget build(BuildContext context)
return SlidingUpPanel(
padding: EdgeInsets.only(top: 12, left: 12, right: 12),
minHeight: 100,
maxHeight: 310,
backdropEnabled: true,
slideDirection: SlideDirection.UP,
isDraggable: true,
controller: _panelController,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24.0),
topRight: Radius.circular(24.0),
),
panel: ExpandedPanelBody(),
collapsed: collapsedPanelBody(),
body: Column(
children: [
Padding(
padding: const EdgeInsets.only(right: 14.0, left: 14, top: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
InkResponse(
onTap: ()
Navigator.pop(context);
,
child: Icon(CupertinoIcons.arrow_left)),
SizedBox(
width: 14,
),
Text('Add post'),
Spacer(),
ElevatedButton(
onPressed: () async ,
style: ButtonStyle(
shadowColor:
MaterialStateProperty.all<Color>(UIColors.primary_a),
backgroundColor:
MaterialStateProperty.all<Color>(UIColors.primary_a),
),
child: Text('share'),
)
],
),
),
SizedBox(
height: 18,
),
TextField(
maxLines: null,
keyboardType: TextInputType.multiline,
controller: _addPostController,
cursorColor: UIColors.primary_a,
decoration: InputDecoration(
contentPadding:
EdgeInsets.symmetric(horizontal: 16.0, vertical: 15.0),
border: InputBorder.none,
hintText: 'Say something...',
),
),
SizedBox(
height: 20,
),
Container(
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 3),
decoration: BoxDecoration(
color: UIColors.primaryTextFieldBackground.withAlpha(3),
),
child: TextField(
maxLines: 1,
controller: _addPostLocationController,
cursorColor: UIColors.primary_a,
style: TextStyle(color: UIColors.primary_a),
decoration: InputDecoration(
contentPadding:
EdgeInsets.symmetric(horizontal: 16.0, vertical: 15.0),
border: InputBorder.none,
hintText: 'drop your location',
prefixIcon: Icon(
CupertinoIcons.location_north_line_fill,
color: UIColors.primary_a,
size: 22,
),
),
),
),
SizedBox(
height: 14,
),
buildImageMedia(_image)
],
),
);
class ExpandedPanelBody extends StatefulWidget
@override
_ExpandedPanelBodyState createState() => _ExpandedPanelBodyState();
class _ExpandedPanelBodyState extends State<ExpandedPanelBody>
_imgFromCamera() async
PickedFile image = await imagePicker.getImage(
source: ImageSource.camera, imageQuality: 100);
setState(()
_image = image;
);
mediaFileType = "IMAGE";
_imgFromGallery() async
PickedFile image = await imagePicker.getImage(
source: ImageSource.gallery, imageQuality: 100);
setState(()
_image = image;
);
mediaFileType = "IMAGE";
@override
Widget build(BuildContext context)
void _showPicker(context)
showModalBottomSheet(
context: context,
builder: (context)
return SafeArea(
child: Container(
child: new Wrap(
children: <Widget>[
new ListTile(
leading: new Icon(Icons.photo_library),
title: new Text('Photo Library'),
onTap: ()
setState(()
_imgFromGallery();
);
Navigator.of(context).pop();
),
new ListTile(
leading: new Icon(Icons.photo_camera),
title: new Text('Camera'),
onTap: () async
await _imgFromCamera();
Navigator.of(context).pop();
,
),
],
),
),
);
);
return Container(
margin: EdgeInsets.only(bottom: 13.0),
child: Column(
children: [
Container(
decoration: BoxDecoration(
color: Colors.black12,
borderRadius: BorderRadius.all(Radius.circular(64.0)),
),
margin: EdgeInsets.only(left: 180.0, right: 180.0, bottom: 10.0),
height: 6.0,
),
ListTile(
onTap: () ,
leading: Icon(
CupertinoIcons.plus_circle,
color: UIColors.primary_a,
),
title: Text(
'upload media',
style: TextStyle(
color: UIColors.primary_a, fontWeight: FontWeight.w300),
),
),
ListTile(
onTap: ()
_showPicker(context);
,
leading: Icon(
FeatherIcons.image,
color: Colors.black87,
),
title: Text(
'picture',
style:
TextStyle(color: Colors.black87, fontWeight: FontWeight.w300),
),
),
ListTile(
leading: Icon(
FeatherIcons.film,
color: Colors.black87,
),
title: Text(
'video',
style:
TextStyle(color: Colors.black87, fontWeight: FontWeight.w300),
),
),
ListTile(
leading: Icon(
FeatherIcons.headphones,
color: Colors.black87,
),
title: Text(
'audio',
style:
TextStyle(color: Colors.black87, fontWeight: FontWeight.w300),
),
),
ListTile(
leading: Icon(
FeatherIcons.video,
color: UIColors.primary_a,
),
title: Text(
'go Live',
style: TextStyle(
color: UIColors.primary_a, fontWeight: FontWeight.w300),
),
),
],
),
);
Widget collapsedPanelBody()
return Container(
child: Column(
children: [
Container(
decoration: BoxDecoration(
color: Colors.black12,
borderRadius: BorderRadius.all(Radius.circular(64.0)),
),
margin: EdgeInsets.only(left: 180.0, right: 180.0, bottom: 10.0),
height: 6.0,
),
ListTile(
onTap: ()
_panelController.open();
,
leading: Icon(
CupertinoIcons.plus_circle,
color: UIColors.primary_a,
),
title: Text(
'upload media',
style: TextStyle(
color: UIColors.primary_a, fontWeight: FontWeight.w300),
),
),
],
),
);
Widget buildImageMedia(PickedFile imgFilePreview)
return Container(
margin: EdgeInsets.only(right: 14.0, left: 14),
child: FractionallySizedBox(
widthFactor: 1.0,
child: ClipRRect(
borderRadius: BorderRadius.circular(12.0),
child: Container(
height: 300,
decoration: BoxDecoration(
color: UIColors.notSelectedColor,
image: DecorationImage(
image: imgFilePreview == null
? Image.file(File("assets/images/empty.png")).image
: Image.file(File(imgFilePreview.path)).image,
fit: BoxFit.cover),
)),
),
),
);
【问题讨论】:
【参考方案1】:我知道问题出在哪里。希望你不介意我改变了一些东西,Stateless Widget 可以用来代替 Stateful Widget,因为你不需要管理它们中的任何状态。
我将您的 AddPostScreen 更改为 StatelessWidget,不过您可以根据需要将其更改回 StatefulWidget。
MediaFilesServices mediaFilesServices = MediaFilesServices();
PanelController _panelController = new PanelController();
String mediaFileType = "NONE";
class AddPostScreen extends StatelessWidget
const AddPostScreen(Key key) : super(key: key);
@override
Widget build(BuildContext context)
return Scaffold(
resizeToAvoidBottomInset: false,
body: SafeArea(
child: Container(
margin: const EdgeInsets.only(top: 20), child: AddPostBody()),
),
);
请注意我声明的新 PickedFile 变量和我添加的方法。我让 cmets 进一步解释。
class AddPostBody extends StatefulWidget
@override
_AddPostBodyState createState() => _AddPostBodyState();
class _AddPostBodyState extends State<AddPostBody>
/// This is the variable that will hold the image the user has selected and is passed to the [buildImageMedia] Widget method
PickedFile userSelectedImage;
/// This is a callback method that will assign the value of [userSelectedImage]
/// from the user's image choice in the ExpandedPanelBody class
void selectedImageHandler(PickedFile selectedImage)
setState(()
userSelectedImage = selectedImage;
);
@override
Widget build(BuildContext context)
return SlidingUpPanel(
padding: EdgeInsets.only(top: 12, left: 12, right: 12),
minHeight: 100,
maxHeight: 310,
backdropEnabled: true,
slideDirection: SlideDirection.UP,
isDraggable: true,
controller: _panelController,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24.0),
topRight: Radius.circular(24.0),
),
panel: ExpandedPanelBody(selectedImageHandler),
collapsed: collapsedPanelBody(),
body: Column(
children: [
Padding(
padding: const EdgeInsets.only(right: 14.0, left: 14, top: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
InkResponse(
onTap: ()
Navigator.pop(context);
,
child: Icon(CupertinoIcons.arrow_left)),
SizedBox(
width: 14,
),
Text('Add post'),
Spacer(),
ElevatedButton(
onPressed: () async ,
style: ButtonStyle(
shadowColor:
MaterialStateProperty.all<Color>(Colors.orangeAccent),
backgroundColor:
MaterialStateProperty.all<Color>(Colors.orangeAccent),
),
child: Text('share'),
)
],
),
),
SizedBox(
height: 18,
),
TextField(
maxLines: null,
keyboardType: TextInputType.multiline,
decoration: InputDecoration(
contentPadding:
EdgeInsets.symmetric(horizontal: 16.0, vertical: 15.0),
border: InputBorder.none,
hintText: 'Say something...',
),
),
SizedBox(
height: 20,
),
Container(
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 3),
decoration: BoxDecoration(
color: Colors.blueAccent,
),
child: TextField(
maxLines: 1,
style: TextStyle(color: Colors.black),
decoration: InputDecoration(
contentPadding:
EdgeInsets.symmetric(horizontal: 16.0, vertical: 15.0),
border: InputBorder.none,
hintText: 'drop your location',
prefixIcon: Icon(
CupertinoIcons.location_north_line_fill,
color: Colors.red,
size: 22,
),
),
),
),
SizedBox(
height: 14,
),
buildImageMedia(userSelectedImage)
],
),
);
对于 ExpandedPanelBody 类,我从 [_imgFromCamera] 和 [_imgFromGallery] 方法中删除了 setState 调用,而是使用了在类构造函数中声明的 [triggerSelectedImage] 函数,该函数将在AddPostBody 类并导致显示用户的图像选择
class ExpandedPanelBody extends StatefulWidget
final Function(PickedFile) triggerSelectedImage;
ExpandedPanelBody(this.triggerSelectedImage);
@override
_ExpandedPanelBodyState createState() => _ExpandedPanelBodyState();
class _ExpandedPanelBodyState extends State<ExpandedPanelBody>
/// The instance of ImagePicker is declared here, where it is used
final ImagePicker imagePicker = ImagePicker();
_imgFromCamera() async
PickedFile image = await imagePicker.getImage(
source: ImageSource.camera, imageQuality: 100);
widget.triggerSelectedImage(image);
mediaFileType = "IMAGE";
_imgFromGallery() async
PickedFile image = await imagePicker.getImage(
source: ImageSource.gallery, imageQuality: 100);
widget.triggerSelectedImage(image);
mediaFileType = "IMAGE";
@override
Widget build(BuildContext context)
void _showPicker(context)
showModalBottomSheet(
context: context,
builder: (context)
return SafeArea(
child: Container(
child: new Wrap(
children: <Widget>[
new ListTile(
leading: new Icon(Icons.photo_library),
title: new Text('Photo Library'),
onTap: ()
setState(()
_imgFromGallery();
);
Navigator.of(context).pop();
),
new ListTile(
leading: new Icon(Icons.photo_camera),
title: new Text('Camera'),
onTap: () async
await _imgFromCamera();
Navigator.of(context).pop();
,
),
],
),
),
);
);
return Container(
margin: EdgeInsets.only(bottom: 13.0),
child: Column(
children: [
Container(
decoration: BoxDecoration(
color: Colors.black12,
borderRadius: BorderRadius.all(Radius.circular(64.0)),
),
margin: EdgeInsets.only(left: 180.0, right: 180.0, bottom: 10.0),
height: 6.0,
),
ListTile(
onTap: () ,
leading: Icon(
CupertinoIcons.plus_circle,
color: Colors.red,
),
title: Text(
'upload media',
style:
TextStyle(color: Colors.black, fontWeight: FontWeight.w300),
),
),
ListTile(
onTap: ()
_showPicker(context);
,
leading: Icon(
Icons.account_balance,
color: Colors.black87,
),
title: Text(
'picture',
style:
TextStyle(color: Colors.black87, fontWeight: FontWeight.w300),
),
),
ListTile(
leading: Icon(
Icons.account_balance,
color: Colors.black87,
),
title: Text(
'video',
style:
TextStyle(color: Colors.black87, fontWeight: FontWeight.w300),
),
),
ListTile(
leading: Icon(
Icons.account_balance,
color: Colors.black87,
),
title: Text(
'audio',
style:
TextStyle(color: Colors.black87, fontWeight: FontWeight.w300),
),
),
ListTile(
leading: Icon(
Icons.account_balance,
color: Colors.orangeAccent,
),
title: Text(
'go Live',
style:
TextStyle(color: Colors.black, fontWeight: FontWeight.w300),
),
),
],
),
);
其他 Widget 方法(collapsedPanelBody 和 buildImageMedia)保持不变。 解决方案是使用回调方法。
【讨论】:
以上是关于颤振 - 从画廊/相机中选择图像不会更新包含所选择图像预览的容器的主要内容,如果未能解决你的问题,请参考以下文章
ImageView 未显示在 android 中使用相机捕获的图像