1

i am trying to create material native tabs so when we scroll through the page, appbar collapses but the tabbar should be visible always and I implemented this using NestedScrollView in flutter

class HomeScreen extends StatefulWidget {
    @override
    _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateMixin {
    TabController _tabController;

    @override
    void initState() {
        super.initState();
        _tabController = TabController(length: 2, vsync: this);
    }

    @override
    void dispose() {
        super.dispose();
        _tabController.dispose();
    }

    @override
    Widget build(BuildContext context) {
        return NestedScrollView(
            headerSliverBuilder: (BuildContext context, bool isBoxScrolled) {
                return [
                    SliverAppBar(
                title: Text("Scroller title"),
                forceElevated: isBoxScrolled,
                pinned: true,
                floating: true,
                bottom: TabBar(
                    tabs: [Tab(text: "tab1"), Tab(text: "tab2")],
                    controller: _tabController))
          ];
        },
        body: TabBarView(
          children: [Page1(), Page1()],
          controller: _tabController,
        ));
  }
} 

class Page1 extends StatefulWidget {
  @override
  _Page1State createState() => _Page1State();
}

class _Page1State extends State<Page1> {
  ScrollController _controller;

  void _scrollListener(){
    if(_controller.offset >= _controller.position.maxScrollExtent && !_controller.position.outOfRange){
      print("reached the bottom");
    }
  }

  @override
  void initState() {
    super.initState();
    _controller = ScrollController();
    _controller.addListener(_scrollListener);
  }

  @override
  Widget build(BuildContext context) {
    return ListView(
      controller: _controller,
      children: <Widget>[Text("data"), SizedBox(height: 2000.0)],
    );
  }
}

but when I tried to use scrollController inside one of my tabBarview widget it disconnects contact with appbar and scroll individually.

1 Answers1

8

the solution is the PrimaryScrollController

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateMixin {
  TabController _tabController;

  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 2, vsync: this);
  }

  @override
  void dispose() {
    super.dispose();
    _tabController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return NestedScrollView(
      headerSliverBuilder: (BuildContext context, bool isBoxScrolled) {
        return [
          SliverAppBar(
              title: Text("Scroller title"),
              forceElevated: isBoxScrolled,
              pinned: true,
              floating: true,
              bottom: TabBar(
                  tabs: [Tab(text: "tab1"), Tab(text: "tab2")],
                  controller: _tabController))
        ];
      },
      body: Builder(
        builder: (BuildContext context) {
          final innerScrollController = PrimaryScrollController.of(context);

          // Use the innerScrollController to listen to the scrolling.
          // This would be your controller for list. You can listen to this controller to know whether the list has reached maxScrollExtent and fetch data from API.

          return TabBarView(
            children: [
              Page1(innerScrollController),
              Page1(innerScrollController)
            ],
            controller: _tabController,
          );
        },

      ),
    );
  }
}

class Page1 extends StatefulWidget {

  final ScrollController _PrimaryScrollController;

  Page1(this._PrimaryScrollController);

  @override
  _Page1State createState() => _Page1State();
}

class _Page1State extends State<Page1> {


  void _scrollListener(){
    if(this.widget._PrimaryScrollController.offset >= this.widget._PrimaryScrollController.position.maxScrollExtent && !this.widget._PrimaryScrollController.position.outOfRange){
      print("reached the bottom");
    }
  }

  @override
  void initState() {
    super.initState();

    this.widget._PrimaryScrollController.addListener(_scrollListener);
  }

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: <Widget>[Text("data"), SizedBox(height: 2000.0)],
    );
  }
}
FloW
  • 1,211
  • 6
  • 13
  • 1
    adding scrollListener function to the pages but when I am printing offsets then it prints only up to 128.00 after that scrollListener function is not running and behaving abnormally, I want to detect whether I reached the bottom of pages or not – Balaji Jangde Apr 10 '20 at 12:38
  • 1
    I want to listen for the scroll actions of pages inside but I think your code is listening only for Nestedscrollview so it prints only up to 128.00 (my screen height) for an infinite list. – Balaji Jangde Apr 10 '20 at 12:45
  • did you see? PrimaryScrollController is working fine – FloW Apr 13 '20 at 05:26
  • @BalajiJangde is right, this causes the listener to behave abnormally – Raul Mabe May 11 '21 at 18:00