Skip to content

added support for showMenu which allows user to show bottom menu outs… #2039

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions modules/ensemble/lib/framework/view/bottom_nav_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import 'package:ensemble/framework/menu.dart';
import 'package:flutter/material.dart';

/// Singleton controller that stores and provides access to built bottom navigation widgets
class GlobalBottomNavController extends ChangeNotifier {
static GlobalBottomNavController? _instance;

// Private constructor
GlobalBottomNavController._();

// Singleton instance getter
static GlobalBottomNavController get instance {
_instance ??= GlobalBottomNavController._();
return _instance!;
}

// Stored widgets
Widget? _storedBottomNavWidget;
Widget? _storedFloatingActionButton;
FloatingActionButtonLocation? _storedFloatingActionButtonLocation;

// Getters
Widget? get bottomNavWidget => _storedBottomNavWidget;
Widget? get floatingActionButton => _storedFloatingActionButton;
FloatingActionButtonLocation? get floatingActionButtonLocation =>
_storedFloatingActionButtonLocation;

set bottomNavWidget(Widget? widget) {
_storedBottomNavWidget = widget;
notifyListeners();
}

set floatingActionButton(Widget? widget) {
_storedFloatingActionButton = widget;
notifyListeners();
}

set floatingLocation(FloatingActionButtonLocation? widget) {
_storedFloatingActionButtonLocation = widget;
notifyListeners();
}


/// Unregister widgets when PageGroup is disposed
void unregisterBottomNavWidgets() {
_storedBottomNavWidget = null;
_storedFloatingActionButton = null;
_storedFloatingActionButtonLocation = null;

notifyListeners();
}

/// Get bottom nav widget
Widget? getBottomNavWidget() {
return _storedBottomNavWidget != null
? Container(child: _storedBottomNavWidget)
: null;
}

/// Get floating action button
Widget? getFloatingActionButton() {
return _storedFloatingActionButton != null
? Container(child: _storedFloatingActionButton)
: null;
}

@override
void dispose() {
super.dispose();
}
}

/// Extension to make it easier to access the controller
extension GlobalBottomNavControllerExtension on BuildContext {
GlobalBottomNavController get globalBottomNav =>
GlobalBottomNavController.instance;
}
37 changes: 30 additions & 7 deletions modules/ensemble/lib/framework/view/bottom_nav_page_group.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import 'package:ensemble/util/utils.dart';
import 'package:ensemble/framework/widget/icon.dart' as ensemble;
import 'package:ensemble/widget/helpers/controllers.dart';
import 'package:flutter/material.dart';
import 'package:ensemble/framework/view/bottom_nav_controller.dart';

