I am working on this navigation that is a list but it is very inconstant in refreshing. Sometimes it refreshes and appears the new button, and other times it does not show the new button. Have you had this problem before.
This is my page showing the routers list and the pages list:
class RouterTabletPage extends StatefulWidget { // ignore: must_be_immutable
BuildContext? context;
RouterTabletPage({Key? key}) : super(key: key);
@override
_RouterTabletPageState createState() => _RouterTabletPageState();
}
class _RouterTabletPageState extends State<RouterTabletPage> with SingleTickerProviderStateMixin {
final NavigationController _navigationController = IoC.get<NavigationController>()!;
final TopBarWidgetController _topBarController = IoC.get<TopBarWidgetController>()!;
final TerminalLockController _terminalLockController = IoC.get<TerminalLockController>()!;
final PreAlarmController _preAlarmController = IoC.get<PreAlarmController>()!;
final UpdateUiService _updateUiService = IoC.get<UpdateUiService>()!;
PageController? _pageViewController;
@override
initState() {
super.initState();
WidgetsBinding.instance!.addPostFrameCallback((_) async {
await _navigationController.initRoutes();
_navigationController.pageViewController = PageController(initialPage: _navigationController.menuSelectedIndex);
_pageViewController = _navigationController.pageController;
await _navigationController.getTabs(_navigationController.menuSelectedIndex);
await _navigationController.setListenerPage();
_topBarController.initTopBar();
await _navigationController.refreshCycleBtn();
});
}
registerContext() {
widget.context = context;
_terminalLockController.registerContext(context);
_preAlarmController.registerContext(context);
_updateUiService.registerContext(context);
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
registerContext();
return Observer(
builder: (_) {
return Scaffold(
appBar: _topBarController.topBarWidget,
backgroundColor: Theme.of(context).primaryColor,
body: Container(
color: Theme.of(context).primaryColor,
margin: const EdgeInsets.all(0.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
width: 60,
margin: const EdgeInsets.only(top: 0.0),
color: Theme.of(context).primaryColor,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
children: _navigationController.tabs,
),
),
Expanded(
child: Container(
color: Theme.of(context).backgroundColor,
child: PageView(
scrollDirection: Axis.vertical,
controller: _pageViewController,
children: _navigationController.pages,
),
And this is my controller:
@observable
Map<num, PageWithRouter> routers = {};
@observable
String router2 = "";
@observable
int menuSelectedIndex = 0;
@observable
String preloader = "";
@observable
Object? arguments;
@observable
List<Widget> pages = [];
@observable
PageController? pageController;
@observable
List<Widget> tabs = [];
String pathPage = "";
@observable
bool cycleEnable = true;
@observable
bool isCycleStarted = false;
@observable
BuildContext? context;
set pageViewController(PageController _pageController) =>
pageController = _pageController;
@action
Future setRouter(String path) async {
initRoutes();
if (router2 != path) {
pathPage = path;
if (path != pathSplashScreen && path != "") router2 = path;
if (router2 != pathRegister) {
await _treatAuth().then((value) async {
if (!value) {
setMenuSelectedByPath(path);
await initPages();
}
});
}
}
}
@action
Future refreshCurrentPage(List<String> path) async {
if (path.contains(router2)) {
await _treatAuth().then((value) async {
if (!value) {
await initPages();
}
});
}
}
@action
setRouterByHomePage(String path) async {
await setRouter(path);
// log('path'+path);
pageController!.jumpToPage(menuSelectedIndex);
}
@action
Future<void> setRouterByIndex(int index) async {
menuSelectedIndex = index;
String _router = routers[menuSelectedIndex]?.defaultRouter ?? " ";
if (_router != router2) {
await setRouter(_router);
}
}
@action
setArguments(Object? obj) {
arguments = obj;
}
@action
setCycleEnable(bool enable) async{
cycleEnable = enable;
log('noaction'+cycleEnable.toString());
}
@action
setCycleStarted(bool started) {
isCycleStarted = started;
}
@action
setMenuSelectedIndex(int index) {
menuSelectedIndex = index;
}
@action
Widget getByIndex(int index) {
return routers[index]?.page ?? const SizedBox.shrink();
}
@action
Future<Widget> getPage(String router, int pageIdentify) async {
if (pageIdentify != menuSelectedIndex) {
return getByIndex(pageIdentify);
}
for (var item in routers.values) {
if (item.routers.any((r) => r == router) ||
(router == "" && item.defaultRouter == pathHome)) {
item.page = _createPageByRouter(router);
return _createPageByRouter(router);
// TODO: strange behaviour where returning item.page is identified as Widget?
}
}
return const SizedBox.shrink();
}
@action
Widget _createPageByRouter(String router) {
bool isTablet = DeviceType.get().isTablet!;
Widget page = const SizedBox.shrink();
bool isShowSubMenu = true;
log('router'+router);
switch (router) {
case '':
case pathHome:
page = isTablet
? const HomeTabletPage(title: "Home")
: const HomePhonePage(title: "Home");
if (isTablet) isShowSubMenu = false;
break;
case pathMessageReceived:
page = isTablet
? MessageListTabletPage(type: 1)
: MessageListPhonePage(type: 1);
break;
case pathMessageSend:
page = isTablet
? MessageListTabletPage(type: 0)
: MessageListPhonePage(type: 0);
break;
case pathInformation:
page = isTablet ? const InformationTabletPage() : const InformationPhonePage();
break;
case pathExternalDevice:
page =
isTablet ? const ExternalDeviceTabletPage() : const ExternalDevicePhonePage();
break;
case pathErrorCode:
page = isTablet ? const ErrorCodeTabletPage() : const ErrorCodePhonePage();
break;
case pathSetting:
page = isTablet
? SettingTabletPage(title: "Configurações", defaultPage: true)
: SettingPhonePage(title: "Configurações", defaultPage: true);
break;
case pathSettingUnitConfig:
page = isTablet
? SettingTabletPage(title: "Configurações", defaultPage: false)
: SettingPhonePage(title: "Configurações", defaultPage: false);
break;
case pathOperationCycle:
if (isTablet) {
return const OperationCycleMasterTablet();
} else {
if (isCycleStarted) {
page = const OperationCyclePhonePage();
} else {
page = const OperationCycleHomePhonePage();
}
}
if (isTablet) isShowSubMenu = false;
break;
default:
page = const SizedBox.shrink();
break;
}
if (isShowSubMenu && isTablet) {
return Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
IoC.get<SubMenu>()!,
Expanded(
child: page,
),
],
);
}
return page;
}
@action
Future initPages() async {
initRoutes();
pages = [];
for (final key in routers.keys) {
if (routers[key]!.defaultRouter ==
NavigationControllerBase.pathOperationCycle &&
cycleEnable == false) {
setMenuSelectedIndex(0);
setRouterByHomePage(pathHome);
}
final page = await getPage(router2, key as int);
pages.add(page);
log('initPages'+ pages.toString());
}
}
Future<bool> _treatAuth() async {
final authStatusService = IoC.get<AuthStatusService>()!;
final authStatus = await authStatusService.syncData();
if (authStatus != null && authStatus.requireLogin) {
pathPage = pathAuthentication;
router2 = pathAuthentication;
IoC.get<NavigationService>()!.navigateWithoutHistory(pathAuthentication);
return true;
}
return false;
}
@action
Future initRoutes() async {
if (DeviceType.get().isPhone!) {
routers = <num, PageWithRouter>{
0: PageWithRouter([pathHome], null),
1: PageWithRouter([pathInformation, pathExternalDevice], null),
2: PageWithRouter([pathErrorCode], null),
3: PageWithRouter([pathSetting, pathSettingUnitConfig], null),
};
if (cycleEnable) {
routers.addAll({
4: PageWithRouter([pathOperationCycle], null),
5: PageWithRouter([pathMessageReceived, pathMessageSend], null),
});
} else {
routers.addAll({
4: PageWithRouter([pathMessageReceived, pathMessageSend], null),
});
}
} else {
routers = <num, PageWithRouter>{
0: PageWithRouter([pathHome], null),
1: PageWithRouter([pathMessageReceived, pathMessageSend], null),
2: PageWithRouter([pathInformation, pathExternalDevice], null),
3: PageWithRouter([pathErrorCode], null),
4: PageWithRouter([pathSetting, pathSettingUnitConfig], null),
};
if (cycleEnable == true) {
routers.addAll({
5: PageWithRouter([pathOperationCycle], null)
});
log('initRoutes'+routers.toString());
}
}
}
@action
Future refreshCycleBtn() async {
tabs = [];
if (DeviceType.get().isPhone!) {
tabs = List.from(tabs..addAll(await getTabsPhone(menuSelectedIndex)));
} else {
tabs = List.from(tabs..addAll(await getTabs(menuSelectedIndex)));
log('tabs'+tabs.toString());
}
await initRoutes();
await _verifyOperationCycleEnabled();
if (cycleEnable == false) {
if (menuSelectedIndex > routers.length - 1) {
await setRouterByIndex(0);
pageController!.jumpToPage(0);
} else {
await initPages();
}
} else {
await initPages();
pages = [];
List<Widget> newPages = [];
for (final key in routers.keys) {
final page = await getPage(router2, key as int);
newPages.add(page);
pages = List.from(pages..addAll(newPages));
}
if (menuSelectedIndex > routers.length - 1) {
await setRouterByIndex(0);
} else {
await initRoutes();
await initPages();
}
}
}
Future<List<Widget>> getTabs(int index) async {
List<Widget> newTabs = [];
//await _menuIconsController.refreshAllBadge();
for (final key in routers.keys) {
if (routers[key]!.defaultRouter ==
NavigationControllerBase.pathOperationCycle && cycleEnable == false) {
continue;
}
newTabs.add(
SizedBox(
height: 70,
child: TextButton(
style: TextButton.styleFrom(
backgroundColor: index == key ? AppTheme.theme.colorScheme.secondary : Colors.transparent,
shape: const ContinuousRectangleBorder(
borderRadius: BorderRadius.all(Radius.zero),
),
),
child: _getTabIcon(routers[key]!.defaultRouter),
onPressed: () async {
await setRouterByIndex(key as int);
pageController!.jumpToPage(key);
},
),
),
);
}
tabs = [];
tabs = List.from(tabs..addAll(newTabs));
log('newtabs'+newTabs.toString());
return newTabs;
}
Widget _getTabIcon(String router) { // os cases com icons
switch (router) {
case '': //setado para home
return const Icon(
Icons.home,
color: Colors.white,
size: 30,
);
case NavigationControllerBase.pathHome:
return const Icon(
Icons.home,
key: Key('btn_home_page'),
color: Colors.white,
size: 30,
);
case NavigationControllerBase.pathMessageReceived:
var icon = const Icon(
Icons.mail,
key: Key('btn_message_page'),
color: Colors.white,
size: 30,
);
return Observer(builder: (_) {
if (_menuIconsController.countMessages == 0) return icon;
return Badge(
badgeContent: Text(
_menuIconsController.countMessages.toString(),
style: const TextStyle(
color: Colors.white,
),
),
child: icon,
);
});
case NavigationControllerBase.pathInformation:
return const Icon(
Icons.info,
key: Key('btn_information_page'),
color: Colors.white,
size: 30,
);
case NavigationControllerBase.pathErrorCode:
var icon = const Icon(
Icons.warning,
key: Key('btn_erro_code_page'),
color: Colors.white,
);
return Observer(builder: (_) {
if (_menuIconsController.countUccErrors == 0) return icon;
return Badge(
badgeContent: Text(
_menuIconsController.countUccErrors.toString(),
style: const TextStyle(
color: Colors.white,
),
),
child: icon,
);
});
case NavigationControllerBase.pathSetting:
var icon = const Icon(
Icons.settings,
key: Key('btn_setting_page'),
color: Colors.white,
size: 30,
);
if (!_menuIconsController.showBadgetSetting!) return icon;
return Observer(builder: (_) {
return Badge(
badgeContent: const Padding(
padding: EdgeInsets.all(2.0),
child: Text(
"!",
style: TextStyle(
color: Colors.white,
),
),
),
child: icon,
);
});
case NavigationControllerBase.pathOperationCycle:
return const Icon(
MtfIcons.operationalCycleHomeMenu,
key: Key('btn_operation_cycle'),
color: Colors.white,
size: 30,
);
default:
return const SizedBox.shrink();
}
}
Future<List<Widget>> getTabsPhone(int index) async {
List<Widget> newTabs = [];
//await _menuIconsController.refreshAllBadge();
for (final key in routers.keys) {
if (routers[key]!.defaultRouter ==
NavigationControllerBase.pathOperationCycle &&
cycleEnable == false) {
continue;
}
newTabs.add(
Expanded(
child: TextButton(
style: TextButton.styleFrom(
primary: index == key ? AppTheme.theme.colorScheme.secondary : Colors.transparent,
),
onPressed: () async {
await setRouterByIndex(key as int);
pageController!.jumpToPage(key);
},
child: _getTabIcon(routers[key]!.defaultRouter),
),
),
);
}
tabs = [];
tabs = List.from(tabs..addAll(newTabs));
return newTabs;
}
Future setListenerPage() async {
// TODO: refactor this lambda;
var listener = () async { // ignore: prefer_function_declarations_over_variables
if (DeviceType.get().isPhone!) {
await getTabsPhone(menuSelectedIndex);
} else {
await getTabs(menuSelectedIndex);
}
double page = pageController!.page!;
await setRouterByIndex(page.round());
};
pageController!.removeListener(listener);
pageController!.addListener(listener);
}
void setMenuSelectedByPath(String? path) { //init menuSeletecteIndex =id
routers.forEach((id, page) {
if (page.routers.any((r) => r == path)) {
menuSelectedIndex = id as int;
}
});
}
Future _verifyOperationCycleEnabled() async { //setCycleEnable true || false
final _operationCycleService = IoC.get<OperationCycleService>()!;
final operationCycle = await _operationCycleService.syncData();
if (operationCycle == null) {
setCycleEnable(false);
} else {
setCycleEnable(true);
}
}
}
class PageWithRouter {
List<String> routers;
Widget? page;
String get defaultRouter => routers.first;
PageWithRouter(this.routers, this.page);
}
I have already tried changing WidgetsBinding to Future.delay and tried initiating different ways inside refreshCycleBtn().