1

I am new to Flutter and am trying to find out how to achieve something that I have done with web programming, namely to have an app that has several 'areas' that can all be 'live/current' at the same time. Imagine an app that has 4 'areas'. Each of the 4 areas has a 'start' screen that is typically a list of items within that area. The user can view the details of a list item, or create a new list item, using a secondary 'child' screen for that area. Each of the 4 'area' screens also includes a drawer, for switching the 'current view' to a specific area. The notion is: The user can go to an area, scroll the list, filter the list, etc. They then can ask the drawer to go to a different area, where they can perform similar actions on that screen. They then should be able to use the drawer to [re] go to the first area, or go to any other area they want, and, when they get there, the 'contents' (data) of that screen should be exactly as it was before they left that screen. Note that if they 'drill into' a list item, or create a new item, they have no access to the 'area drawer' and can only 'go back' to the list screen of that area.

In web programming, each 'area' would be a div, and would all 'live' on a single page. A drawer element would simply hide and show each of the divs as the user wanted, with each of them remaining 'untouched' while hidden.

So, the question is: How to achieve something like this in Flutter? I am slowly getting to understand the 'widget tree' thing, but, as 'screens' (which are really just widgets) are 'pushed' into the tree, what happens to the screen that was currently showing? I understand the nav stack, but what happens to the previous screen, its widgets and its data (memory)? Do they get 'erased' from the tree? From previous questions I've seen, I think the answer is 'yes'. Once a 'pushed off' screen is 're-pushed' to, it begins its life all over again, meaning it has to get data, scroll it, filter it, render it, etc.. all over again.

I guess what I am wondering is, can the app have multiple 'areas' (screens) in memory, in the tree, at the same time, but only one being 'seen' at a time, and, each with its own 'child-screen navigation'. I am sure I am approaching things in the 'wrong' way, but each toolkit/programming system seems to approach this issue in different ways and I am just not sure how to do it with Flutter. I understand I can keep all of the 'data' of each area's 'start' page in memory (at the app level) and could thus re-build each area from that data (without going to the db). I've also heard some folks discuss 'linear navigation' (versus hierarchical?) but do not know much about that.

Thanks for your help.

dbaron
  • 53
  • 1
  • 6

1 Answers1

0

This is very similar to problem we faced a few months ago. We have a drawer with links to four different lists of things. The lists are completely independent from each other and are their own page. You can also tap into one of those items in a list and be brought to a details page.

(Once you are tapped into a details page you can only hit the back button, the drawer is only available on the lists and other top-level pages).

The lists themselves are sortable, paginate, can filter, and quick search. Each list saves its state to a session which is based on the Inherited Widget pattern. I based my Session class on this post:

Flutter: How to correctly use an Inherited Widget?

If you were to go to the List A, sort a column, used the drawer to travel to List B, and then hit back to return to List A, the list would appear in the same state you left it.

If you were to go to the List A, sort a column, used the drawer to travel to List B, and then used the drawer again to go go List A, the list would appear in the same state you left it.

The difference between these two scenarios is what the Navigation Stack looks like. In the first case the stack is simply List A. In the second case the stack is List A, List B, List A.

If you were to take the second scenario, make a change to List A, and then pop the stack twice (hitting the back button), List A at the bottom of the stack actually looks like the List A you changed higher up on the stack.

Here's a partial look at my Session class that supports saving the state of one of the lists. Please note that this is called Session on purpose, and that the app forgets this state when the app is closed. It would be relatively easy to save to shared storage though.

class Session extends StatefulWidget {
  final Widget child;

  Session({this.child});

  @override
  SessionState createState() => SessionState();

  static SessionState of(BuildContext context) {
    return (context.inheritFromWidgetOfExactType(_Session) as _Session).data;
  }
}

class SessionState extends State<Session> {
  ListPagePreferences _myListPrefs = ListPagePreferences();

  //called when the user logs out, for example
  void clear() {
    setState(() {
      _myListPrefs = ListPagePreferences();
    });
  }

  ListPagePreferences get myListPrefs => _myListPrefs;

  void updatePrefs({
    ListPagePreferences prefs,
    rowsPerPage,
    startIndex,
    sortColumnIndex,
    sortAscending,
    searchValue,
    filter,
  }) {
    setState(() {
      prefs.updatePrefs(
        rowsPerPage: rowsPerPage,
        startIndex: startIndex,
        sortColumnIndex: sortColumnIndex,
        sortAscending: sortAscending,
        searchValue: searchValue,
        filter: filter,
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return _Session(
      data: this,
      child: widget.child,
    );
  }
}

class _Session extends InheritedWidget {
  final SessionState data;

  _Session({Key key, this.data, Widget child}) : super(key: key, child: child);

  @override
  bool updateShouldNotify(_Session old) => true;
}

My app is then wrapped with this inherited widget:

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() {
    return _MyAppState();
  }
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return Session(
      child: MaterialApp(
        title: "My App",
        home: WelcomePage(),
      ),
    );
  }
}

This means from anywhere in the app I can access the Session and store things in it. The way it is used anywhere in the app then is like:

ListPagePreferences savedPrefs = Session.of(context).myListPrefs;

There are other ways to save state but this was pretty straightforward and easy for our needs.

Greg Noe
  • 1,176
  • 1
  • 12
  • 29