19

I implemented a basic TabBar and TabBarView with a DefaultTabController, see code below.

class MyApp2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: BOTTOM_TABS,
      child: Scaffold(
        appBar: AppBar(title: const Text('Bottom App Bar')),
        body: _tabBarView(),
        bottomNavigationBar: _bottomTabBar(),
      ),
    );
  }

  _tabBarView() {
    return TabBarView(
      physics: NeverScrollableScrollPhysics(),
      children: [
        Container(
          color: Colors.blue,
        ),
        Container(
          color: Colors.orange,
        ),
        Container(
          color: Colors.lightGreen,
        ),
        Container(
          color: Colors.red,
        ),
      ],
    );
  }

  _bottomTabBar() {
    return TabBar(
      tabs: [
        Tab(
          icon: new Icon(Icons.home),
        ),
        Tab(
          icon: new Icon(Icons.public),
        ),
        Tab(
          icon: new Icon(Icons.group),
        ),
        Tab(
          icon: new Icon(Icons.person),
        )
      ],
    );
  }
}

Works great! Now what I want to do is change the animation between the two tabs from the default animation. But I can't find an easy way to do that.

After a bit of research it seems like I need to use a custom TabController and somehow use its animateTo method. To me that seems like a pretty big change just to change the animation. What I wonder is if that is the correct way or if I am missing some easier way to just change the default animation between the tabviews?

If someone could give me some good resources to point me in the right direction I'd greatly appreciate it.

molundb
  • 484
  • 1
  • 4
  • 18

4 Answers4

20

This is not hard, just use TabController (to do so you need to use SingleTickerProviderStateMixin ) and AnimatedBuilder.

enter image description here

class MyApp2 extends StatefulWidget {
  @override
  _MyApp2State createState() => _MyApp2State();
}

class _MyApp2State extends State<MyApp2> with SingleTickerProviderStateMixin {
  TabController _tabController;

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

  _tabBarView() {
    return AnimatedBuilder(
      animation: _tabController.animation,
      builder: (BuildContext context, snapshot) {
        return Transform.rotate(
          angle: _tabController.animation.value * pi,
          child: [
            Container(
              color: Colors.blue,
            ),
            Container(
              color: Colors.orange,
            ),
            Container(
              color: Colors.lightGreen,
            ),
            Container(
              color: Colors.red,
            ),
          ][_tabController.animation.value.round()],
        );
      },
    );
  }

  _bottomTabBar() {
    return TabBar(
      controller: _tabController,
      labelColor: Colors.black,
      tabs: [
        Tab(
          icon: new Icon(Icons.home),
        ),
        Tab(
          icon: new Icon(Icons.public),
        ),
        Tab(
          icon: new Icon(Icons.group),
        ),
        Tab(
          icon: new Icon(Icons.person),
        )
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 4,
      child: Scaffold(
        appBar: AppBar(title: const Text('Bottom App Bar')),
        body: _tabBarView(),
        bottomNavigationBar: _bottomTabBar(),
      ),
    );
  }
}
Kherel
  • 14,882
  • 5
  • 50
  • 80
  • What does that double array mean in Transform.rotate() inside _tabBarView function ? Is that like a multidimensional array ? – Sarvesh Dalvi Mar 20 '21 at 21:38
  • 1
    it's not double array. In the first brackets is the list of tabs, and in the second brackets is the index of the current tab. – Kherel Mar 22 '21 at 18:36
  • if you wanted to have no animation at all, would this approach be useful too? – Iván Yoed May 19 '21 at 16:31
  • then you don't need tabview, just add all tabs widgets in a list, and save tabController.index in state. to do that you need addListener to tabController, and save index when indexIsChanging, similar idea is here https://stackoverflow.com/questions/52547731/tabbarview-with-variable-height-inside-a-listview, but you don't need indexed stack... – Kherel May 19 '21 at 18:02
  • this is one of a few working examples one the web, thanks – Ehab Reda Aug 25 '21 at 10:30
2

Screenshot (Null safe):

enter image description here


Code:

If you want fine-grained control, you can make use of the AnimationController.

class _MyPageState extends State<MyPage> with TickerProviderStateMixin {
  late final TabController _tabController;
  late final AnimationController _controller;

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

    _controller = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 400),
      value: 1,
    );

    _tabController = TabController(
      length: 3,
      vsync: this,
    )..addListener(() {
        if (_tabController.indexIsChanging) {
          setState(() => _controller.forward(from: 0.5));
        }
      });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ScaleTransition(
        scale: _controller,
        child: [
          Container(color: Colors.red),
          Container(color: Colors.green),
          Container(color: Colors.blue),
        ][_tabController.index],
      ),
      bottomNavigationBar: TabBar(
        controller: _tabController,
        tabs: [
          Tab(child: Text('Red')),
          Tab(child: Text('Green')),
          Tab(child: Text('Blue')),
        ],
      ),
    );
  }
}
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
1

I don't know if you want to completely change the animation.

But if you just need some customization, did you try to use a TabController instead of a DefaultTabController ? You just need to pass the tabController as an arg to the TabBar & TabBarView.

To customize the animation with the tabController, you should specify an Animation for the tabController and also specify the curve and duration with the animateTo function of the tabController.

https://api.flutter.dev/flutter/material/TabController/animateTo.html https://api.flutter.dev/flutter/material/TabController-class.html

Gaspard Merten
  • 1,063
  • 7
  • 14
1

Disable animation between flutter tabs by setting animation duration to zero like this

    tabController = TabController(
    animationDuration: Duration.zero,
    length: 4, vsync: this, initialIndex: 0);

Thank me later.

MUHINDO
  • 788
  • 6
  • 10