image_picker取消:Flutter的小部件构建器中的Navigation.pop

时间:2019-02-12 13:37:53

标签: build flutter widget picker navigator

我的Flutter应用程序中存在以下问题:

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

此image_picker-Cancel问题的主要问题是:如何在小部件的构建器中向后导航(即Navigator.pop(context))?

以下引发错误:

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

我知道Widget应该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)

下面是上述要求的详细说明:

实际上,我希望用户一按image_picker Plugin的用法就按下Navigator.pop()。

我意识到snapshot.hashCode更改是一种检测取消按钮是否被用户按下的方法。因此,如果用户按下该“取消”按钮,我只想将navigation.pop返回到我来自的位置;)...我不想再显示或将用户保留在Widget中,但立即返回最初是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);
        },
      )
    ],
  );
}

2 个答案:

答案 0 :(得分:0)

在我看来,您似乎根本没有捕获点击。对我来说,我将在_cancelBtnPressedWidget中返回一个按钮,并在onPressed调用弹出窗口中返回

答案 1 :(得分:0)

我终于找到了答案:

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

现在,image_picker的“取消”按预期工作!

这是整个代码:

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();
  }
}