点击TextField后在路由后重建/重新加载小部件

8

我遇到了一个问题,当使用文本字段时,整个屏幕小部件会重新加载。

如果将此屏幕作为着陆页面加载应用程序,则不会发生此情况。

但是,当从另一页进行路由并单击文本字段时,就会发生重建。

我甚至尝试了一个简单的应用程序,也出现了这个问题。尝试了很多方法,但无法找到解决方案。

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


class Screen1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Screen 1"), // screen title
      ),
      body: new Center(
        child: new Column(
          children: <Widget>[
            new RaisedButton(
              onPressed: () {
                button1(context);
              },
              child: new Text("Go to Screen 2"),
            )
          ],
        ),
      ),
    );
  }
}

class Screen2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("Widget rebuilds");
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Screen 2"),
      ),
      body: new Center(
        child: new Column(
          children: <Widget>[
            new Container(
                height: 350.0,
                child: TextFormField(
                  keyboardType: TextInputType.text,
                  style: TextStyle(fontSize: 16.0, color: Colors.black),
                )),

          ],
        ),
      ),
    );
  }
}

void main() {
  runApp(new MaterialApp(
    home: new Screen1(),
    routes: <String, WidgetBuilder>{
      '/screen2': (BuildContext context) => new Screen2()
    },
  ));
}

void button1(BuildContext context) {
  print("Button 1");
  Navigator.of(context).pushNamed('/screen2');
}

这里的应用程序加载了屏幕1,点击“转到屏幕2”按钮将加载屏幕2和文本字段。点击此字段会弹出键盘,点击键盘上的“完成”按钮并再次聚焦于文本字段将重新构建屏幕。当键盘出现和消失时,这种情况会一直发生。

但是如果将Screen2设置为登陆页面,那么点击文本字段并执行上述相同的过程将不会重新加载小部件。小部件只会构建一次。似乎问题出在从Screen1导航到Screen2时。

 runApp(new MaterialApp(
    home: new Screen2(),
    routes: <String, WidgetBuilder>{
      '/screen2': (BuildContext context) => new Screen2()
    },
  ));
2个回答

5

这是正常的行为,没有问题。实际上,它符合build方法的规格: 可以任意调用,你应该期望如此。

如果这会导致问题,很可能是你的build函数不是纯函数。这意味着它包含副作用,例如http请求或类似操作。

这些操作不应在build方法中执行。更多细节请参见:如何处理不需要的小部件构建?


关于“何时会触发构建”,有几种常见情况:

  • 路由弹出/推入,用于进出动画
  • 屏幕大小调整,通常由键盘出现或方向更改引起
  • 父级小部件重新创建其子级
  • 依赖的InheritedWidget(Class.of(context)模式)发生变化

这种情况下有什么副作用? - dragonfly02
他的示例除了“print”之外没有任何副作用。但是他的应用程序可能会有一些副作用,否则它不会引起问题。 - Rémi Rousselet
他说他可以使用发布的示例代码重现问题。 - dragonfly02
是的,他重现了小部件树的重建事实。这是为了动画目的而在路由更改时发生的,或者由于屏幕调整大小而出现键盘。 - Rémi Rousselet
1
它实际上重建了整个应用程序,所有的initStates都会再次被调用等等。据我所知,这不应该发生。我在Flutter存储库中创建了一个错误票证来解决这个问题。 - Graham
显示剩余3条评论

0

如果你正在使用MediaQuery,键盘的弹出会触发MediaQuery。所有依赖于MediaQuery的小部件都将被重建。有一些包可以替换MediaQuery并处理尺寸而不会引起不必要的重建,例如sizer包。


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