Flutter TabBar Example – WeightTracker 5
In this short post, I will document how I added TabBar to my WeightTracker app.
Note: I am using Redux to populate data to view, if you are not familiar with Redux, just ignore StoreConnector widget and focus only on Builder body which can be treated like normal build function.
In order to add Tabs to the application, all I needed was to create TabBar and TabBarView and attach TabController to them. TabController will sync TabBar with TabBarView so we can have the behavior we wanted.
class MainPageState extends State<MainPage> with SingleTickerProviderStateMixin{ TabController _tabController;
@override void initState() { super.initState(); _tabController = new TabController(vsync: this, length: 2);
}
@override void dispose() { _tabController.dispose(); super.dispose(); }
@override Widget build(BuildContext context) { return new StoreConnector<ReduxState, MainPageViewModel>( converter: (store) { return new MainPageViewModel([...]); }, builder: (context, viewModel) { return new Scaffold( appBar: new AppBar( title: new Text(widget.title), bottom: new TabBar( tabs: <Tab>[ new Tab( text: "STATISTICS", icon: new Icon(Icons.show_chart), ), new Tab( text: "HISTORY", icon: new Icon(Icons.history), ), ], controller: _tabController, ), ), body: new TabBarView( children: <Widget>[ new StatisticsPage(), new HistoryPage(), ], controller: _tabController, ), floatingActionButton: [...] ); }, ); }}However, I didn’t want the title widget to stick on the top of the screen forever. To add nice hiding behavior, I introduced NestedScrollView with SliverAppBar:
class MainPageState extends State<MainPage> with SingleTickerProviderStateMixin { ScrollController _scrollViewController; TabController _tabController;
@override void initState() { super.initState(); _scrollViewController = new ScrollController(); _tabController = new TabController(vsync: this, length: 2); }
@override void dispose() { _scrollViewController.dispose(); _tabController.dispose(); super.dispose(); }
@override Widget build(BuildContext context) { return new StoreConnector<ReduxState, MainPageViewModel>( converter: (store) { return new MainPageViewModel([...]); }, builder: (context, viewModel) { return new Scaffold( body: new NestedScrollView( controller: _scrollViewController, headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { //<-- headerSliverBuilder return <Widget>[ new SliverAppBar( title: new Text(widget.title), pinned: true, //<-- pinned to true floating: true, //<-- floating to true forceElevated: innerBoxIsScrolled, //<-- forceElevated to innerBoxIsScrolled bottom: new TabBar( tabs: <Tab>[ new Tab( text: "STATISTICS", icon: new Icon(Icons.show_chart), ), new Tab( text: "HISTORY", icon: new Icon(Icons.history), ), ], controller: _tabController, ), ), ]; }, body: new TabBarView( children: <Widget>[ new StatisticsPage(), new HistoryPage(), ], controller: _tabController, ), ), floatingActionButton: [...] ); }, ); }}And that’s it.
If you are interested in the full code shown in this post, see my GitHub repository. 🙂
Cheers! 🙂