无状态组件
了解无状态组件及其与有状态组件的区别。
无状态与有状态组件对比
在 Flutter 中,我们可以创建 无状态组件 或 有状态组件。严格来说,当一个组件需要与用户交互时,它就是一个有状态组件。
反之亦然:无状态组件永远不会改变,也无法被用户交互。
然而,Flutter 中的“状态”概念并不像上述描述那样简单明了。
在本章入门内容中,我们将深入理解 Flutter 中状态的细微差别。
有状态组件具有内部状态,而无状态组件则没有。
在两种情况下,当输入数据发生变化时,UI 都会重新渲染。但只有在有状态组件中,当其内部状态或本地数据发生变化时,才会触发 UI 重绘。
下图将帮助我们理解 Flutter 中状态管理的核心概念。稍后我们还会进一步解释。
为了更深入地理解无状态与有状态组件的区别,请参考以下示意图:
为了更好地理解,我们先从无状态组件开始。
无状态组件简介
无状态组件是指:当其属性发生变化时,不会重新执行 build() 方法的组件。
如果上述说法难以理解,可以这样思考:
当无状态组件通过构造函数接收外部数据或输入数据时,它会重新运行 build() 方法,从而重新渲染 UI,如上图所示。
然而,对于无状态组件的属性而言,情况并非如此。在无状态组件中,我们可以通过点击 ElevatedButton 来更改其属性。
当我们按下该按钮时,组件的属性会发生变化——例如从 0 变为 1,再从 1 变为 2。
但此时,build() 方法并不会重新执行,因此 UI 也不会重新渲染。
正因为如此,屏幕上看不到任何变化。尽管如此,我们仍希望在实际应用中验证这一点。
以下示例将帮助我们理解这一理论概念。
无状态组件示例
考虑一个简单的例子:用户点击按钮,按钮上方显示的数字随之增加。
每当用户点击按钮,控制台就会输出递增的数字,如下所示:
1I/flutter (5093): 1 2I/flutter (5093): 2 3I/flutter (5093): 3
运行代码后,我们可以清楚地看到输出结果。但这些变化并未反映在界面上。因为 build() 方法没有重新执行,UI 也没有重新渲染。
为什么会这样?
原因在于:用户试图通过内部状态或局部数据来改变状态,但无状态组件无法响应这种变化。
Flutter 的 build() 方法
让我们深入探讨一下 Flutter 中 build() 方法的工作机制。首先,我们要了解 build() 方法会传递一个指向当前组件在组件树中位置的 context 参数。
下面的代码片段有助于我们理解这个关键方法在 Flutter 中的作用:
1import 'package:flutter/material.dart'; 2 3void main() { 4 runApp(OurApp()); 5} 6 7class OurApp extends StatelessWidget { 8 9 Widget build(BuildContext context) { 10 return MaterialApp( 11 title: 'Our App', 12 debugShowCheckedModeBanner: false, 13 home: Scaffold( 14 body: ACenterClass(), 15 ), 16 ); 17 } 18}
我们将使用 ACenterClass 组件的属性和方法。在 ElevatedButton 组件中,一个名为 onPressed 的参数会调用 ACenterClass 中的 callback 函数。
每次点击按钮时,调试控制台都会显示数字递增。但由于无法重新执行 build() 方法,应用无法重新渲染 UI,因此屏幕上看不到数字的变化。
请查看下方的无状态组件示例应用。
-
点击运行按钮以开始构建应用程序。
-
然后点击代码框下方提供的链接。该链接是你的网页应用的存储地址。
请注意:由于正在构建 Flutter 应用,可能需要等待约一分钟才能完成构建。
如果更改未立即显示,请多次刷新链接页面。
No configuration found for ID: 5552e68211c70b57b0b2d8a676f88a596a263318f5a72dd0f684859e80f3018f
最关键的一段代码如下:
1var pressRemoteCount = 0; 2 3void pressRemote() { 4 pressRemoteCount = pressRemoteCount + 1; 5 print(pressRemoteCount); 6}
但由于点击按钮不会重新执行 build() 方法,我们无法在 Text 组件中显示 pressRemoteCount 的值。
1Text( 2 '$pressRemoteCount', 3 style: TextStyle( 4 fontSize: 30.0, 5 color: Colors.blue, 6 ), 7),
如果我们对同一段代码进行重构,将其改为有状态组件,就能让计数正确显示出来。