Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update content of a ModalBottomSheet from parent while active

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?

like image 617
NduJay Avatar asked Aug 31 '25 15:08

NduJay


1 Answers

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.

like image 155
Herbert Poul Avatar answered Sep 02 '25 18:09

Herbert Poul