在复杂的应用程序中,有时可以通过某些“外部事件”更改“附加”到小部件的全局变量,例如(1)在另一个线程中运行的计时器,或(2)socket.io服务器发出事件(3)其他...
我们将此全局变量称为gintCount,应用程序有3个页面,即:
假设用户正在执行第1页或第2页中的操作,我们何时何地应该“刷新”页面以显示EXTERNAL事件可能/可能更改的最新值?
我阅读了Stack Overflow中的其他问答,据说Flutter的状态管理有4种方法,分别是:
由于我是Flutter的新手,所以我在2到4中完全迷失了,所以我使用no构建了一个应用程序。 1,即setState。演示如何管理抖动中的状态。我希望将来,我能够(或其他人)使用否提供答案。 2至4。
下面的动画gif中让我们看一下正在运行的应用程序:
您可以在gif中看到,第1页和第2页有一个全局计数器,第3页是静态页。
让我解释一下我是怎么做到的:
完整的源代码可以在以下地址找到:
https://github.com/lhcdims/statemanagement01
有7个飞镖文件,它们是:
让我们首先看一下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不会由任何用户交互更改,而是由不属于任何小部件的外部事件更改。例如,您可以将此应用视为:
一个CHAT应用程序,“ gintCount”是其他人通过socket.io服务器发送给您的消息。或者,
一个多人在线游戏,“ gintCount”是您屏幕中另一位玩家的位置,该位置由该玩家使用另一部手机控制!
答案 0 :(得分:1)
对于您的需要,您绝对应该更多地研究您所讨论的可用体系结构。 例如,REDUX完全符合解决问题所需的条件。
我只能建议您看一下REDUX的此演示文稿: https://www.youtube.com/watch?v=zKXz3pUkw9A
即使对于这种模式的新手来说,这也是非常容易理解的(我不久前还没来过)。 完成后,请查看http://fluttersamples.com/
此网站包含许多不同模式的示例项目。这可以帮助您入门
答案 1 :(得分:0)
我已经使用Redux重写了示例,让我们看一下屏幕截图:
如您所见,第1页中有2个计数器,变量存储在gv.dart中
在gv.dart(存储所有全局变量的dart文件)中,我创建了一个“存储”:
import 'package:flutter/material.dart';
import 'package:redux/redux.dart';
import 'dart:convert';
enum Actions { Increment } // The reducer, which takes the previous count and increments it in response to an Increment action.
int counterReducer(int intSomeInteger, dynamic action) {
if (action == Actions.Increment) {
// print('Store Incremented: ' + (intSomeInteger + 1).toString());
return intSomeInteger + 1;
}
return intSomeInteger;
}
class gv {
static Store<int> storeState = new Store<int>(counterReducer, initialState: 0);
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 gintGlobal1 = 0; // Global Counter 1
static var gintGlobal2 = 0; // Global Counter 2
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
}
同样,在main.dart中,我创建了另一个线程“ funTimerExternal”来模拟一个“外部事件”,某些全局变量会被socket.io服务器发出事件所改变。
在“ funTimerExternal”的结尾处,在更改了一些变量之后,我调用了:
gv.storeState.dispatch(Actions.Increment);
仅当用户浏览第1页或第2页时才能更改Page1或Page2的状态。(即,当用户浏览第3页时不执行任何操作)
main.dart:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:threading/threading.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.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.gintGlobal1 += 1;
gv.gintGlobal2 = (gv.gintGlobal1 / 2).toInt();
gv.storeState.dispatch(Actions.Increment);
}
}
class MyApp extends StatefulWidget { // Main App
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return StoreProvider(
store: gv.storeState,
child: MaterialApp(
debugShowCheckedModeBanner: false, // Disable Show Debug
home: MainBody(),
),
);
}
}
class MainBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
switch (gv.gstrCurPage) {
// Here Return Page According to gv.gstrCurPage
case 'page1':
gv.gintPage1Counter += 1;
return StoreConnector<int, int>(
builder: (BuildContext context, int intTemp) {
return new ClsPage1(intTemp);
}, converter: (Store<int> sintTemp) {
return sintTemp.state;
},);
break;
case 'page2':
gv.gintPage2Counter += 1;
return StoreConnector<int, int>(
builder: (BuildContext context, int intTemp) {
return new ClsPage2(intTemp);
}, converter: (Store<int> sintTemp) {
return sintTemp.state;
},);
break;
default:
return ClsPage3();
break;
}
}
}
与网上提供的示例不同,“存储”不是在main.dart内声明的,而是在另一个dart文件gv.dart内声明的。即我将用户界面和数据分开了!
完整的示例可以在这里找到:
https://github.com/lhcdims/statemanagement02
再次感谢Miiite和shadowsheep的帮助。