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

[Question/Proposal] NavigationBody and state preservation #95

Closed
henry2man opened this issue Nov 17, 2021 · 19 comments
Closed

[Question/Proposal] NavigationBody and state preservation #95

henry2man opened this issue Nov 17, 2021 · 19 comments
Labels
invalid This doesn't seem right

Comments

@henry2man
Copy link
Contributor

I'm using NavigationView, a NavigationPane with menu entries and a NavigationBody.

Looking into the source code, it looks that every time I change the main index the associated children[index] is rebuilt from scratch. But, in my case, I wish there were some kind of state preservation.

I've added AutomaticKeepAliveClientMixin to every children but every time I change the page using the menu the previous state is lost.

Is there a known way to preserve state when switching between menu options? If not, I think it would be great to add this option to NavigationBody.

At the moment I think I am going to try to use a PageView, where I do have experience maintaining the state between changes.

Here it is a sample code:

import 'package:fluent_ui/fluent_ui.dart';

void main() {
  runApp(MainScreenTest());
}

class MainScreenTest extends StatefulWidget {
  MainScreenTest({Key? key}) : super(key: key);

  @override
  State<MainScreenTest> createState() => _MainScreenTestState();
}

class _MainScreenTestState extends State<MainScreenTest> {
  int index = 0;

  @override
  Widget build(BuildContext context) {
    return FluentApp(
      home: NavigationView(
          content: NavigationBody(
            index: index,
            children: [
              StatefulPage(title: "Screen 1"),
              StatefulPage(title: "Screen 2"),
              StatefulPage(title: "Screen 3")
            ],
          ),
          pane: NavigationPane(
            selected: index,
            onChanged: (i) => setState(() => index = i),
            // displayMode: PaneDisplayMode.top,
            items: [
              PaneItem(icon: Icon(FluentIcons.home), title: Text("Screen 1")),
              PaneItem(icon: Icon(FluentIcons.read), title: Text("Screen 2")),
              PaneItem(icon: Icon(FluentIcons.event), title: Text("Screen 3")),
            ],
          )),
    );
  }
}

class StatefulPage extends StatefulWidget {
  const StatefulPage({Key? key, required this.title}) : super(key: key);
  final String title;

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

class _StatefulPageState extends State<StatefulPage>
    with AutomaticKeepAliveClientMixin {
  int counter = 0;

  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Mica(
      child: Column(
        children: [
          Text(widget.title),
          Button(
            child: Text("Current: $counter - Press to increment!"),
            onPressed: () => setState(() => counter++),
          ),
        ],
      ),
    );
  }
}
@bdlukaa bdlukaa added the enhancement New feature or request label Nov 20, 2021
@takifouhal
Copy link

use IndexedStack instead of NavigationBody.
but you need to be aware that IndexedStack loads all pages at the beginning , to overcome that you need to add a condition on the page build method to currentIndex == pageIndex || pageLoadedAtLeastOnce

@bdlukaa
Copy link
Owner

bdlukaa commented Nov 27, 2021

And it won't provide any animations (unless you do so)

@henry2man
Copy link
Contributor Author

And it won't provide any animations (unless you do so)

And would it be possible to reuse any of the animations in the package?

@bdlukaa
Copy link
Owner

bdlukaa commented Nov 29, 2021

Yes. You can use https://github.com/bdlukaa/fluent_ui/blob/538371720241b595dcc04cbcce4ea5227787ff10/lib/src/styles/motion/page_transitions.dart

@henry2man
Copy link
Contributor Author

henry2man commented Dec 3, 2021

use IndexedStack instead of NavigationBody. but you need to be aware that IndexedStack loads all pages at the beginning , to overcome that you need to add a condition on the page build method to currentIndex == pageIndex || pageLoadedAtLeastOnce

@takifouhal Thanks! IndexedStack worked like a charm as a 1:1 direct replacement of NavigationBody.
@bdlukaa Thanks for the tip. In my use case for now I think I'll go without animations because is some kind of backend UI and "instant switch" feels better than anything animated.

