在复杂的应用程序中,有时“附加”到小部件的全局变量可以通过一些“外部事件”来更改,例如(1)在另一个线程中运行的计时器,或(2)socket.io 服务器发出事件(3 ) 其他 ......
我们称这个全局变量为 gintCount,应用程序有 3 个页面,即:
- 第 1 页:需要显示 gintCount 最新值的“动态”页面。
- 第 2 页:另一个需要显示 gintCount 最新值的“动态”页面,带有文本输入字段。
- 第 3 页:当 gintCount 更改时不执行任何操作的“静态”页面。
假设用户在第 1 页或第 2 页中做某事,我们应该何时何地“刷新”页面以显示可能/可能被外部事件更改的最新值?
我在 Stack Overflow 中阅读了其他问答,据说 Flutter 的状态管理有 4 种方式,它们分别是:
- 使用 setState
- 使用 ScopedModal
- 将 Rxdart 与 BLoC 一起使用
- 使用 Redux
由于我是 Flutter 的新手,我完全迷失在 2 到 4 中,所以我使用 no 构建了一个应用程序。1,即setState。展示我们如何在颤动中管理状态。我希望,在未来,我能够(或其他人)通过使用 no 来提供答案。2到4。
让我们看一下以下动画 gif 中正在运行的应用程序:
在gif中可以看到,在Page 1和Page 2中有一个Global Counter,而Page 3是一个静态Page。
让我解释一下我是如何做到的:
完整的源代码可以在以下地址找到:
https://github.com/lhcdims/statemanagement01
共有 7 个 dart 文件,它们分别是:
- gv.dart:存储所有全局变量。
- ScreenVariable.dart:获取屏幕的高度/宽度/字体大小等。你可以忽略它。
- BottomBar.dart:底部导航栏。
- main.dart:主程序。
- Page1.dart:第 1 页小部件。
- Page2.dart:第 2 页小部件。
- Page3.dart:第 3 页小部件。
我们先来看看gv.dart:
import 'package:flutter/material.dart';
class gv {
static var gstrCurPage = 'page1'; // gstrCurPage stores the Current Page to be loaded
static var gintBottomIndex = 0; // Which Tab is selected in the Bottom Navigator Bar
static var gintCount = 0; // The Global Counter
static var gintCountLast = 0; // Check whether Global Counter has been changed
static var gintPage1Counter = 0; // No. of initState called in Page 1
static var gintPage2Counter = 0; // No. of initState called in Page 2
static var gintPage3Counter = 0; // No. of initState called in Page 3
static bool gbolNavigatorBeingPushed = false; // Since Navigator.push will called the initState TWICE, this variable make sure the initState only be called once effectively!
static var gctlPage2Text = TextEditingController(); // Controller for the text field in Page 2
}
我如何模拟更改全局变量 gv.gintCount 的外部事件?
好的,我在 main.dart 中创建了一个线程,它运行计时器'funTimerExternal',并每秒增加 gv.gintCount!
现在,让我们看一下 main.dart:
// This example tries to demonstrate how to maintain the state of widgets when
// variables are changed by External Event
// e.g. by a timer of another thread, or by socket.io
// This example uses setState and a timer to maintain States of Multiple Pages
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import "package:threading/threading.dart";
import 'gv.dart';
import 'Page1.dart';
import 'Page2.dart';
import 'Page3.dart';
import 'ScreenVariables.dart';
void main() { // Main Program
var threadExternal = new Thread(funTimerExternal); // Create a new thread to simulate an External Event that changes a global variable defined in gv.dart
threadExternal.start();
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
.then((_) {
sv.Init(); // Init Screen Variables
runApp(new MyApp()); // Run MainApp
});
}
void funTimerExternal() async { // The following function simulates an External Event e.g. a global variable is changed by socket.io and see how all widgets react with this global variable
while (true) {
await Thread.sleep(1000);
gv.gintCount += 1;
}
}
class MyApp extends StatefulWidget { // Main App
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
initState() {
super.initState();
var threadTimerDefault = new Thread(funTimerDefault); // *** Set funTimerDefault, to listen to change of Vars ***
threadTimerDefault.start();
}
void funTimerDefault() async {
while (true) {
await Thread.sleep(500); // Allow this thread to run each XXX milliseconds
if (gv.gintCount != gv.gintCountLast) { // Check any changes need to setState here, if anything changes, setState according to gv.gstrCurPage
gv.gintCountLast = gv.gintCount;
switch (gv.gstrCurPage) {
case 'page1':
setState(() {}); // Page 1: Refresh Page
break;
case 'page2':
setState(() {}); // Page 2: Refresh Page
break;
default: // Page 3: Do Nothing, since Page 3 is static
break;
}
}
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false, // Disable Show Debug
home: MainBody(),
);
}
}
class MainBody extends StatefulWidget {
@override
_MainBodyState createState() => _MainBodyState();
}
class _MainBodyState extends State<MainBody> {
@override
initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
switch (gv.gstrCurPage) { // Here Return Page According to gv.gstrCurPage
case 'page1':
return ClsPage1();
break;
case 'page2':
return ClsPage2();
break;
default:
return ClsPage3();
break;
}
return ClsPage1(); // The following code will never be run, to avoid warning only
}
}
如您所见,我使用另一个计时器“funTimerDefault”来跟踪 gv.gintCount 的变化,并确定是否应该每 XXX 毫秒调用一次 setState。(XXX 目前设置为 500)
我知道,这很愚蠢!
如何使用 ScopedModal 或 Rxdart 与 BLoC 或 Redux 来创建类似的示例?
在任何人提供任何答案之前,请记住全局变量 gintCount 不会被任何用户交互更改,而是不属于任何小部件的外部事件。例如,您可以将此应用程序视为:
一个聊天应用程序,'gintCount' 是其他人通过 socket.io 服务器发送给你的消息。或者,
多人在线游戏,'gintCount' 是另一个玩家在您的屏幕中的位置,由该玩家使用另一部手机控制!