В сложном приложении иногда глобальная переменная, «прикрепленная» к виджету, может быть изменена некоторым «ВНЕШНИМ СОБЫТИЕМ», таким как (1) таймер, выполняющийся в другом потоке, или (2) серверный сервер socket.io событие (3) Другое ......
Давайте назовем эту глобальную переменную gintCount, и приложение имеет 3 страницы, а именно:
- Страница 1: «Динамическая» страница, на которой необходимо отобразить последнее значение gintCount.
- Страница 2: Еще одна динамическая страница, на которой необходимо отобразить последнее значение gintCount с полем ввода текста.
- Страница 3: «Статическая» страница, которая ничего не делает при изменении gintCount.
Предположим, что пользователь что-то делает на странице 1 или странице 2, когда и где мы должны «обновить» страницу, чтобы отобразить последнее значение, которое может / может быть изменено событием EXTERNAL?
Я прочитал другие вопросы и ответы в Переполнение стека, и мне сказали, что есть 4 способа управления состоянием флаттера, а именно:
- Использование setState
- Использование ScopedModal
- Использование Rxdart с BLoC
- Использование Redux
Поскольку я новичок во Флаттере, я полностью потерялся со 2 до 4, поэтому я создал приложение, используя no. 1, то есть setState. чтобы продемонстрировать, как мы можем управлять состояниями во флаттере. И я надеюсь, что в будущем я смогу (или кто-то еще) дать ответы, используя нет. От 2 до 4.
Давайте посмотрим на работающее приложение в следующем анимационном GIF:
Снимок экрана Gif Link
Как видно из рисунка, на странице 1 и странице 2 есть глобальный счетчик, а страница 3 - статическая страница.
Позвольте мне объяснить, как я это сделал:
Полный исходный код можно найти по следующему адресу:
https://github.com/lhcdims/statemanagement01
Есть 7 дротиков, а именно:
- gv.dart: хранит все глобальные переменные.
- ScreenVariable.dart: получить высоту / ширину / размер шрифта и т. Д. Вы можете игнорировать это.
- BottomBar.dart: нижняя панель навигации.
- main.dart: основная программа.
- Page1.dart: виджет Page 1.
- Page2.dart: виджет Page 2.
- Page3.dart: виджет Page 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 и определять, должен ли setState вызываться каждые XXX миллисекунд. (XXX в настоящее время установлен на 500)
Я знаю, это глупо!
Как создать похожие примеры с помощью ScopedModal, или Rxdart с BLoC, или Redux?
Прежде чем кто-либо даст какие-либо ответы, имейте в виду, что глобальная переменная gintCount не изменяется НИКАКИМ ВЗАИМОДЕЙСТВИЕМ ПОЛЬЗОВАТЕЛЯ, а ВНЕШНЕЕ СОБЫТИЕ НЕ ЧАСТЬ ЛЮБЫХ ВИДЖЕТОВ . Например, вы можете рассматривать это приложение как:
Приложение ЧАТ, которое «gintCount» - это сообщение, отправленное вам кем-то еще через сервер socket.io. Или
Многопользовательская онлайн-игра, в которой gintCount - это позиция другого игрока на ВАШЕ ЭКРАНЕ, которая контролируется этим игроком с помощью другого мобильного телефона!