Flutter - 从画廊/相机中选择图像不会更新保存所选择图像预览的容器

问题描述

我正在一个允许用户写帖子的屏幕上工作(类似于 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),)),);
}

enter image description here

解决方法

我知道问题出在哪里了。希望您不介意我更改了一些东西,可以使用无状态小部件代替有状态小部件,因为您不需要管理它们中的任何状态。

我将您的 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)保持不变。 解决方案是使用回调方法。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...