什么时候在 Flutter 中使用 setState?

Posted

技术标签:

【中文标题】什么时候在 Flutter 中使用 setState?【英文标题】:When do I use setState in Flutter? 【发布时间】:2018-12-19 08:28:21 【问题描述】:

作为 Flutter 的新手,在 Flutter 应用程序中使用 setState 让我非常困惑。在下面的代码中,布尔值searching 和变量resBodysetState 内部使用。我的问题是为什么只有searchingresBody 在setState 中?为什么其他变量不可变?

var resBody;
bool searching =  false,api_no_limit = false;
String user = null;

Future _getUser(String text) async
setState(() 
  searching = true;
);
user = text;
_textController.clear();
String url = "https://api.github.com/users/"+text;
  var res = await http
      .get(Uri.encodeFull(url), headers: "Accept": 
           "application/json");
  setState(() 
    resBody = json.decode(res.body);
  );

【问题讨论】:

这取决于代码在小部件内部或外部的位置。从您的问题无法得出。 有什么好的资源来了解这个主题吗? docs.flutter.io/flutter/widgets/State/setState.html, flutterbyexample.com/stateful-widget-lifecycle。我确定有几个教程和 YouTube 视频,但我手头没有链接。我发现youtube.com/watch?v=RS36gBEp8OI 的介绍很好。 简单来说,当你改变变量的值时。这是最基本的。 但用户变量不在 setState 内,但它仍在应用程序内工作。那么它在 StatefulWidget 中是如何工作的呢? @goops17 【参考方案1】:

根据docs:

调用 setState 会通知框架此对象的内部状态已发生更改,可能会影响此子树中的用户界面,这会导致框架为此 State 对象安排构建。

因此,如果小部件的状态发生变化您必须调用setState 来触发视图的重建并立即看到新状态所暗示的变化。

无论如何,下面的 sn-ps 是等价的。

第一种情况(直接形成flutter create <myproject>):

class _MyHomePageState extends State<MyHomePage> 
  int _counter = 0;

  void _incrementCounter() 

    setState(() 
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counter++;
    );
  

第二种情况:

class _MyHomePageState extends State<MyHomePage> 
  int _counter = 0;

  void _incrementCounter() 
    _counter++;
    setState(() );
  

我不知道为什么,如果第一种情况是使用setState 的常规方式,我会说是因为代码的可读性。

【讨论】:

问题是,很多人都在问同样的问题,为什么第一种方法是约定。文档说 “通常建议 setState 方法仅用于包装对状态的实际更改,而不是任何可能与更改相关联的计算。” 但是如果我的更改是我从 base64 字符串中读取的大型高清图像?如果图像的读取和转换在此之前完成,我在setState() 中放了什么?【参考方案2】:

当您更改有状态小部件的状态时,请使用 setState() 重新构建小部件及其后代。 您无需在构造函数中调用setState() 或小部件的initState(),因为build() 无论如何都会在之后运行。

也不要在build() 内的同步代码中调用setState()。您不需要在 build() 内重新运行 build()

【讨论】:

我很确定他的问题是关于“为什么我们不在 setState 回调中计算所有内容?”【参考方案3】:

如果你看setState的实现:

  void setState(VoidCallback fn) 
    assert(fn != null);
    assert(...);
    final dynamic result = fn() as dynamic;
    assert(...);
    _element.markNeedsBuild();
  

您会看到它所做的唯一事情是:断言一些事情以帮助您调试它的不正确使用、执行回调以及标记元素以便重建。

因此,从技术上讲,只要调用 setState 就可以更改 setState 回调内部或外部的某些变量。

但是,为了可读性,有很大的不同。重建小部件会影响应用程序的性能,因此您希望尽可能少地这样做。对需要在 setState 回调中重建小部件的变量进行所有(且仅是那些)更改,可以让人们(包括你未来的自己)清楚地知道为什么需要重建。

【讨论】:

【参考方案4】:

当您需要更改任何小部件在屏幕上显示的值时。例如,在应用程序中有一个任务。完成后应将哪些积分添加到“钱包”中。但问题是我们需要刷新应用才能看到“钱包”上的积分。为了解决这个问题,我们在 Button Onpressed() 上使用 Setstate()

例如:

RaisedButton(
 
 onpressed()
    
   setstate()
      
     points+10;

   
 

)

每次按下按钮时,它都会使用“wallet”变量返回的新值刷新小部件,而无需重新启动整个应用程序。

【讨论】:

【参考方案5】:

根据documentations:

一般建议setState方法只用于 包装对状态的实际更改,而不是任何可能的计算 与变化相关联。例如,这里使用的值 build 函数递增,然后将更改写入 磁盘,但只有增量包含在setState

Future<void> _incrementCounter() async 
  setState(() 
    _counter++;
  );
  Directory directory = await getApplicationDocumentsDirectory();
  final String dirName = directory.path;
  await File('$dir/counter.txt').writeAsString('$_counter');

【讨论】:

嗯,但是 _counter++ 增量不是计算本身吗?【参考方案6】:

每当您想要更新小部件树(通常包含一些新数据)时,您都可以调用setState。它只能在State 类中使用。这是简单的实现:

class _MyPageState extends State<MyPage> 
  int _count = 0;

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      body: Center(
        child: RaisedButton(
          onPressed: () => setState(() => _count++),
          child: Text('Count = $_count'),
        ),
      ),
    );
  

【讨论】:

【参考方案7】:

setState() 仅与 statefulWidget 内部颤振一起使用。当setState() 中定义的内容发生变化时,setState() 告诉颤振重建页面。

NOT:setState() 是一个回调函数。

Text(questions[questionIndex])

这里我想根据给定的问题数组更改文本,并且在每次单击按钮时,我想将索引增加 1。

void answerQuesion() 
    setState(() 
      questionIndex = questionIndex + 1;
    );
    print(questionIndex);
  

所以,我必须把这个索引增量放在setState() 中,这样flutter 会在每次更改questionIndex 后重建页面。

【讨论】:

以上是关于什么时候在 Flutter 中使用 setState?的主要内容,如果未能解决你的问题,请参考以下文章

Flutter setStatus为什么有时候需要使用 mounted

Flutter :setState() 或 markNeedsBuild() 在构建期间调用

flutter setState机制

为啥 Flutter 中的 BloCListener 不保存状态,认证过程中出现 setState() 问题?

Flutter:状态类:为啥不在 setState 方法调用之前改变状态变量,而不是在 setState 方法中

Flutter 有状态的代码问题。在 setstate 中放啥?