J'ai écrit un exemple de code qui affiche un message si l'utilisateur clique sur le bouton de connexion, lorsqu'il n'y a pas de chaîne à l'intérieur du TextFormField. Il affichera un message sous TextFormField avertissant l'utilisateur de saisir un e-mail. Ce que je veux pouvoir faire, c'est afficher ce message ailleurs, peut-être sous le bouton de connexion.

Idéalement, j'ai un champ de texte de connexion et de mot de passe, et au lieu d'afficher le message sous chaque champ de texte, je souhaite afficher le message sous le bouton de connexion. Toute aide serait appréciée! J'ai regardé dans les contrôleurs de texte, mais je ne sais pas comment implémenter pour que si le validateur passe sans message, il garde les widgets à la même hauteur, mais n'élargit la hauteur que lorsque le validateur échoue affiche le message.

import 'package:flutter/material.dart';

final Color darkBlue = Color.fromARGB(255, 18, 32, 47);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: TextFieldExample(),
        ),
      ),
    );
  }
}

class TextFieldExample extends StatefulWidget {
  @override
  _TextFieldExampleState createState() => _TextFieldExampleState();
}

class _TextFieldExampleState extends State<TextFieldExample> {
  final _formKey = GlobalKey<FormState>();
  String email = '';

  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.symmetric(vertical: 5),
      padding: EdgeInsets.symmetric(horizontal: 15),
      decoration: BoxDecoration(borderRadius: BorderRadius.circular(29)),
      child: Form(
        key: _formKey,
        child: Column(children: <Widget>[
          TextFormField(
            validator: (value) => value.isEmpty ? 'Enter an email' : null,
            onChanged: (value) {
              setState(() => email = value);
            },
            decoration:
                InputDecoration(icon: Icon(Icons.person), hintText: "Email"),
          ),
          Container(
              margin: EdgeInsets.symmetric(vertical: 5),
              child: ClipRRect(
                  borderRadius: BorderRadius.circular(29),
                  child: RaisedButton(
                      padding: EdgeInsets.symmetric(vertical: 10),
                      onPressed: () async {
                        if (_formKey.currentState.validate()) {
                          //sign in;
                        }
                      },
                      child: Text("Login")))),
        ]),
      ),
    );
  }
}

exampleOfPositioning

0
Damanjit Hundal 30 août 2020 à 11:19

2 réponses

Meilleure réponse

Merci à l'aide de LOfG (Dans Flutter, j'essaie de mettre à jour dynamiquement mon widget de colonne basé sur le contrôleur de texte. Comment faire cela?), je me suis inspiré de la réponse.

Le code suivant effectue les opérations suivantes:

  • Utilise StreamBuilder pour reconstruire à chaque nouvel événement
  • Affiche un message sous le bouton de connexion uniquement si la validation échoue, sinon retourne un conteneur vide
  • Lorsque l'utilisateur clique sur le nom d'utilisateur ou le mot de passe, le message disparaît s'il n'est pas vide
  • Utilise snapshot.data pour se connecter à l'utilisateur (peut utiliser Provider avec FirebaseAuth ici)

La raison pour laquelle je voulais cette fonctionnalité, est d'afficher des messages d'erreur à un certain endroit, plutôt que dans les champs de texte.

import 'package:flutter/material.dart';
import 'dart:async';

final Color darkBlue = Color.fromARGB(255, 18, 32, 47);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: TextFieldExample(),
        ),
      ),
    );
  }
}

class TextFieldExample extends StatefulWidget {
  @override
  _TextFieldExampleState createState() => _TextFieldExampleState();
}

class _TextFieldExampleState extends State<TextFieldExample> {
  @override
  void dispose() {
    _username.close();
    _password.close();
    super.dispose();
  }

