Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solution for NavigationView to keep-alive tabs? #649

Closed
b34st80y opened this issue Dec 12, 2022 · 7 comments
Closed

Solution for NavigationView to keep-alive tabs? #649

b34st80y opened this issue Dec 12, 2022 · 7 comments

Comments

@b34st80y
Copy link

b34st80y commented Dec 12, 2022

In previous versions of fluent_ui, I was able to solve this using an IndexedStack in the NavigationView.content (this solution has been mentioned in a few other issues before muizidn@99f7f0c ) - however with the new breaking changes to NavigationView, this solution can not be implemented due to new PaneItem.body requirement.

I have looked at #607, but I am on the latest version (4.1.2) and with the new NavigationPane implementation my state is still being reset.

Is there a new recommended way to achieve this same behavior with the new NavigationView? Ideally I would like to use all of the nice features of the NavigationPane while still allowing the body(s) to stay-alive similar to an IndexedStack implementation.

@b34st80y b34st80y changed the title Solution for NavigationView to keep-alive tabs Solution for NavigationView to keep-alive tabs? Dec 15, 2022
@bdlukaa
Copy link
Owner

bdlukaa commented Dec 23, 2022

You can use NavigationView.paneBodyBuilder to build the body of the pane

@bdlukaa bdlukaa closed this as not planned Won't fix, can't repro, duplicate, stale Dec 23, 2022
@b34st80y
Copy link
Author

b34st80y commented Dec 29, 2022

@bdlukaa Thanks for the suggestion - I have looked deeply into paneBodyBuilder but I cannot figure out how this can help me. The IndexedStack implementation does not work here since the body widget is still passed by the NavigationPane and it does not provide the entire list. I even tried to ignore the provided body and use the index manually. but this also does not work since the build function is called each time the tab switches causing the entire stack to rebuild.

paneBodyBuilder: (body) => IndexedStack(
          index: index,
          children: const [
            ScreenOne(),
            ScreenTwo(),
            ScreenThree(),
          ],
        ),

A code example for how panBodyBuilder could be used for this purpose would be greatly appreciated! I know a few others are also struggling to preserve the state of these tabs as well.

@b34st80y
Copy link
Author

b34st80y commented Dec 29, 2022

@bdlukaa I have created a basic example using the template counter app to better show my issue. You can see (below) I am using AutomaticKeepAliveClientMixin (as mentioned in #604 and #607) but still the state is not being preserved. Based on your suggestion above - could you provide an example of how to modify this template app (maybe using paneBodyBuilder) such that the counter state can be saved whilst switching tabs?

package version - fluent_ui: ^4.1.3

import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  int index = 0;

  @override
  Widget build(BuildContext context) {
    return FluentApp(
      title: 'Flutter Demo',
      home: NavigationView(
        pane: NavigationPane(
          displayMode: PaneDisplayMode.top,
          selected: index,
          onChanged: (i) => setState(() => index = i),
          items: [
            PaneItem(
              icon: const Icon(FluentIcons.send),
              title: const Text('Page 1'),
              body: const MyHomePage(title: "Page 1"),
            ),
            PaneItem(
              icon: const Icon(FluentIcons.download),
              title: const Text('Page 2'),
              body: const MyHomePage(title: "Page 2"),
            ),
            PaneItem(
              icon: const Icon(FluentIcons.download),
              title: const Text('Page 3'),
              body: const MyHomePage(title: "Page 3"),
            ),
            PaneItemSeparator(),
          ],
        ),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with AutomaticKeepAliveClientMixin {
  int _counter = 0;

  @override
  bool get wantKeepAlive => true;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    super.build(context);

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text('$_counter'),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

@francescotau
Copy link

francescotau commented Dec 29, 2022

Hi, I am facing the same problem as reported by @b34st80y. The body state is not preserved when changing index, even using AutomaticKeepAliveClientMixin as shown in the example above.

I am using -> fluent_ui: ^4.1.3

Please, can you provide a working example?

EDIT:
I don't know if it can help but the problem is not present using TabView, which has a similar approach to NavigationView/NavigationPane. Moreover it works without using AutomaticKeepAliveClientMixin for the StatefulWidget used as body of the Tab.

@bdlukaa
Copy link
Owner

bdlukaa commented Dec 31, 2022

Hey! This is a common problem, and not only happens with NavigationView. Thing is that body is recreated every time the index changes and the widget is rebuilt.

Altough, by using fluent_ui: ^4.1.3, you can provide a key to PaneItem and store its state (#656). Another solution is to create the items list outside of the build method (https://github.com/bdlukaa/fluent_ui/blob/master/example/lib/main.dart#L144-L370).

@b34st80y
Copy link
Author

b34st80y commented Jan 5, 2023

Thank you! I was able to produce a workable solution using your second example.

However I wanted to leave this comment to express some issues that I have still been having with this implementation in comparison to my old implementation with IndexedStack.

  1. This new implementation is slower. Each time I switch tabs, the build method is still called for each living tab (even though the state is saved). In my app this has resulted in a slowdown on switching tabs depending on how many tabs you have "alive" (and on how fast/slow those build methods are).
  2. Some of the pages have other stateless widgets inside of them. Since the build method is still called - this results in those stateless widgets reloading. I can solve this by converting those into stateful widgets with AutomaticKeepAliveClientMixin as well - but this is cumbersome in my opinion and I would prefer if all descendants of the page would stay alive.
  3. What if I wanted to pre-load all the pages immediately? With IndexedStack it would run the build method for all pages immediately, allowing for the underlying pages to load up even before you navigate to them.

@bdlukaa
Copy link
Owner

bdlukaa commented Jan 5, 2023

¹ This is expected. Only the state is stored - and heavy computation shouldn't be done inside the build method;
² You must expect the build method to be called multiple times when building your flutter app;
³ You can use NavigationView.paneBodyBuilder to create a custom pane, and use IndexedStack in it:

paneBodyBuilder: (body) {
  return IndexedStack(
    children: [
      ...,
    ],
  );
},

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants