I have a custom button which takes in a list of items. When it is pressed, it opens a modal bottom sheet and passes that list to the bottom sheet.
However, When the buttons items change it doesn't update the items in the bottom sheet.
How can I achieve this effect.
Simple example
ButtonsPage
|
Button(items: initialItems)
|
BottomSheet(items: initialItems)
** After a delay, setState is called in ButtonsPage with newItems, thereby sending newItems to the button
ButtonsPage
|
Button(items: newItems)
| ## Here, the bottom sheet is open. I want it to update initialItems to be newItems in the bottom sheet
BottomSheet(items: initialItems -- should be newItems)
Code Sample
This is my select field which, as shown, receives a list items
and when it is pressed it opens a bottom sheet and sends the items received to the bottom sheet.
class PaperSelect extends StatefulWidget {
final List<dynamic> items;
PaperSelect({
this.items,
}) : super(key: key);
@override
_PaperSelectState createState() => _PaperSelectState();
}
class _PaperSelectState extends State<PaperSelect> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: widget.disabled ? null : () => _showBottomSheet(context),
child: Container(
),
);
}
void _showBottomSheet(BuildContext context) {
showModalBottomSheet(
context: context,
builder: (BuildContext context) => BottomSheet(
items: widget.items,
),
)
);
}
}
After some time (a network call), items
is updated in the parent Widget of PaperSelect
. PaperSelect
then updates and receives the new items.
class BottomSheet extends StatefulWidget {
final dynamic items;
BottomSheet({
this.items,
}) : super(key: key);
@override
State<StatefulWidget> createState() {
return _BottomSheetState();
}
}
class _BottomSheetState extends State<BottomSheet> {
dynamic items;
@override
void initState() {
print(widget.items);
items = widget.items;
super.initState();
}
@override
Widget build(BuildContext context) {
if(items==null) return Center(
child: SizedBox(
width: 140.0,
height: 140.0,
child: PaperLoader()
),
);
if(items==-1) return Text("Network Error");
return Column(
children: <Widget>
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: items.length,
itemBuilder: (BuildContext context, int i) => ListTile(
onTap: () => Navigator.pop(context),
title: Text('')
)
),
),
],
);
}
}
Now, I want to send the updated data to the bottom sheet. However, it doesn't work because the ModalBottomSheet is already open.
How can I get around this?
I assume here that items is a List<String>
, since you did not specify that at all. (You should generally not use dynamic
in most cases, because it does not do any type checking at all). Anyway,
One thing you could do (beside countless others) is pass in a ValueNotifier<List<String>>
instead of a List<String>
and then user that with a ValueListenableBuilder
in your bottom sheet. like:
// in the caller
final itemsNotifier = ValueNotifier(items);
// in your bottom sheet
ValueListenableBuilder<List<String>>(
valueListenable: itemsNotifier,
builder: (context, snapshot, child) {
final items = snapshot.data;
// build your list
},
)
then every time you would change itemsNotifier.value = [...]
your items would rebuild.
Note when updating itemsNotifier.value
: It must not be done inside build
/didUpdateWidget
/etc. so if this is the case you might use WidgetsBinding.instance.addPostFrameCallback(() => itemsNotifier.value = ... )
to postpone updating the value until after the current build phase.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With