How to pass a GlobalKey through Stateless Widget Children
I suggest using ChangeNotifier
, ChangeNotifierProvider
, Consumer
and context.read
to manage state. You have to add this package and this import: import 'package:provider/provider.dart';
. The steps:
- Set up a
ChangeNotifier
holdingisExpanded
value, with a setter that notifies listeners:
class MyNotifier with ChangeNotifier {
bool _isExpanded = false;
bool get isExpanded => _isExpanded;
set isExpanded(bool isExpanded) {
_isExpanded = isExpanded;
notifyListeners();
}
}
- Insert the above as a
ChangeNotifierProvider
in your widget tree atMenuBar
:
class MenuBarState extends State<MenuBar> {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => MyNotifier(),
child: MenuBarContainer(
menuItems: widget.menuItems,
));
}
}
After this you can easily read and write the isExpanded
value from anywhere in your widget tree under the ChangeNotifierProvider
, for example:
ElevatedButton(
onPressed: () {
setState(() {
final myNotifier = context.read<MyNotifier>();
myNotifier.isExpanded = !myNotifier.isExpanded;
});
},
child: Text('Push Me'),
),
And if you want to use this state to automatically build something when isExpanded
is changed, use Consumer
, which will be notified automatically upon every change, for example:
class MenuBarItemState extends State<MenuBarItem> {
@override
Widget build(BuildContext context) {
return Consumer<MyNotifier>(builder: (context, myNotifier, child) {
return Row(
children: <Widget>[
Text('Current Status:\t${myNotifier.isExpanded}'),
Text(widget.myText),
],
);
});
}
}
Xenoranger
I code it out Typing faster just to script it away =p I wonder if a slice of Raspberry Pi would help reduce energy use for uploads?
Updated on January 02, 2023Comments
-
Xenoranger over 1 year
I'm trying to create a custom menu bar in my app. Right now, the biggest issue I'm having is passing a state for when it's expanded to it's children after a setState occurs.
I thought about inheritance, but from what I've tried all inheritance needs to be in-line. I can't create a widget where the children [] are fed into the constructor on an ad-hoc basis.
My current approach is to use a GlobalKey to update the State of the children widgets being inserted into the StateFul while updating them directly.
The children for my MenuBar are declared as:
List<MenuBarItem> menuItems;
MenuBarItem is an abstract interface class that I intend to use to limit the widgets that can be fed in as menuItems to my MenuBar.
abstract class iMenuItem extends Widget{}
class MenuBarItem extends StatefulWidget implements iMenuItem{
At some iterations of this script, I had a bool isExpanded as part of the iMenuItem, but determined it not necessary.
Here is my code at its current iteration:
My Main:
void main() { // runApp(MainApp()); //runApp(InherApp()); runApp(MenuBarApp()); } class MenuBarApp extends StatelessWidget{ @override Widget build(BuildContext context){ return MaterialApp( home: Scaffold( body: MenuBar( menuItems: [ // This one does NOT work and is where I'm trying to get the // value to update after a setState MenuBarItem( myText: 'Outsider', ), ], ), ), ); } }
My Code:
import 'package:flutter/material.dart'; /// Primary widget to be used in the main() class MenuBar extends StatefulWidget{ List<MenuBarItem> menuItems; MenuBar({ required this.menuItems, }); @override State<MenuBar> createState() => MenuBarState(); } class MenuBarState extends State<MenuBar>{ bool isExpanded = false; late GlobalKey<MenuBarContainerState> menuBarContainerStateKey; @override void initState() { super.initState(); menuBarContainerStateKey = GlobalKey(); } @override Widget build(BuildContext context){ return MenuBarContainer( menuItems: widget.menuItems, ); } } class MenuBarContainer extends StatefulWidget{ List<MenuBarItem> menuItems; late Key key; MenuBarContainer({ required this.menuItems, key, }):super(key: key); @override MenuBarContainerState createState() => MenuBarContainerState(); } class MenuBarContainerState extends State<MenuBarContainer>{ bool isExpanded = false; @override void initState() { super.initState(); isExpanded = false; } @override Widget build(BuildContext context){ List<Widget> myChildren = [ ElevatedButton( onPressed: (){ setState((){ this.isExpanded = !this.isExpanded; }); }, child: Text('Push Me'), ), // This one works. No surprise since it's in-line MenuBarItem(isExpanded: this.isExpanded, myText: 'Built In'), ]; myChildren.addAll(widget.menuItems); return Container( child: Column( children: myChildren, ), ); } } /// The item that will appear as a child of MenuBar /// Uses the iMenuItem to limit the children to those sharing /// the iMenuItem abstract/interface class MenuBarItem extends StatefulWidget implements iMenuItem{ bool isExpanded; String myText; MenuBarItem({ key, this.isExpanded = false, required this.myText, }):super(key: key); @override State<MenuBarItem> createState() => MenuBarItemState(); } class MenuBarItemState extends State<MenuBarItem>{ @override Widget build(BuildContext context){ GlobalKey<MenuBarState> _menuBarState; return Row( children: <Widget> [ Text('Current Status:\t${widget.isExpanded}'), Text('MenuBarState GlobalKey:\t${GlobalKey<MenuBarState>().currentState?.isExpanded ?? false}'), Text(widget.myText), ], ); } } /// To give a shared class to any children that might be used by MenuBar abstract class iMenuItem extends Widget{ }
I've spent 3 days on this, so any help would be appreciated.
Thanks!!