  String validatorMessage;
  bool validate = false; //will be true if the user clicked in the    login button
  final _username = StreamController<String>(); //stream to   validate   the text
  final _password = StreamController<String>();

  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
          margin: EdgeInsets.symmetric(vertical: 5),
          padding: EdgeInsets.symmetric(horizontal: 15),
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(29),
          ),
          //expose streambuilder to the column widget to use on multiple widgets
          child: StreamBuilder<String>(
              initialData: '',
              stream: _username.stream,
              builder: (context, usernameSnapshot) {
                return StreamBuilder<String>(
                    initialData: '',
                    stream: _password.stream,
                    builder: (context, passwordSnapshot) {
                      return Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: <Widget>[
                          TextField(
                            onChanged: _username.add, //everytime the text changes a new value will be added to the stream
                            decoration: InputDecoration(
                              icon: Icon(Icons.person),
                              hintText: "Email",
                            ),
                          ),
                          TextField(
                            obscureText: true,
                            onChanged: _password.add, //everytime the text changes a new value will be added to the stream
                            decoration: InputDecoration(
                              icon: Icon(Icons.visibility),
                              hintText: "Password",
                            ),
                          ),
                          Container(
                            margin: EdgeInsets.symmetric(vertical: 5),
                            child: ClipRRect(
                              borderRadius: BorderRadius.circular(29),
                              child: RaisedButton(
                                padding: EdgeInsets.symmetric(vertical: 10),
                                //when user presses button, validate turns to true and we check snapshots to get the data for the entries
                                onPressed: () async {
                                  if (usernameSnapshot.data.isNotEmpty && passwordSnapshot.data.isNotEmpty) {
                                    try {
                                      //sign in with usernameSnapshot.data and passwordSnapshot.data
                                    } catch (e) {
                                      print(e);
                                    }
                                    validate = true;
                                  } else {
                                    setState(() {
                                      validate = true;
                                    });
                                  }
                                },
                                child: Text("Login"),
                              ),
                            ),
                          ),
                          if (usernameSnapshot.data.isEmpty &&validate == true) //checking the stream and if the user clicked in the button
                            Container(
                              alignment: Alignment.center,
                              child: Text(
                                'Check your e-mail and password.',
                                style: TextStyle(
                                  color: Colors.red,
                                ),
                              ),
                            )
                          else
                            Container()
                        ],
                      );
                    });
              })),
    );
  }
}

widget

0
Damanjit Hundal 31 août 2020 à 07:32

Pensez à utiliser un Row au lieu de Column

import 'package:flutter/material.dart';
    
    final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
          debugShowCheckedModeBanner: false,
          home: Scaffold(
            body: Center(
              child: TextFieldExample(),
            ),
          ),
        );
      }
    }
    
    class TextFieldExample extends StatefulWidget {
      @override
      _TextFieldExampleState createState() => _TextFieldExampleState();
    }
    
    class _TextFieldExampleState extends State<TextFieldExample> {
      final _formKey = GlobalKey<FormState>();
      String email = '';
    
      Widget build(BuildContext context) {
        return Container(
          margin: EdgeInsets.symmetric(vertical: 5),
          padding: EdgeInsets.symmetric(horizontal: 15),
          decoration: BoxDecoration(borderRadius: BorderRadius.circular(29)),
          child: Form(
            key: _formKey,
            child: Row(children: <Widget>[
              Flexible(
    
                child: TextFormField(
                  validator: (value) => value.isEmpty ? 'Enter an email' : null,
                  onChanged: (value) {
                    setState(() => email = value);
                  },
                  decoration:
                  InputDecoration(icon: Icon(Icons.person), hintText: "Email"),
                ),
              ),
              Expanded(
                flex: 0,
                child: Container(
                  margin: EdgeInsets.symmetric(vertical: 5),
                  child: ClipRRect(
                      borderRadius: BorderRadius.circular(29),
                      child: RaisedButton(
                          padding: EdgeInsets.symmetric(vertical: 10),
                          onPressed: () async {
                            if (_formKey.currentState.validate()) {
                              //sign in;
                            }
                          },
                          child: Text("Login")))),)
    
            ]),
          ),
        );
      }
    }
0
Amon Chowdhury 30 août 2020 à 08:40