Sign in
Generate apps fast! skip repetitive tasks with AI-powered flow
Which widget keeps your screens smooth and state intact? Learn how Flutter’s IndexedStack and PageView handle navigation, memory, and user state—so you can pick the right one for your app without second-guessing.
Are your Flutter tabs resetting scroll position every time a user switches between them? As apps grow with more screens and logic, keeping navigation smooth without losing state becomes harder.
What happens when you choose the wrong navigation widget?
Users expect quick transitions, remembered inputs, and uninterrupted scrolling. However, using the wrong option can lead to wasted memory or screen rebuilds that break the experience. The problem gets worse with complex flows.
That’s why comparing Flutter IndexedStack vs PageView is so important.
This article explains how each handles scroll position, memory, and rebuilds. It will help you pick the right one for your app’s navigation.
IndexedStack keeps all child widgets alive, which is ideal for preserving state across navigation bar pages.
PageView provides scrollable transitions with lazy loading and optional state preservation via mixins.
Choose IndexedStack for quick tab switching, PageView for swiping or dynamic content loading.
Use AutomaticKeepAliveClientMixin to preserve scroll position in PageView.
Combine both for custom tab pages and complex bottom navigation bar page layouts.
Let’s begin by comparing how these two widgets work fundamentally.
Feature | IndexedStack | PageView |
---|---|---|
Default Behavior | Shows only one child, keeps all children alive | Allows swiping between children, builds lazily |
State Retention | Preserves state without extra effort | Requires mixins like AutomaticKeepAliveClientMixin |
Scroll Animation | No built-in animation | Built-in swipe transition |
Memory Usage | Higher (all children built at once) | Lower (only current/adjacent pages built) |
Use Case Fit | Best for bottom navigation bar page or static tabs | Best for top app bar tabs and swipeable views |
The IndexedStack widget shows one child from a list, based on the active index. Unlike a typical Stack, the indexed stack turns into a tab manager by keeping each child widget alive in memory.
1IndexedStack( 2 index: _selectedIndex, 3 children: [ 4 HomePage(), 5 SearchPage(), 6 ProfilePage(), 7 ], 8)
This approach ensures:
The scroll position and state of tab pages remain untouched.
ListView displaying content doesn’t reset when switching tabs.
Works great with bottom tabs via a bottom navigation bar.
However, every child is built up front—even unused ones. If all the tabs are heavy, this can lead to longer startup times or memory pressure.
Use Case: A shopping app with a bottom navigation bar page of 3 sections (Home, Orders, Profile). Users expect instant transitions, and each screen should preserve scroll position.
PageView is like a horizontally scrollable ListView, allowing users to swipe across pages. Unlike IndexedStack, it:
Builds only the first time you swipe to a page (with PageView.builder)
Supports gestures and animations out of the box
Requires setup to preserve the state (like AutomaticKeepAliveClientMixin)
1PageView( 2 controller: _controller, 3 children: [ 4 HomePage(), 5 SearchPage(), 6 ProfilePage(), 7 ], 8)
To preserve scroll position in PageView:
1class MyTabPage extends StatefulWidget { 2 3 _MyTabPageState createState() => _MyTabPageState(); 4} 5 6class _MyTabPageState extends State<MyTabPage> 7 with AutomaticKeepAliveClientMixin { 8 9 bool get wantKeepAlive => true; 10 11 12 Widget build(BuildContext context) { 13 super.build(context); 14 return ListView.builder(...); // Example scrollable list 15 } 16}
This approach is scalable, making it suitable for:
Scrollable list UIs like carousels or onboarding
Tabbar widgets with tab pages that change via swipe
A bottom navigation bar that should delay building screens
Pro Tip: Disable user swiping (NeverScrollableScrollPhysics) to simulate IndexedStack behavior.
Here’s a quick snapshot of real-world patterns:
IndexedStack | PageView + KeepAlive |
---|---|
All screens created on startup | Builds page only when first shown |
Instant switching | May delay on first navigation |
Simple to implement | Needs controller, mixins |
No animation | Has swipe/animateToPage transitions |
Higher memory usage for many pages | Lower initial memory footprint |
State Flow Between Tabs
This diagram illustrates tab switching with both approaches. IndexedStack keeps state, while PageView requires keep-alive mixins to behave similarly.
Use PageView via TabBarView – users expect swiping
IndexedStack is overkill unless swipe is disabled
Need to preserve scroll position?
â—¦ Use IndexedStack (default behavior)
â—¦ Or PageView + AutomaticKeepAliveClientMixin
Avoid Pitfall: Using PageView without preserving state causes list scrolls to reset.
1class MainScreen extends StatelessWidget { 2 final int index; 3 MainScreen(this.index); 4 5 6 Widget build(BuildContext context) { 7 return Scaffold( 8 body: IndexedStack( 9 index: index, 10 children: [ 11 HomePage(), 12 SearchPage(), 13 ProfilePage(), 14 ], 15 ), 16 bottomNavigationBar: BottomNavigationBar(...), 17 ); 18 } 19}
1class PageViewScreen extends StatefulWidget { 2 3 _PageViewScreenState createState() => _PageViewScreenState(); 4} 5 6class _PageViewScreenState extends State<PageViewScreen> { 7 PageController _controller = PageController(); 8 9 10 void dispose() { 11 _controller.dispose(); // Dispose to avoid leaks 12 super.dispose(); 13 } 14 15 16 Widget build(BuildContext context) { 17 return Scaffold( 18 body: PageView( 19 controller: _controller, 20 physics: NeverScrollableScrollPhysics(), // Optional 21 children: [ 22 HomePage(), 23 SearchPage(), 24 ProfilePage(), 25 ], 26 ), 27 bottomNavigationBar: BottomNavigationBar(...), 28 ); 29 } 30}
Both examples represent a simple scaffold with three bottom tab pages.
You need all the TabBar child widgets ready instantly
You want to preserve the scroll position without extra code
The number of bottom tabs is small (3–5)
You’re building a basic Flutter app or a simple navigation bar
You want swipe gestures and animations
Pages are scrollable list views or loaded lazily
You’re optimizing performance for large navigation bar sets
You need to change pages via a controller programmatically
Problem | Solution |
---|---|
Scroll resets on tab switch (PageView) | Use AutomaticKeepAliveClientMixin |
Memory issues (IndexedStack with many tabs) | Limit to small sets or consider lazy stack widget |
Unexpected rebuilds | Move child list outside of build method |
Swipe unwanted in bottom tabs | Set NeverScrollableScrollPhysics in PageView |
Rebuild form/scroll only once | Keep widgets with PageStorageKey or keep-alive |
Your choice between IndexedStack and PageView shapes how your app handles scroll position, memory, and tab-switching speed. IndexedStack works best for simple tabs where preserving state matters most. For swipe gestures, lazy loading, and scalable pages, PageView delivers more control and flexibility.
Flutter apps need fast and responsive navigation to meet user expectations. By mastering Flutter IndexedStack vs. PageView, you’ll build smoother flows and better user experiences. Pick the widget that fits your app and start building with clarity.