点击Flutter中的TextField小部件时,小部件是否会重新构建?

21

我相信这是初学者的错误,但我似乎无法解决这个问题。在下面的应用程序中,当第二个路由中的文本字段被点击时,键盘会打开并立即关闭。经过仔细调查,似乎每当小部件获得焦点时,它就会被重建,导致路由重置,使用户无法输入文字。

当我从表单中删除“key”时,问题就不会发生。这不是长期的解决方案,因为我需要“key”来验证表单。

有什么想法吗?


注:翻译时保留了原文中的html标签。
import 'package:flutter/material.dart';


void main() => runApp(MyApp());

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My app',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.lightBlue,
        fontFamily: 'Nunito',
      ),
      home: LoginPage(),
    );
  }
}


class LoginPage extends StatefulWidget {

  @override
  LoginPageState createState() {
    return new LoginPageState();
  }
}

class LoginPageState extends State<LoginPage> {

  Widget build(BuildContext context) {

    final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();

    final registerButton = Padding(
      padding: EdgeInsets.symmetric(vertical: 16.0),
      child: RaisedButton(
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(24),
        ),
        onPressed: () {
          Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => SecondPage()),
          );
        },
        child: Text('Register Now', style: TextStyle(color: Colors.white)),
      ),
    );

    // Now load the main login page
    return Scaffold(
      backgroundColor: Colors.white,
      key: _scaffoldKey,
      body: Center(
        child: ListView(
          shrinkWrap: true,
          children: <Widget>[
            registerButton,
          ],
        ),
      ),
    );
  }
}


class SecondPage extends StatelessWidget {

  @override
  Widget build(BuildContext context) {

    final emailController =  TextEditingController();
    final _formKey = GlobalKey<FormState>();

    final email = TextFormField(
      keyboardType: TextInputType.emailAddress,
      controller: emailController,
      autofocus: false,
      decoration: InputDecoration(
        hintText: 'Email',
        contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
        border: OutlineInputBorder(borderRadius: BorderRadius.circular(32.0)),
      ),
    );

    return Scaffold(
      appBar: AppBar(
        title: Text('Second page'),
      ),
      body: Center(
        child: Form(
          key: _formKey,
          child: email,
        ),
      ),
    );
  }
}

我有类似的问题,在调试程序时,我的情况是Page Widget由Navigator.push触发,似乎每次我点击TextField时Navigator.push都会重新执行Page Widget的创建。你尝试在Navigator.push上设置断点了吗? - aristo_sh
5个回答

19

您需要在build方法之外将_formKey声明为静态变量。


1
谢谢您的回复,当_formkey在build方法之外声明时,发生相同的行为。 - Pat J
1
使其静态化。 - Viren V Varasadiya
1
谢谢,那个方法可行!您能解释一下为什么将它声明为静态的会有所不同吗? - Pat J
我对此不是很了解。几天前我遇到了同样的问题,当时在GitHub上找到了解决方案。我认为Flutter尝试重新声明formkey,这可能会导致混淆。 - Viren V Varasadiya

12
如果你的屏幕依赖于MediaQuery至少有一个小部件依赖于MediaQuery,键盘弹出会改变屏幕的大小,这会触发mediaQuery并导致重构...在这种情况下,避免使用mediaQuery,而是使用(sizer包)获取您的尺寸 https://pub.dev/packages/sizer

这是我的情况。 - Abdol Hussain Mozaffari
没错,MediaQuery正在帮我解决问题...谢谢! - Chris
谢谢。它解决了不必要的构建。 - alperefesahin

6

当我们点击TextField时,Flutter会自动调用build()方法,因此您在build()方法中初始化的所有对象都将被重新创建。

这就是为什么总是要创建在Form()中使用的全局变量,例如:

final emailController =  TextEditingController();
final _formKey = GlobalKey<FormState>();

你能提供如何使用全局变量 _formKey 来操作 TextField 的示例吗? - Saf

2
当你点击 TextField 时,Flutter 会重新声明你的 _formKey 变量,导致 widget 被重构。所以你需要在 build 方法外部声明 _formKey 变量,这样 Flutter 就不会重新声明你的变量。"最初的回答"。

0

当我们点击文本字段并打开键盘时,媒体查询的这种奇怪行为会在堆栈中再次重建一个相同的页面。

MaterialApp( useInheritedMediaQuery: true,)

将useInheritedMediaQuery设置为true。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接