【问题标题】: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),
            )),
      ),
    ),
  );
}

【问题讨论】:

    标签: flutter dart imagepicker


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

    【讨论】:

      猜你喜欢
      • 2019-07-06
      • 2020-01-22
      • 2021-06-08
      • 2021-01-30
      • 1970-01-01
      • 1970-01-01
      • 2021-05-12
      • 2020-05-20
      • 1970-01-01
      相关资源
      最近更新 更多