bdlukaa / fluent_ui

Implements Microsoft's WinUI3 in Flutter.
https://bdlukaa.github.io/fluent_ui/
BSD 3-Clause "New" or "Revised" License
2.97k stars 464 forks source link

🐛 PaneItem's body is re created on Navigation #1110

Closed R3HP closed 2 days ago

R3HP commented 2 months ago

Describe the bug PaneItem body widget is re created on navigating back to the page . so if i have a query initiated in body's 'initState()' it re runs again.

To Reproduce Steps to reproduce the behavior:

  1. Copy The Code Below
  2. Put The Created App Widget in runApp
  3. run

Example Code

import 'package:fluent_ui/fluent_ui.dart';

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

  @override
  State<TestDesktopApp> createState() => _TestDesktopAppState();
}

class _TestDesktopAppState extends State<TestDesktopApp> {

  int selectedIndex = 0;
  final items = <NavigationPaneItem>[

    PaneItem(icon: const Icon(FluentIcons.a_a_d_logo), body: const FirstScreen()),
    PaneItem(icon: const Icon(FluentIcons.a_t_p_logo), body: const SecondScreen()),
  ];

  @override
  Widget build(BuildContext context) {
    return FluentApp(
      restorationScopeId: 'restore',
      home: NavigationView(
        pane: NavigationPane(
          items: items,
          selected: selectedIndex,
          onChanged: (value) {
            setState(() {
              selectedIndex = value;
            });
          },
        ),
      ),
    );
  }
}

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

  @override
  State<FirstScreen> createState() => _FirstScreenState();
}

class _FirstScreenState extends State<FirstScreen> {
  @override
  void initState() {
    super.initState();
    print('firstScreen InitState');
  }
  @override
  Widget build(BuildContext context) {
    return const Text('first');
  }
}

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

  @override
  State<SecondScreen> createState() => _SecondScreenState();
}

class _SecondScreenState extends State<SecondScreen> {
  @override
  void initState() {
    super.initState();
    print('second Screen Init State');
  }

  @override
  Widget build(BuildContext context) {
    return const Text('second');
  }
}

Expected behavior i believed the screens would be maintained or at least the state of widgets would be maintained.

alexursul commented 2 months ago

There was a similar issue, it was suggested to set paneBodyBuilder:

paneBodyBuilder: (item, body) {
  return body!;
},

However it doesn't work. Adding value keys for PaneItem and/or the body doesn't work as well.

R3HP commented 2 months ago

There was a similar issue, it was suggested to set paneBodyBuilder:

paneBodyBuilder: (item, body) {
  return body!;
},

However it doesn't work. Adding value keys for PaneItem and/or the body doesn't work as well.

Tnx I'll check it out

alexursul commented 2 months ago

Thank you! Oh, by the way, it rebuilds a header every time you switch between panes.

NavigationPane(
   header: const HomePageHeader(),
   ...
);

class HomePageHeader extends StatelessWidget {
  const HomePageHeader({super.key});

  @override
  Widget build(BuildContext context) {
      print('I am being rebuilt! :(');
      ...
bdlukaa commented 2 days ago

This is the expected behavior. The widget is disposed when the content is gone and initialized when it is requested again. Use paneBodyBuilder to maintain your very own body logic, using IndexedStack, for example:

NavigationView(
  paneBodyBuilder: (item, body) {
    return IndexedStack(
      index: currentIndex,
      children: [ ... ],
    );
  },
)