Flutter:在输入时提交 TextFormField

Flutter: Submit TextFormField on Enter

我正在使用 Flutter 为 Windows 开发桌面应用程序。我想听键盘按键。我制作了一个登录页面,它有两个 TextFormFields(一个用于用户名,另一个用于密码)。 当我按下键盘上的 'enter' 键时,我希望在按下 'Login' 按钮时提交表单。因此,如果我在用户名或密码文本字段中按下 'enter' 键,我希望应用程序的行为就像我按下 'Login' 按钮一样。

这是登录页面的图片: --> Login Page

登录页面代码如下:

import 'package:flutter/material.dart';
import '../../api/services/login_services.dart';
import '../../api/models/login_models.dart';
import '../../constants/constant_methods.dart';
import '../main_page/main_screen.dart';
import '../../constants/constant_variables.dart';

class LoginPage extends StatefulWidget {
  const LoginPage({Key? key}) : super(key: key);

  @override
  _LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
TextEditingController usernameController = TextEditingController();
TextEditingController passwordController = TextEditingController();
late LoginRequestModel loginRequestModel;
bool isUsernameEmpty = false;
bool isPasswordEmpty = false;
bool secure = true;
bool notSecure = false;
bool isLoading = false;

@override
void initState() {
  super.initState();
  loginRequestModel = LoginRequestModel(
    usernameController.text,
    passwordController.text,
  );
}

@override
Widget build(BuildContext context) {
  return SafeArea(
    child: Scaffold(
      body: Container(
        height: MediaQuery.of(context).size.height,
        width: MediaQuery.of(context).size.width,
        decoration: const BoxDecoration(
          gradient: loginPageGradient,
        ),
        child: Center(
          child: SingleChildScrollView(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Image.asset(
                  'assets/images/cloud_soft_logo.png',
                  height: 90,
                ),
                const SizedBox(height: 15),
                Text(
                  'Hoomy\'s Real Estate',
                  style: Theme.of(context).textTheme.headline4,
                ),
                const SizedBox(height: 20),
                Container(
                  width: 310,
                  decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: borderRadius(10.0),
                  ),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: [
                      const SizedBox(height: 20),
                      Text(
                        'Welcome',
                        style: Theme.of(context)
                            .textTheme
                            .headline6!
                            .copyWith(
                                fontWeight: FontWeight.w600,
                                foreground: Paint()..shader = linearGradient),
                      ),
                      Text(
                        'Please Log in to Your Account',
                        style: Theme.of(context).textTheme.headline2,
                      ),
                      const SizedBox(height: 10),
                      loginTextField("Username", "Username can't be empty",
                          usernameController),
                      loginTextField("Password", "Password can't be empty",
                          passwordController),
                      forgetPasswordButton(),
                      loginButton(context),
                      const SizedBox(height: 40),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    ),
  );
}

Widget loginTextField(
  String labelText,
  String errorText,
  TextEditingController controller,
) {
  return SizedBox(
    width: 250,
    child: TextFormField(
      decoration: InputDecoration(
        labelText: labelText,
        errorText: labelText == "Username"
            ? isUsernameEmpty
                ? errorText
                : null
            : isPasswordEmpty
                ? errorText
                : null,
        suffixIcon: labelText == "Username"
             ? IconButton(
                onPressed: () {}, icon: const Icon(Icons.person, size: 20))
            : IconButton(
                onPressed: () {
                  setState(() {
                    secure = !secure;
                  });
                },
                icon: Icon(
                  secure ? Icons.visibility_off : Icons.visibility,
                  size: 20,
                ),
              )),
      obscureText: labelText == "Username" ? notSecure : secure,
      controller: controller,
    ),
  );
 }

 Padding forgetPasswordButton() {
   return Padding(
     padding: const EdgeInsets.all(20).copyWith(right: 30),
     child: Row(
       mainAxisAlignment: MainAxisAlignment.end,
       children: [
         Theme(
           data: ThemeData(splashColor: Colors.transparent),
           child: TextButton(
             child: const Text('Forget Password'),
             onPressed: () {},
           ),
         ),
       ],
     ),
   );
 }

 DecoratedBox loginButton(BuildContext context) {
   return DecoratedBox(
     decoration: BoxDecoration(
       borderRadius: borderRadius(50.0),
       gradient: loginButtonGradient,
     ),
     child: ElevatedButton(
       style: ElevatedButton.styleFrom(
         primary: Colors.transparent,
         fixedSize: const Size(250, 50),
         shape: RoundedRectangleBorder(
           borderRadius: borderRadius(50.0),
       )),
       child: isLoading
         ? CircularProgressIndicator(
             color: Colors.blue[800],
             backgroundColor: Colors.grey,
           )
         : const Text('Login'),
       onPressed: () {
         setState(() {
           usernameController.text.isEmpty
              ? isUsernameEmpty = true
              : isUsernameEmpty = false;
           passwordController.text.isEmpty
              ? isPasswordEmpty = true
              : isPasswordEmpty = false;
         });
         setState(() {
           isLoading = true;
         });
         LoginService.login(usernameController.text, passwordController.text)
             .then((response) {
           setState(() {
             isLoading = false;
           });
           if (response) {
             Navigator.of(context).push(
               MaterialPageRoute(
                 builder: (context) => const MainScreen(),
               ),
             );
           } else if (!response &&
               usernameController.text.isNotEmpty &&
               passwordController.text.isNotEmpty) {
             showErrorDialog(context, "Invalid Username or Password!");
            }
         });
       }),
   );
 }

 Future<dynamic> showErrorDialog(BuildContext context, String dialogContent) {
   return showDialog(
    context: context,
    builder: (BuildContext context) {
      return Center(
        child: SingleChildScrollView(
          scrollDirection: Axis.vertical,
          child: SizedBox(
            width: 350,
            child: AlertDialog(
              backgroundColor: Colors.white,
              shape: RoundedRectangleBorder(borderRadius: borderRadius(10.0)),
              title: Text("Error",
                style: Theme.of(context)
                    .textTheme
                    .bodyText1!
                    .copyWith(color: Colors.red)),
              content: Text(dialogContent,
                style: Theme.of(context)
                    .textTheme
                    .headline2!
                    .copyWith(color: Colors.black87)),
              actions: <Widget>[
                TextButton(
                  child: Text("OK",
                    style: Theme.of(context)
                        .textTheme
                        .bodyText1!
                        .copyWith(color: Colors.blue)),
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                ),
              ],
            ),
          ),
        ),
      );
    },
  );
}
}

添加字段 onFieldSubmittedFormKey:

// Widget attribute

final _formKey = GlobalKey<FormState>();


// Widget method: build()

Form(
  key: _formKey,
  child:
  TextFormField(
    onFieldSubmitted: (value) {
     
      print('ENTER pressed'); 

      // Will trigger validation for ALL fields in Form.
      // All your TextFormFields (email, password) share
      // the SAME Form and thus the SAME _formKey.

      if (_formKey.currentState!.validate()) { 

        print('ALL FIELDS ARE VALID, GO ON ...');

      }
    }
  )
)

文档

https://api.flutter.dev/flutter/material/TextFormField-class.html