Regarding this issue, do whatever you want. If you consider this kind of feature useful, leave it open; if not, please close it.

Thanks for your time guys.

@henry2man
Copy link
Contributor Author

As I'm happy using IndexedStack I'm going to close this. Thanks!

@bdlukaa
Copy link
Owner

bdlukaa commented Jan 1, 2022

Reopening as a valid request!

@bdlukaa bdlukaa reopened this Jan 1, 2022
@alimsk
Copy link

alimsk commented Feb 7, 2022

hi, how do i use transition in IndexedStack?
an example would be useful 🙂

@bdlukaa
Copy link
Owner

bdlukaa commented Feb 7, 2022

Wrap your IndexedStack in a AnimatedSwitcher. Something like the following:

int index = 0;

AnimatedSwitcher(
  transitionBuilder: (child, animation) {
    return DrillInPageTransition(child: child, animation: animation);
  },
  child: IndexedStack(
    key: ValueKey<int>(index),
    currentIndex: 0,
    children: [...],
  ),
)

@Spsden
Copy link

Spsden commented Feb 8, 2022

I also had the same problem and Pageview works absolutely fine. Although I have not been able to find a way to preserve state when the app switches PaneDisplayMode. Is there any fix for that ?

@bdlukaa
Copy link
Owner

bdlukaa commented Feb 8, 2022

I could check how PageView implements it and try to replicate

@Spsden
Copy link

Spsden commented Feb 8, 2022

I could check how PageView implements it and try to replicate

Sure,
Here's an implementation in my repository
https://github.com/Spsden/Drip/blob/master/lib/main.dart

Thanks for the excellent work you are doing.

@jonahzheng
Copy link

class MyViewPageState extends State with AutomaticKeepAliveClientMixin{
..................

@OverRide
bool get wantKeepAlive => true;
}

@henry2man
Copy link
Contributor Author

I've added AutomaticKeepAliveClientMixin to every children but every time I change the page using the menu the previous state is lost.

As I said in the issue description, I tried this solution without success but, TBH, since I moved to the solution with IndexedStack (#95 (comment)) I have not retested the issue using this possibility again.

@bdlukaa
Copy link
Owner

bdlukaa commented Jun 26, 2022

I was able to keep the state on the example app using NavigationBody. Take a look!

Basically, the pages can't be built (call ()) on the buildMethod. Instead, they're built on the startup and their state are stored there, without changes when called setState.

I could even keep the scroll position by using a PageStorageKey (key: PageStorageKey(_pageIndex)).

There, I created a new class called Page, but that's not necessary to make it work. It's just a wrap around flutter's widget to make the code more readable.

So, basically, to make the state persist, you need to do the following:

final List<Widget> pages = [
  const PageOne(),
  const PageTwo(),
];

NavigationView(
  ...
  bodies: pages,
),

@henry2man
Copy link
Contributor Author

Sounds great! I'll take a look because this will let me use page transitions easily...

Giving the fact that we have a working solution, you can close this issue if you're happy too.

@bdlukaa bdlukaa closed this as completed Jun 29, 2022
@bdlukaa bdlukaa added invalid This doesn't seem right and removed enhancement New feature or request labels Jun 29, 2022
@MasterHiei
Copy link
Contributor

@bdlukaa Hi! Could you give me more details about #95 (comment)?
In my case, I'm using provider but those will be recreated evevry time I switch the pane.

@bdlukaa
Copy link
Owner

bdlukaa commented Sep 27, 2022

@MasterHiei what if you create your provider above your NavigationView?

@MasterHiei
Copy link
Contributor

@bdlukaa It's not bad:+1:
In riverpod 2.0, cacheTime also worked for me but I would like to use it. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
invalid This doesn't seem right
Projects
None yet
Development

No branches or pull requests

7 participants