即使插入值后,文本字段仍显示错误消息

时间:2019-12-13 06:01:03

标签: firebase validation flutter dart

我在下面的代码中使用了3个文本字段,我已经验证了每条消息,并且当我单击凸起的按钮时,它会显示错误消息,但是在此之后,当我输入值时,它仍将在屏幕上显示相同的错误消息。屏幕有人可以帮我吗。

这是我的代码:

   import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:toast/toast.dart';

class AddPromo extends StatefulWidget {
  @override
  _AddPromoState createState() => _AddPromoState();
}

class _AddPromoState extends State<AddPromo> {
  final databaseReference = Firestore.instance;

  var priceReductionController = TextEditingController();
  var perReductionController = TextEditingController();
  var promocodeController = TextEditingController();

  bool _validate = false;
  bool _validatePrice = false;
  bool _validatePerReduction = false;
  bool _validatePromoCode = false;


  @override
  void dispose() {
    priceReductionController.dispose();
    perReductionController.dispose();
    promocodeController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Add promo"),backgroundColor: Colors.indigo,),
      body: Column(
        children : <Widget>[
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: TextField(
              keyboardType: TextInputType.number,
            controller: priceReductionController,
              decoration: InputDecoration(labelText: "Price Reduction",
                  errorText: _validatePrice ? 'value cant be empty' : null,
                  border: new OutlineInputBorder(
                borderRadius: const BorderRadius.all(
                  const Radius.circular(0.0),
                ),
                borderSide: new BorderSide(
                  color: Colors.indigo,
                  width: 1.0,
                ),
              )),
            ),
          ),
         Padding(
           padding: const EdgeInsets.all(8.0),
           child: TextField(
             keyboardType: TextInputType.number,


             controller: perReductionController,
             decoration: new InputDecoration(labelText: "Percentage Reduction",
                 errorText: _validatePerReduction ? 'value cant be empty' : null,
                 border: new  OutlineInputBorder(
               borderSide: new BorderSide(color: Colors.indigo)

             )),


            ),
         ),
         Padding(
           padding: const EdgeInsets.all(8.0),
           child: TextField(

             controller: promocodeController,
             decoration: InputDecoration(labelText: "Promo code",
                 errorText: _validatePromoCode ? 'value cant be empty' : null,
                 border: OutlineInputBorder(

             )),

            ),
         ),
          Spacer(flex:2),
        Container(
          width: 350,

          child: RaisedButton(
            color: Colors.indigo,
            onPressed: () async {

              //perform the validation first
              setState(() {
                priceReductionController.text.isEmpty
                    ? _validatePrice = true
                    : _validatePrice = false;
                perReductionController.text.isEmpty
                    ? _validatePerReduction = true
                    : _validatePerReduction = false;
                promocodeController.text.isEmpty
                    ? _validatePromoCode = true
                    : _validatePromoCode = false;
              });

              //verify that all are fine
              if (_validatePrice || _validatePromoCode || _validatePerReduction) {
                return;
              }

              //store the values into the database
              DocumentReference ref = await databaseReference.collection("promo_codes")
                  .add({
                'percentage_reduction': perReductionController.text,
                'price_reduction': priceReductionController.text,
                'promo_code': promocodeController.text,
              });

              Scaffold.of(context)
                  .showSnackBar(SnackBar(content: Text("Promo saved.")))
                  .closed
                  .then((reason) {
                // snackbar is now closed
              });

this is how my code looks like when i click on the button without any fields entered and later i filled it with thesevalues
                  //print(ref.documentID);                 }

the data is getting stored in the database but still the error message is not disapppearing from the scrren

I/OpenGLRenderer( 6519): Initialized EGL, version 1.4
D/OpenGLRenderer( 6519): Swap behavior 2
W/IInputConnectionWrapper( 6519): getTextBeforeCursor on inactive InputConnection
W/IInputConnectionWrapper( 6519): getSelectedText on inactive InputConnection
W/IInputConnectionWrapper( 6519): getTextAfterCursor on inactive InputConnection
W/IInputConnectionWrapper( 6519): beginBatchEdit on inactive InputConnection
W/IInputConnectionWrapper( 6519): endBatchEdit on inactive InputConnection
W/BiChannelGoogleApi( 6519): [FirebaseAuth: ] getGoogleApiForMethod() returned Gms: com.google.firebase.auth.api.internal.zzak@172de21
W/DynamiteModule( 6519): Local module descriptor class for com.google.firebase.auth not found.
I/FirebaseAuth( 6519): [FirebaseAuth:] Loading module via FirebaseOptions.
I/FirebaseAuth( 6519): [FirebaseAuth:] Preparing to create service connection to gms implementation
D/FirebaseAuth( 6519): Notifying id token listeners about user ( 7pkcfF4j1yVLCnMUn9BE0uvjL2s1 ).
I/flutter ( 6519): AsyncSnapshot<String>(ConnectionState.waiting, null, null)
W/DynamiteModule( 6519): Local module descriptor class for providerinstaller not found.
I/DynamiteModule( 6519): Considering local module providerinstaller:0 and remote module providerinstaller:0
W/ProviderInstaller( 6519): Failed to load providerinstaller module: No acceptable module found. Local version is 0 and remote version is 0.
I/dminbookingpag( 6519): The ClassLoaderContext is a special shared library.
I/chatty  ( 6519): uid=10296(m20zero.adminbookingpage) AsyncTask #2 identical 1 line
I/dminbookingpag( 6519): The ClassLoaderContext is a special shared library.
V/NativeCrypto( 6519): Registering com/google/android/gms/org/conscrypt/NativeCrypto's 286 native methods...
W/dminbookingpag( 6519): Accessing hidden field Ljava/nio/Buffer;->address:J (light greylist, reflection)
W/dminbookingpag( 6519): Accessing hidden method Ljava/security/spec/ECParameterSpec;->getCurveName()Ljava/lang/String; (light greylist, reflection)
D/NetworkSecurityConfig( 6519): No Network Security Config specified, using platform default
I/ProviderInstaller( 6519): Installed default security provider GmsCore_OpenSSL
W/dminbookingpag( 6519): Accessing hidden field Ljava/net/Socket;->impl:Ljava/net/SocketImpl; (light greylist, reflection)
W/dminbookingpag( 6519): Accessing hidden method Ldalvik/system/CloseGuard;->get()Ldalvik/system/CloseGuard; (light greylist, linking)
W/dminbookingpag( 6519): Accessing hidden method Ldalvik/system/CloseGuard;->open(Ljava/lang/String;)V (light greylist, linking)
W/dminbookingpag( 6519): Accessing hidden field Ljava/io/FileDescriptor;->descriptor:I (light greylist, JNI)
W/dminbookingpag( 6519): Accessing hidden method Ljava/security/spec/ECParameterSpec;->setCurveName(Ljava/lang/String;)V (light greylist, reflection)
W/dminbookingpag( 6519): Accessing hidden method Ldalvik/system/BlockGuard;->getThreadPolicy()Ldalvik/system/BlockGuard$Policy; (light greylist, linking)
W/dminbookingpag( 6519): Accessing hidden method Ldalvik/system/BlockGuard$Policy;->onNetwork()V (light greylist, linking)
I/flutter ( 6519): 21
I/flutter ( 6519): AsyncSnapshot<String>(ConnectionState.done, 21, null)
W/IInputConnectionWrapper( 6519): getTextBeforeCursor on inactive InputConnection
W/IInputConnectionWrapper( 6519): getSelectedText on inactive InputConnection
W/IInputConnectionWrapper( 6519): getTextAfterCursor on inactive InputConnection
W/IInputConnectionWrapper( 6519): requestCursorAnchorInfo on inactive InputConnection
E/flutter ( 6519): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: Scaffold.of() called with a context that does not contain a Scaffold.
E/flutter ( 6519): No Scaffold ancestor could be found starting from the context that was passed to Scaffold.of(). This usually happens when the context provided is from the same StatefulWidget as that whose build function actually creates the Scaffold widget being sought.
E/flutter ( 6519): There are several ways to avoid this problem. The simplest is to use a Builder to get a context that is "under" the Scaffold. For an example of this, please see the documentation for Scaffold.of():
E/flutter ( 6519):   https://api.flutter.dev/flutter/material/Scaffold/of.html
E/flutter ( 6519): A more efficient solution is to split your build function into several widgets. This introduces a new context from which you can obtain the Scaffold. In this solution, you would have an outer widget that creates the Scaffold populated by instances of your new inner widgets, and then in these inner widgets you would use Scaffold.of().
E/flutter ( 6519): A less elegant but more expedient solution is assign a GlobalKey to the Scaffold, then use the key.currentState property to obtain the ScaffoldState rather than using the Scaffold.of() function.
E/flutter ( 6519): The context used was:
E/flutter ( 6519):   AddPromo
E/flutter ( 6519): #0      Scaffold.of (package:flutter/src/material/scaffold.dart:1316:5)
E/flutter ( 6519): #1      _AddPromoState.build.<anonymous closure> (package:adminbookingpage/screens/addPromo.dart:119:24)
E/flutter ( 6519): <asynchronous suspension>
E/flutter ( 6519): #2      _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:706:14)
E/flutter ( 6519): #3      _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:789:36)
E/flutter ( 6519): #4      GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182:24)
E/flutter ( 6519): #5      TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:486:11)
E/flutter ( 6519): #6      BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:264:5)
E/flutter ( 6519): #7      BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:199:7)
E/flutter ( 6519): #8      PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:467:9)
E/flutter ( 6519): #9      PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:76:12)
E/flutter ( 6519): #10     PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:117:9)
E/flutter ( 6519): #11     _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:379:8)
E/flutter ( 6519): #12     PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:115:18)
E/flutter ( 6519): #13     PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:101:7)
E/flutter ( 6519): #14     GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:218:19)
E/flutter ( 6519): #15     GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:198:22)
E/flutter ( 6519): #16     GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:156:7)
E/flutter ( 6519): #17     GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:102:7)
E/flutter ( 6519): #18     GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:86:7)
E/flutter ( 6519): #19     _rootRunUnary (dart:async/zone.dart:1138:13)
E/flutter ( 6519): #20     _CustomZone.runUnary (dart:async/zone.dart:1031:19)
E/flutter ( 6519): #21     _CustomZone.runUnaryGuarded (dart:async/zone.dart:933:7)
E/flutter ( 6519): #22     _invoke1 (dart:ui/hooks.dart:273:10)
E/flutter ( 6519): #23     _dispatchPointerDataPacket (dart:ui/hooks.dart:182:5)

5 个答案:

答案 0 :(得分:1)

代码中存在三个错误。

  
      
  1. 您在单击按钮时清除了控制器对象
  2.   

不清除控制器对象。我们需要保留控制器对象的值,以维持小部件重建时的文本字段状态。 因此,要解决此问题,请从您的代码中删除以下代码段

        perReductionController.clear();
        priceReductionController.clear();
        promocodeController.clear();
  
      
  1. 您仅保留一个布尔变量来存储所有文本字段的有效状态
  2.   

这意味着,即使您填写了第一个文本字段,单击按钮后,第一个文本字段仍会继续显示错误消息,因为您的第二个文本字段仍为空。要解决此问题,必须保留一个专用于每个文本字段的布尔变量。

以下说明了这些步骤

添加三个变量

   bool _validatePrice = false;
   bool _validatePerReduction = false;
   bool _validatePromoCode = false;

用适当的布尔检查替换每个文本字段中的验证。

例如,对于百分比减少文本字段,请使用以下内容,

  errorText: _validatePerReduction ? 'value cant be empty' : null,

在按钮点击事件功能中替换以下代码段

 priceReductionController.text.isEmpty ? _validate = true :_validate = false;
          perReductionController.text.isEmpty ? _validate = true :_validate = false;
          promocodeController.text.isEmpty ? _validate = true :_validate = false;

  priceReductionController.text.isEmpty
              ? _validatePrice = true
              : _validatePrice = false;
          perReductionController.text.isEmpty
              ? _validatePerReduction = true
              : _validatePerReduction = false;
          promocodeController.text.isEmpty
              ? _validatePromoCode = true
              : _validatePromoCode = false;
  
      
  1. 您将在验证表单之前将值存储到Firestore中。
  2.   

您应该首先验证表单,并且必须确保一切都很好,然后只有您才能继续下一步,即将表单存储到数据库中。

所以您的onPressed函数应该看起来像

 onPressed: () async {

        //perform the validation first
        setState(() {
          priceReductionController.text.isEmpty
              ? _validatePrice = true
              : _validatePrice = false;
          perReductionController.text.isEmpty
              ? _validatePerReduction = true
              : _validatePerReduction = false;
          promocodeController.text.isEmpty
              ? _validatePromoCode = true
              : _validatePromoCode = false;
        });

        //verify that all are fine
        if (_validatePrice || _validatePromoCode || _validatePerReduction) {
          return;
        }

        //store the values into the database
         DocumentReference ref = await databaseReference.collection("promo_codes")
                  .add({
                'percentage_reduction': perReductionController.text,
                'price_reduction': priceReductionController.text,
                'promo_code': promocodeController.text,
              });

        Scaffold.of(context)
            .showSnackBar(SnackBar(content: Text("Promo saved.")))
            .closed
            .then((reason) {
          // snackbar is now closed
        });

        //print(ref.documentID);
      },

为解决您的Toast问题,建议您使用材质设计中的小吃栏。

因此,要使用小吃栏,必须将脚手架窗口小部件移至父窗口小部件,如下所示。

 return Scaffold(
  appBar: AppBar(
    title: Text("Add promo"),
    backgroundColor: Colors.indigo,
  ),
  body: AddPromo(),
);

然后显示小吃店,将您的Toast代码段替换为以下内容

 Scaffold.of(context)
            .showSnackBar(SnackBar(content: Text("Promo saved.")))
            .closed
            .then((reason) {
          // snackbar is now closed
        });

答案 1 :(得分:0)

您的_validate始终为false。请检查设置状态方法并打印_validate。

答案 2 :(得分:0)

您可以使用onFieldSubmitted的TextFormField

 onFieldSubmitted: (value){
                          _setState(() {
                         _validate =  !priceReductionController.text.isEmpty &&
 !perReductionController.text.isEmpty  &&
 !promocodeController.text.isEmpty;
                          });
                        },

但是,除了使用单个_validate之外,最好使用三种不同的布尔状态或完全使用Form https://flutter.dev/docs/cookbook/forms/validation小部件并使用其validate方法。而不是自己处理

也不要清除控制器

perReductionController.clear();
priceReductionController.clear();
promocodeController.clear();

答案 3 :(得分:0)

尝试一下:

const connection = await MongoClient.connect(mongoDBURL);

答案 4 :(得分:0)

Blockquote 表单(键:_formKey,autovalidateMode:AutovalidateMode.onUserInteraction)

您应该将Autovalidate Mode.onUserInteraction属性添加到表单小部件中,以便您每次用户更改某些值时都可以验证表单。