【问题标题】:image_picker Cancel: Navigation.pop inside Widget builder for Flutterimage_picker 取消:Flutter 的 Widget 构建器中的 Navigation.pop
【发布时间】:2019-07-06 04:13:25
【问题描述】:

我的 Flutter 应用程序出现以下问题:

为了使image_picker 取消按钮正常工作,我需要能够在用户按下image_picker Plugin 内的取消按钮时使用 Navigate.pop()。

这个 image_picker-Cancel 问题的主要问题是:如何在 Widget 的构建器中导航回来(即Navigator.pop(context))?

以下抛出错误:

  Widget _cancelBtnPressedWidget(BuildContext context) {
    Navigator.pop(context);
  }

我知道一个小部件应该是return 的东西。因此,是否可以伪返回某些东西 - 但实际上将 Navigator.pop() 作为 Widget 内的主要操作? (最好是自动调用,无需额外的用户交互)...

从上面的代码,错误是:

flutter: ══╡ EXCEPTION CAUGHT BY ANIMATION LIBRARY ╞═════════════════════════════════════════════════════════
flutter: The following assertion was thrown while notifying status listeners for AnimationController:
flutter: setState() or markNeedsBuild() called during build.
flutter: This Overlay widget cannot be marked as needing to build because the framework is already in the
flutter: process of building widgets. A widget can be marked as needing to be built during the build phase
flutter: only if one of its ancestors is currently building. This exception is allowed because the framework
flutter: builds parent widgets before children, which means a dirty descendant will always be built.
flutter: Otherwise, the framework might not visit this widget during this build phase.
flutter: The widget on which setState() or markNeedsBuild() was called was:
flutter:   Overlay-[LabeledGlobalKey<OverlayState>#b5c98](state: OverlayState#6a872(entries:
flutter:   [OverlayEntry#cd1e7(opaque: false; maintainState: false), OverlayEntry#43b81(opaque: false;
flutter:   maintainState: true), OverlayEntry#f0b49(opaque: false; maintainState: false),
flutter:   OverlayEntry#b9362(opaque: false; maintainState: true)]))
flutter: The widget which was currently being built when the offending call was made was:
flutter:   FutureBuilder<File>(dirty, state: _FutureBuilderState<File>#d3cac)

.

这里更详细地描述了上述要求的来源:

事实上,我想在用户按下取消按钮后立即使用 Navigator.pop(),就像使用 image_picker Plugin 一样。

我意识到snapshot.hashCode-change 是检测用户按下取消按钮的一种方法。因此,如果用户按下该取消按钮,我只想导航.pop 回到我来自的地方;)...我不想再显示或将用户留在小部件中,但立即返回到最初 Navigate.pushed 的视图。

这是进行图像查找和取消处理的图像选择器部分(即调用_cancelBtnPressedWidget-Widget)。

import 'package:flutter/material.dart';
import 'dart:io';
import 'package:image_picker/image_picker.dart';

File _imageFile;
bool _pickImage = true;
int _hashy = 0;

@override
Widget build(BuildContext context) {
  if (_pickImage) {
    return FutureBuilder<File>(
      future: ImagePicker.pickImage(source: ImageSource.camera),
      builder: (BuildContext context, AsyncSnapshot<File> snapshot) {
        if (snapshot.hasData) {
          _pickImage = false;
          _imageFile = snapshot.data;
          return _showImage(snapshot.data);
        } else {
          // when cancel is pressed, the hashCode changes...
          if ((_hashy != 0) && (snapshot.hashCode != _hashy)) {
            // when cancel pressed
            return _cancelBtnPressedWidget(context);
          }
          _hashy = snapshot.hashCode;
          return Scaffold(
            body: Center(
              child: Text('no image picker available'),
            ),
          );
        }
      },
    );
  } else {
    return _showImage(_imageFile);
  }
}
Widget _cancelBtnPressedWidget(BuildContext context) {
  // requires a return ..... How to overcome this requirement ????
  Navigator.pop(context);
}
Widget _showImage(File imgFile) {
  return Scaffold(
    body: SafeArea(
      child: Stack(
        alignment: AlignmentDirectional.topStart,
        children: <Widget>[
          Positioned(
            left: 0.0,
            bottom: 0.0,
            width: MediaQuery.of(context).size.width,
            height: MediaQuery.of(context).size.height,
            child: Center(
              child: imgFile == null
                  ? Text('No image selected.')
                  : Image.file(imgFile),
            ),
          ),
         // more stacks ... not important here....
        ],
      ),
    ),
  );
}

当然,在 pubspec.yaml 中添加必要的依赖项:

dependencies:
  flutter:
    sdk: flutter
  image_picker: ^0.5.0+3

附加组件:

我尝试添加一个确认对话框(即询问用户“你真的要取消吗”)。

现在,上面的错误消失了。但是,现在image_picker 不断弹出...覆盖此对话框。

我还做错了什么??

Widget _cancelBtnPressedWidget(BuildContext context) {
  return AlertDialog(
    title: Text('Camera Alert'),
    content: Text('Are you sure you want to cancel ?'),
    actions: <Widget>[
      FlatButton(
        child: Text('Close'),
        onPressed: () {
          Navigator.pop(context);
        },
      )
    ],
  );
}

【问题讨论】:

    标签: build flutter widget picker navigator


    【解决方案1】:

    终于找到答案了:

    确实,我可以放置一个确认对话框,并在那里放置必要的return Widget

    现在 image_picker 的 Cancel 正在按预期工作!

    这是完整的代码:

    import 'package:flutter/material.dart';
    import 'dart:io';
    import 'package:image_picker/image_picker.dart';
    
    class MyImagePickerView extends StatefulWidget {
      _MyImagePickerViewState createState() => _MyImagePickerViewState();
    }
    
    class _MyImagePickerViewState extends State<MyImagePickerView> {
      File _imageFile;
      bool _pickImage = true;
      int _hashy = 0;
      bool _cancelPressed = false;
    
      @override
      Widget build(BuildContext context) {
        if (_pickImage) {
          return FutureBuilder<File>(
            future: ImagePicker.pickImage(source: ImageSource.camera),
            builder: (BuildContext context, AsyncSnapshot<File> snapshot) {
              if (snapshot.hasData) {
                _pickImage = false;
                _imageFile = snapshot.data;
                return _showImage(snapshot.data);
              } else {
                // when cancel is pressed, the hashCode changes...
                if ((_hashy != 0) && (snapshot.hashCode != _hashy)) {
                  // when cancel pressed
                  return _cancelBtnPressedWidget(context);
                }
                _hashy = snapshot.hashCode;
                return Scaffold(
                  body: Center(
                    child: Text('no image picker available'),
                  ),
                );
              }
            },
          );
        } else {
          if (_cancelPressed) {
            return _showAlert();
          } else {
            return _showImage(_imageFile);
          }
        }
      }
    
      Widget _cancelBtnPressedWidget(BuildContext context) {
        _cancelPressed = true;
        _pickImage = false;
        return Scaffold(
          body: Center(
            child: Text('Press button to start.'),
          ),
        );
      }
    
      Widget _showImage(File imgFile) {
        StateContainerState container = StateContainer.of(context);
        return Scaffold(
          body: SafeArea(
            child: Stack(
              alignment: AlignmentDirectional.topStart,
              children: <Widget>[
                Positioned(
                  left: 0.0,
                  bottom: 0.0,
                  width: MediaQuery.of(context).size.width,
                  height: MediaQuery.of(context).size.height,
                  child: Center(
                    child: imgFile == null
                        ? Text('No image selected.')
                        : Image.file(imgFile),
                  ),
                ),
                // more stacks ... not important here....
              ],
            ),
          ),
        );
      }
    
      Widget _showAlert() {
        return AlertDialog(
          title: Text('Camera Alert'),
          content: Text('Are you sure you want to cancel the Camera ?'),
          actions: <Widget>[
            FlatButton(
              child: Text('No'),
              onPressed: () {
                setState(() {
                  _pickImage = true;
                  _cancelPressed = false;
                });
              },
            ),
            FlatButton(
              child: Text('Yes'),
              onPressed: () {
                Navigator.pop(context);
              },
            ),
          ],
        );
      }
    
      @override
      void dispose() {
        _myController.dispose();
        super.dispose();
      }
    }
    

    【讨论】:

      【解决方案2】:

      对我来说,您似乎根本没有捕捉到点击。对我来说,我会在 _cancelBtnPressedWidget 和 onPressed 调用 pop 中返回一个按钮。

      【讨论】:

      • 嗯,我不想让用户点击两次。 (即取消按钮是我猜的本机插件的一部分),因此我想在不添加另一个额外按钮的情况下退出小部件。但是,您的回答让我想到我可以添加某种确认对话框,在那里我可以使用onPressed-event。让我试试……
      • 我在原始问题的底部添加了确认对话框的想法。但是,它仍然不起作用,因为这个 image_picker 不断覆盖对话框。 (即对话框只出现一瞥,然后再次被相机插件覆盖而消失)。我还能做些什么来将对话框保持在视图顶部?感谢您对此的任何提示...
      • 啊,我明白了,我对那个插件不是很熟悉。但也许试试这个? pub.dartlang.org/packages/multi_image_picker#-readme-tab-
      猜你喜欢
      • 1970-01-01
      • 2019-04-01
      • 1970-01-01
      • 2019-10-22
      • 2021-06-27
      • 2019-09-25
      • 2021-11-24
      • 2020-12-11
      • 2021-08-09
      相关资源
      最近更新 更多