brianegan/scoped_model

Widget is not updated even if notifyListeners called

Opened this issue · 0 comments

kinex commented

In my app there is a page which needs to be refreshed if new data is received from cloud. I have implemented it like this:

class ViewEventPage extends StatelessWidget {
  final String eventId;

  const ViewEventPage({Key key, this.eventId}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ScopedModel<EventListViewModel>(
      model: MainViewModel().eventList,
      child: ScopedModelDescendant<EventListViewModel>(
        builder: (context, child, eventList) {
          final currentEventViewModel =
              eventList.getEventViewModel(eventId);
          return ScopedModel<EventViewModel>(
            model: currentEventViewModel,
            child: ScopedModelDescendant<EventViewModel>(
              builder: (context, child, eventViewModel) {
                return Scaffold(
                    key: ValueKey(eventViewModel.event.localHashCode),
                    appBar: _AppBar(eventViewModel),
                    body: _Body(eventViewModel));
              },
            ),
          );
        },
      ),
    );
...
  }

Basic idea is that EventListViewModel notifies listeners when any changes from cloud are received. Then we know that we need to refresh the EventViewModel instance which we are listening too.

One of the child widgets of _Body is a stateful widget:

class AttachmentsSection extends StatefulWidget {
  final EventViewModel viewModel;

  const AttachmentsSection({
    Key key,
    this.viewModel,
  }) : super(key: key);

  @override
  _AttachmentsSectionState createState() => _AttachmentsSectionState();
}

class _AttachmentsSectionState extends State<AttachmentsSection> {
  @override
  void initState() {
    super.initState();

    // option 1: this does not work (except during initial build where it works ok)
    widget.viewModel.listAttachments();

    // option 2: this works
    // scheduleMicrotask(() => widget.viewModel.listAttachments());

    // option 3: this works too
    // widget.viewModel.listAttachments().then((_) => setState(() => {}));

    // option 4: issue gets fixed also if I wrap Build-method with
    // ScopedModel<EventViewModel> + ScopedModelDescendant<EventViewModel>
  }
...
}

In the above code listAttachments is an async method which calls notifyListeners on the EventViewModel which should trigger building the whole page again.

So the main problem here is that I don't understand why the option 1 does not work (works normally during the initial build but after that it doesn't work). And what is the preferred fix?

Also I am not fully sure if the code in ViewEventPage.build is valid usage of ScopedModel because I am changing the EventViewModel instance (it may be different instance than on previous build). But at least it seems to work with options 2-4.

I added this issue for a possible documentation update, because at least in my opinion this is quite confusing.