Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multi tab / page view in flutter

Tags:

flutter

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:

  1. Both feed and notifications are list of items fetched over network.
  2. Assuming the original tab is feed, the notifications should be fetched only when user clicks on the notification tab
  3. If a user scrolls in the feed, and clicks on notification icon and then clicks again on feed icon, the scroll position should be remembered.

If i use a TabBarView, it rebuilds the widgets every time the tab is changed, so the scroll position is not retained.

like image 527
aptik Avatar asked Sep 04 '17 18:09

aptik


People also ask

How do you make a tab view in Flutter?

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.

How do you navigate between tabs in Flutter?

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.

How do you make a TabController in Flutter?

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.

What is DefaultTabController in Flutter?

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.


2 Answers

To give the pages of the TabBarView a unique scroll position storage container, use a PageStorageKey.

video

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'),
          ),
        ],
      ),
    );
  }
}
like image 86
Collin Jackson Avatar answered Oct 06 '22 00:10

Collin Jackson


enter image description here

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();
        },
      ),
    );
  }
}
like image 24
Paresh Mangukiya Avatar answered Oct 06 '22 00:10

Paresh Mangukiya