Reuse Widget Across Two Pages With Keys

459 views Asked by At

Long-winded context, scroll to bottom for specific question:

I have a page with a custom widget (EntrySearch) containing a ListView and a search bar. When the user clicks on an item in the list view, I push a new page with the same list view and a panel displaying item details. The list view's items are lazily loaded from a Stream backed by an asynchronous query (the stream is paused until the user scrolls to the end of elements fetched so far). The stream is replaced whenever the user types in a new string in the search bar.

I want the widget to have the same list view (elements, scroll position, etc.) and search bar state across both pages.

I could achieve this with any of the many state management solutions suggested on similar questions about shared widget state across pages (eg this one). However, because my ListView is backed by an asynchronous stream along with a ton of other state, this becomes a total pain of caching results fetched so far, creating a new Stream that starts where the old one left off (can't have two StreamBuilders backed by the same non-broadcast stream), etc. etc. I'd really like to avoid such a messy solution.

Giving the top level Widget whose state I want to share a global key is one line (instead of ~50) and results in shared state across the pages. However, when I push the new page, the old page stays in the Widget tree on standby, so I get an error for having two widgets with the same global key in the widget tree at the same time. I can set maintainState to false on the first page, but then when I pop the second the first page shows up as just a black screen.

Is there:

  1. a way to use a global key widget across two pages without them both being in the tree at the same time
  2. a specialized key specifically for reusing a widget across two different pages
  3. some other approach I'm not thinking of?

Here's my widget tree with the two pages. EntrySearch is the widget I want to reuse across the two pages. widget tree

If none of the above works, I'll have to use a traditional state management strategy which is going to be at least an order of magnitude more verbose and bug-prone.

1

There are 1 answers

0
caseycrogers On

Here's the solution I went with, if anyone else is approaching a similar problem:

I couldn't find a way to use keys for this problem. What I'm doing now is I have a single class that contains all the state my custom widget needs to build itself. Whenever I push a route to the navigator, I copy the current page's state object with the desired changes and then add the new state to the RouteSettings.argument field. I can then access the page's state object anywhere within the page using ModalRoute.of(context).settings.arguments.

As expected, this solution was massively complex and painful with a lot of subtle bugs I had to work through. It works and at least has slightly more desirable behavior than simply maintaining identical state across both pages: when I pop a page, the state pops too. This means a user can go back to their previous scroll position in the ListView with the back gesture.