class BottomNavBarItem {
BottomNavBarItem({
Expand All @@ -27,6 +28,7 @@ class BottomNavBarItem {
this.switchScreen = true,
this.onTap,
this.onTapHaptic,
this.page,
});

Widget icon;
Expand All @@ -38,6 +40,7 @@ class BottomNavBarItem {
bool? switchScreen;
EnsembleAction? onTap;
String? onTapHaptic;
String? page;
}

enum FloatingAlignment {
Expand Down Expand Up @@ -152,6 +155,8 @@ class _BottomNavPageGroupState extends State<BottomNavPageGroup>

@override
void dispose() {
// Unregister the bottom nav widgets when the PageGroup is disposed
GlobalBottomNavController.instance.unregisterBottomNavWidgets();
if (widget.menu.reloadView == false) {
controller.dispose();
}
Expand Down Expand Up @@ -221,18 +226,27 @@ class _BottomNavPageGroupState extends State<BottomNavPageGroup>
Utils.getColor(widget.menu.runtimeStyles?['notchColor']) ??
Theme.of(context).scaffoldBackgroundColor;

// Build the widgets
Widget? bottomNavBar = _buildBottomNavBar();
Widget? floatingButton = _buildFloatingButton();
FloatingActionButtonLocation? floatingLocation =
floatingAlignment == FloatingAlignment.none
? null
: floatingAlignment.location;
WidgetsBinding.instance.addPostFrameCallback((_) {
GlobalBottomNavController.instance.bottomNavWidget = bottomNavBar;
GlobalBottomNavController.instance.floatingActionButton = floatingButton;
GlobalBottomNavController.instance.floatingLocation = floatingLocation;
});
return PageGroupWidgetWrapper(
reloadView: widget.menu.reloadView,
scopeManager: widget.scopeManager,
child: Scaffold(
resizeToAvoidBottomInset: true,
backgroundColor: notchColor,
bottomNavigationBar: _buildBottomNavBar(),
floatingActionButtonLocation:
floatingAlignment == FloatingAlignment.none
? null
: floatingAlignment.location,
floatingActionButton: _buildFloatingButton(),
bottomNavigationBar: bottomNavBar,
floatingActionButtonLocation: floatingLocation,
floatingActionButton: floatingButton,
body: widget.menu.reloadView == true
? ListenableBuilder(
listenable: viewGroupNotifier,
Expand Down Expand Up @@ -347,7 +361,16 @@ class _BottomNavPageGroupState extends State<BottomNavPageGroup>
if (widget.menu.reloadView == true) {
viewGroupNotifier.updatePage(index);
} else {
PageGroupWidget.getPageController(context)!.jumpToPage(index);
// Continue the old flow if PageController is available
if (PageGroupWidget.getPageController(context) != null) {
PageGroupWidget.getPageController(context)!.jumpToPage(index);
} else if (GlobalBottomNavController.instance.bottomNavWidget !=
null) {
// This to make sure that if nav buttons are used outside the page group, they would still function
ScreenController().navigateToScreen(context,
screenName: navItems[index].page,
pageArgs: viewGroupNotifier.payload);
}
viewGroupNotifier.updatePage(index);
}

Expand Down
95 changes: 53 additions & 42 deletions modules/ensemble/lib/framework/view/page.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'dart:developer';

import 'bottom_nav_controller.dart';
import 'package:ensemble/ensemble.dart';
import 'package:ensemble/framework/action.dart';
import 'package:ensemble/ensemble_app.dart';
Expand Down Expand Up @@ -468,6 +468,9 @@ class PageState extends State<Page>
EnsembleThemeManager().configureStyles(_scopeManager.dataContext,
widget._pageModel.menu!, widget._pageModel.menu!);
}
final globalBottomNav = GlobalBottomNavController.instance;
Widget? _floatingActionButton;
FloatingActionButtonLocation? _floatingActionButtonLocation;
// build the navigation menu (bottom nav bar or drawer). Note that menu is not applicable on modal pages
if (widget._pageModel.menu != null &&
widget._pageModel.screenOptions?.pageType != PageType.modal) {
Expand All @@ -486,6 +489,13 @@ class PageState extends State<Page>
// sidebar navBar will be rendered as part of the body
}

if (globalBottomNav.bottomNavWidget != null &&
widget._pageModel.runtimeStyles?['showMenu'] == true) {
_bottomNavBar = globalBottomNav.bottomNavWidget;
_floatingActionButton = globalBottomNav.floatingActionButton;
_floatingActionButtonLocation =
globalBottomNav.floatingActionButtonLocation;
}
LinearGradient? backgroundGradient = Utils.getBackgroundGradient(
widget._pageModel.runtimeStyles?['backgroundGradient']);
Color? backgroundColor = Utils.getColor(_scopeManager.dataContext
Expand Down Expand Up @@ -553,12 +563,13 @@ class PageState extends State<Page>
bottomNavigationBar: _bottomNavBar,
drawer: _drawer,
endDrawer: _endDrawer,
floatingActionButton: closeModalButton,
floatingActionButtonLocation:
widget._pageModel.runtimeStyles?['navigationIconPosition'] ==
floatingActionButton: _floatingActionButton ?? closeModalButton,
floatingActionButtonLocation: _floatingActionButtonLocation ??
(
widget._pageModel.runtimeStyles?['navigationIconPosition'] ==
'start'
? FloatingActionButtonLocation.startTop
: FloatingActionButtonLocation.endTop),
: FloatingActionButtonLocation.endTop)),
),
);
DevMode.pageDataContext = _scopeManager.dataContext;
Expand Down Expand Up @@ -588,7 +599,7 @@ class PageState extends State<Page>
}
return rtn;
}

/// determine if we should wraps the body in a SafeArea or not
bool useSafeArea() {
bool? useSafeArea =
Expand Down Expand Up @@ -904,26 +915,26 @@ class AnimatedAppBar extends StatefulWidget {
final duration;
AnimatedAppBar(
{Key? key,
this.automaticallyImplyLeading,
this.leadingWidget,
this.titleWidget,
this.centerTitle,
this.backgroundColor,
this.surfaceTintColor,
this.foregroundColor,
this.elevation,
this.shadowColor,
this.titleBarHeight,
this.backgroundWidget,
this.animated,
this.floating,
this.pinned,
this.collapsedBarHeight,
this.expandedBarHeight,
required this.scrollController,
this.curve,
this.animationType,
this.duration})
this.automaticallyImplyLeading,
this.leadingWidget,
this.titleWidget,
this.centerTitle,
this.backgroundColor,
this.surfaceTintColor,
this.foregroundColor,
this.elevation,
this.shadowColor,
this.titleBarHeight,
this.backgroundWidget,
this.animated,
this.floating,
this.pinned,
this.collapsedBarHeight,
this.expandedBarHeight,
required this.scrollController,
this.curve,
this.animationType,
this.duration})
: super(key: key);

@override
Expand All @@ -932,7 +943,7 @@ class AnimatedAppBar extends StatefulWidget {

class _AnimatedAppBarState extends State<AnimatedAppBar> with WidgetsBindingObserver{
bool isCollapsed = false;

@override
void initState() {
super.initState();
Expand Down Expand Up @@ -997,21 +1008,21 @@ class _AnimatedAppBarState extends State<AnimatedAppBar> with WidgetsBindingObse
centerTitle: widget.centerTitle,
title: widget.animated
? switch (widget.animationType) {
AnimationType.fade => AnimatedOpacity(
opacity: isCollapsed ? 1.0 : 0.0,
duration: Duration(milliseconds: widget.duration ?? 300),
curve: widget.curve ?? Curves.easeIn,
child: widget.titleWidget,
),
AnimationType.drop => AnimatedSlide(
offset: isCollapsed ? Offset(0, 0) : Offset(0, -2),
duration: Duration(milliseconds: widget.duration ?? 300),
curve: widget.curve ?? Curves.easeIn,
child: widget.titleWidget,
),
_ => widget.titleWidget,
}
: widget.titleWidget,
AnimationType.fade => AnimatedOpacity(
opacity: isCollapsed ? 1.0 : 0.0,
duration: Duration(milliseconds: widget.duration ?? 300),
curve: widget.curve ?? Curves.easeIn,
child: widget.titleWidget,
),
AnimationType.drop => AnimatedSlide(
offset: isCollapsed ? Offset(0, 0) : Offset(0, -2),
duration: Duration(milliseconds: widget.duration ?? 300),
curve: widget.curve ?? Curves.easeIn,
child: widget.titleWidget,
),
_ => widget.titleWidget,
}
: widget.titleWidget,
elevation: widget.elevation,
backgroundColor: widget.backgroundColor,
flexibleSpace: wrapsInFlexible(widget.backgroundWidget),
Expand Down