You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
First of all, thank you very much for your project, which has been of great help to me. But when I use the footer feature of SideMenu, I am unable to build SideMenuItems in the footer. Even if I use SideMenuItemWithGlobal, because there is only one item list in Global, I cannot achieve page redirection and other features. Therefore, based on your original code, I have added a footerMenuItems list to maintain the SideMenu at the bottom. Due to my limited abilities, I did not incorporate it into your original code. If you find this feature useful, you may consider adding it to the project. Thanks.
import 'package:easy_sidemenu/easy_sidemenu.dart';
// ignore: implementation_imports
import 'package:easy_sidemenu/src/global/global.dart';
import 'package:flutter/material.dart';
import 'package:badges/badges.dart' as bdg;
class FooterMenu extends StatefulWidget {
/// List of [SideMenuItem] on [FooterMenu]
final List<SideMenuItem> footerItems;
/// divider Widget
final Widget? divider;
const FooterMenu({
super.key,
required this.footerItems,
this.divider,
}) : super();
@override
State<FooterMenu> createState() => _FooterMenuState();
}
class _FooterMenuState extends State<FooterMenu> {
/// SideMenu's global
late final Global? _global;
@override
void initState() {
super.initState();
/// get SideMenu's global
_global = context.findAncestorWidgetOfExactType<SideMenu>()?.global;
assert(_global != null, 'get global exception');
}
@override
void dispose() {
/// release
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
widget.divider ??
const SizedBox.shrink(),
Expanded(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...widget.footerItems.map((item) {
if (item.builder == null) {
return FooterItem(
global: _global!,
item: item,
footerItems: widget.footerItems,
);
} else {
return ValueListenableBuilder(
valueListenable: _global!.displayModeState,
builder: (context, value, child) {
return item.builder!(context, value);
},
);
}
}).toList()
],
),
),
),
],
);
}
}
class FooterItem extends StatefulWidget {
/// Global
final Global global;
/// SideMenuItem
final SideMenuItem item;
/// List of [SideMenuItem] on [FooterMenu]
final List<SideMenuItem> footerItems;
const FooterItem({
super.key,
required this.global,
required this.item,
required this.footerItems,
});
@override
State<FooterItem> createState() => _FooterItemState();
}
class _FooterItemState extends State<FooterItem> {
/// use to set backgroundColor
bool isHovered = false;
bool isDisposed = false;
late int currentPage;
@override
void initState() {
super.initState();
currentPage = widget.global.controller.currentPage;
_nonNullableWrap(WidgetsBinding.instance)!
.addPostFrameCallback((timeStamp) {
// Set initialPage, only if the widget is still mounted
if (mounted) {
currentPage = widget.global.controller.currentPage;
}
if (!isDisposed) {
// Set controller SideMenuItem page controller callback
widget.global.controller.addListener(_handleChange);
}
});
widget.global.itemsUpdate.add(update);
}
@override
void dispose() {
isDisposed = true;
widget.global.controller.removeListener(_handleChange);
super.dispose();
}
/// This allows a value of type T or T?
/// to be treated as a value of type T?.
///
/// We use this so that APIs that have become
/// non-nullable can still be used with `!` and `?`
/// to support older versions of the API as well.
/// https://docs.flutter.dev/development/tools/sdk/release-notes/release-notes-3.0.0#your-code
T? _nonNullableWrap<T>(T? value) => value;
void update() {
if (mounted) {
// Trigger a build only if the widget is still mounted
setState(() {});
}
}
void _handleChange(int page) {
safeSetState(() {
currentPage = page;
});
}
/// Ensure that safeSetState only calls setState when the widget is still mounted.
///
/// When adding changes to this library in future, use this function instead of
/// if (mounted) condition on setState at every place
void safeSetState(VoidCallback fn) {
if (mounted) {
setState(fn);
}
}
/// use to judge isSelected
bool get _isSelected {
if (widget.footerItems.indexOf(widget.item) + widget.global.items.length ==
currentPage) {
return true;
} else {
return false;
}
}
/// Set background color of [FooterMenu]
Color _setColor() {
if (_isSelected) {
if (isHovered) {
return widget.global.style.selectedHoverColor ??
widget.global.style.selectedColor ??
Theme.of(context).highlightColor;
} else {
return widget.global.style.selectedColor ??
Theme.of(context).highlightColor;
}
} else if (isHovered) {
return widget.global.style.hoverColor ?? Colors.transparent;
} else {
return Colors.transparent;
}
}
/// Set icon for of [FooterMenuItem]
Widget _generateIcon(SideMenuItem item) {
if (item.icon == null) return item.iconWidget ?? const SizedBox();
Icon icon = Icon(
item.icon!.icon,
color: _isSelected
? widget.global.style.selectedIconColor ?? Colors.black
: widget.global.style.unselectedIconColor ?? Colors.black54,
size: widget.global.style.iconSize ?? 24,
);
if (item.badgeContent == null) {
return icon;
} else {
return bdg.Badge(
badgeContent: item.badgeContent!,
badgeStyle: bdg.BadgeStyle(
badgeColor: item.badgeColor ?? Colors.red,
),
position: bdg.BadgePosition.topEnd(top: -13, end: -7),
child: icon,
);
}
}
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () => widget.item.onTap?.call(
widget.global.items.length + widget.footerItems.indexOf(widget.item),
widget.global.controller,
),
onHover: (value) {
safeSetState(() {
isHovered = value;
});
},
highlightColor: Colors.transparent,
focusColor: Colors.transparent,
hoverColor: Colors.transparent,
splashColor: Colors.transparent,
child: Padding(
padding: widget.global.style.itemOuterPadding,
child: Container(
height: widget.global.style.itemHeight,
width: double.infinity,
decoration: BoxDecoration(
color: _setColor(),
borderRadius: widget.global.style.itemBorderRadius,
),
child: ValueListenableBuilder(
valueListenable: widget.global.displayModeState,
builder: (context, value, child) {
return Tooltip(
message: (value == SideMenuDisplayMode.compact &&
widget.global.style.showTooltip)
? widget.item.tooltipContent ?? widget.item.title ?? ""
: "",
child: Padding(
padding: EdgeInsets.symmetric(
vertical: value == SideMenuDisplayMode.compact ? 0 : 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: widget.global.style.itemInnerSpacing,
),
_generateIcon(widget.item),
SizedBox(
width: widget.global.style.itemInnerSpacing,
),
if (value == SideMenuDisplayMode.open) ...[
Expanded(
child: FittedBox(
alignment:
Directionality.of(context) == TextDirection.ltr
? Alignment.centerLeft
: Alignment.centerRight,
fit: BoxFit.scaleDown,
child: Text(
widget.item.title ?? '',
style: _isSelected
? const TextStyle(
fontSize: 17, color: Colors.black)
.merge(widget
.global.style.selectedTitleTextStyle)
: const TextStyle(
fontSize: 17, color: Colors.black54)
.merge(widget.global.style
.unselectedTitleTextStyle),
),
),
),
if (widget.item.trailing != null &&
widget.global.showTrailing) ...[
widget.item.trailing!,
SizedBox(
width: widget.global.style.itemInnerSpacing,
),
],
],
],
),
),
);
},
),
),
),
);
}
}
First of all, thank you very much for your project, which has been of great help to me. But when I use the footer feature of SideMenu, I am unable to build SideMenuItems in the footer. Even if I use SideMenuItemWithGlobal, because there is only one item list in Global, I cannot achieve page redirection and other features. Therefore, based on your original code, I have added a footerMenuItems list to maintain the SideMenu at the bottom. Due to my limited abilities, I did not incorporate it into your original code. If you find this feature useful, you may consider adding it to the project. Thanks.
Using it:
The text was updated successfully, but these errors were encountered: