How do i create a multi page view in flutter where the pages correspond to tabs in a bottomnavigationbar such that the widgets corresponding to the pages are built only once and on demand.
For eg., consider a simple facebook app kind of UI with two tabs - the feed and the notification with the following behavior:
If i use a TabBarView, it rebuilds the widgets every time the tab is changed, so the scroll position is not retained.
You can create tabs using the TabBar widget. In this example, create a TabBar with three Tab widgets and place it within an AppBar . return MaterialApp( home: DefaultTabController( length: 3, child: Scaffold( appBar: AppBar( bottom: const TabBar( tabs: [ Tab(icon: Icon(Icons. directions_car)), Tab(icon: Icon(Icons.
A common navigation pattern on mobile is to show multiple tabs with different pages inside them. With Flutter, this is easily done with the TabBar widget, along with a TabController and a TabBarView.
To create a tab in it, create a tab list and create an object of its TabController. TabController _tabController; Initalize the TabController inside the initState() method and also override it in the dispose() method.
DefaultTabController is an inherited widget that is used to share a TabController with a TabBar or a TabBarView. It's used when sharing an explicitly created TabController isn't convenient because the tab bar widgets are created by a stateless parent widget or by different parent widgets.
To give the pages of the TabBarView
a unique scroll position storage container, use a PageStorageKey
.
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
State createState() => new MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
TabController _controller;
int _tab = 0;
@override
void initState() {
_controller = new TabController(length: 2, vsync: this);
}
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text('Example App')),
body: new TabBarView(
controller: _controller,
children: <Widget>[
new ListView.builder(
key: new PageStorageKey('feed'),
itemBuilder: (BuildContext context, int index) {
return new ListTile(
title: new Text('Feed Item $index'),
);
},
),
new ListView.builder(
key: new PageStorageKey('notifications'),
itemBuilder: (BuildContext context, int index) {
return new ListTile(
title: new Text('Notification $index'),
);
},
),
],
),
bottomNavigationBar: new BottomNavigationBar(
onTap: (int value) {
_controller.animateTo(value);
setState(() {
_tab = value;
});
},
currentIndex: _tab,
items: <BottomNavigationBarItem>[
new BottomNavigationBarItem(
icon: new Icon(Icons.home),
title: new Text('Home'),
),
new BottomNavigationBarItem(
icon: new Icon(Icons.notifications),
title: new Text('Notifications'),
),
],
),
);
}
}
The Complete Example
First make a class MyBottomBarDemo
class MyBottomBarDemo extends StatefulWidget {
@override
_MyBottomBarDemoState createState() => new _MyBottomBarDemoState();
}
class _MyBottomBarDemoState extends State<MyBottomBarDemo> {
int _pageIndex = 0;
PageController _pageController;
List<Widget> tabPages = [
Screen1(),
Screen2(),
];
@override
void initState(){
_pageController = PageController(initialPage: _pageIndex);
super.initState();
}
@override
void dispose() {
super.dispose();
_pageController.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Multi tab / page view", style: TextStyle(color: Colors.white)),
backgroundColor: Colors.deepPurple,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _pageIndex,
onTap: onTabTapped,
backgroundColor: Colors.white,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem( icon: Icon(Icons.home), title: Text("Home")),
BottomNavigationBarItem(icon: Icon(Icons.mail), title: Text("Messages")),
],
),
body: PageView(
children: tabPages,
onPageChanged: onPageChanged,
controller: _pageController,
),
drawer: SlideDrawer(),
);
}
void onPageChanged(int page) {
setState(() {
this._pageIndex = page;
});
}
void onTabTapped(int index) {
this._pageController.animateToPage(index,duration: const Duration(milliseconds: 500),curve: Curves.easeInOut);
}
}
Then create a your pagers screens
class Screen1(), extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.yellow,
child: StreamBuilder(
stream: getHomeDataList(param).asStream(), // This is your function of get data from network
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting: {
return LoadingIndicatorView();
}
case ConnectionState.active: {
break;
}
case ConnectionState.done: {
if (snapshot.hasData) {
if (snapshot.data != null) {
if (snapshot.data.length > 0) {
return Container(
color: offBlueColor,
child: Scrollbar(
child: ListView.builder(
key: PageStorageKey('homeList'),
padding: EdgeInsets.all(5),
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('Home Page index $index')
);;
}),
),
);
} else {
return Text("No data found");
}
} else {
// display error message data is null.
return Text("No data found");
}
} else if (snapshot.hasError) {
return Text(snapshot.error.toString());
} else {
return Text("Something went wrong");
}
break;
}
case ConnectionState.none:{
break;
}
}
return Container();
},
),
);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With