问题描述
我正在一个允许用户写帖子的屏幕上工作(类似于 Facebook 的添加帖子),用户可以在其中从图库中选择图像并可以在预览容器中看到该图像,问题是预览不在我选择图像后立即更新,而不是我必须离开屏幕并返回查看预览,我添加了屏幕截图以进一步解释,我还使用向上滑动的面板作为底片,让用户能够选择一种媒体类型(图片、视频、音频...)。
使用的依赖项:
-
sliding_up_panel:^1.0.2
-
图片选择器:^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,style: TextStyle(color: UIColors.primary_a),decoration: InputDecoration(
contentPadding:
EdgeInsets.symmetric(horizontal: 16.0,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,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,ListTile(
leading: Icon(
FeatherIcons.film,title: Text(
'video',ListTile(
leading: Icon(
FeatherIcons.headphones,title: Text(
'audio',ListTile(
leading: Icon(
FeatherIcons.video,title: Text(
'go Live',);
}
}
Widget collapsedPanelBody() {
return Container(
child: Column(
children: [
Container(
decoration: BoxDecoration(
color: Colors.black12,ListTile(
onTap: () {
_panelController.open();
},leading: Icon(
CupertinoIcons.plus_circle,title: Text(
'upload media',style: TextStyle(
color: UIColors.primary_a,);
}
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),)),);
}
解决方法
我知道问题出在哪里了。希望您不介意我更改了一些东西,可以使用无状态小部件代替有状态小部件,因为您不需要管理它们中的任何状态。
我将您的 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 变量和我添加的方法。我留下评论以进一步解释。
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,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,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,ListTile(
leading: Icon(
Icons.account_balance,title: Text(
'video',title: Text(
'audio',color: Colors.orangeAccent,title: Text(
'go Live',);
}
}
其他 Widget 方法(collapsedPanelBody 和 buildImageMedia)保持不变。 解决方案是使用回调方法。