Button Tab Bar In Flutter

Button Tab Bar In Flutter

Using this flutter tutorial you can create the button tab bar in flutter

when you develop any application in flutter and you need to design button tab bar in flutter so this flutter tutorial will be very helpful for you. in button tab bar tutorial all the buttons that you design will be display in a form of tab as you can see in the screenshot(button tab bar : car,transit)

this types of tab bar is common in iOS but now you can design it in android using flutter.i know it is very difficult to understand so all the code is commented with its use so you can easily understand. it is important to control the animation of your whole app.

i use scaffold for the body of the app. each button has its own key so later we can get the button position using its index. When a button is pressed, the Tab Change and Scroll animations are triggered and the current index is set.

here is a full source code with output.study it for get further information about button tab bar in flutter

Screenshot :

Button Tab Bar In Flutter

buttons_tabbar.dart

import 'package:flutter/material.dart';

// Default values from the Flutter's TabBar.
const double _kTabHeight = 46.0;
const double _kTextAndIconTabHeight = 72.0;

class ButtonsTabBar extends StatefulWidget implements PreferredSizeWidget {
  ButtonsTabBar({
    Key key,
    @required this.tabs,
    this.controller,
    this.duration = 250,
    this.backgroundColor = Colors.blueAccent,
    this.unselectedBackgroundColor = Colors.grey,
    this.labelStyle,
    this.unselectedLabelStyle,
    this.physics,
    this.contentPadding,
    this.buttonMargin,
    this.labelSpacing = 4.0,
    this.radius = 7.0,
  }) : super(key: key);

  final List<Widget> tabs;
  final TabController controller;
  final int duration;
  final Color backgroundColor;
  final Color unselectedBackgroundColor;
  final TextStyle labelStyle;
  final TextStyle unselectedLabelStyle;
  final ScrollPhysics physics;
  final EdgeInsets contentPadding;
  final EdgeInsets buttonMargin;
  final double labelSpacing;
  final double radius;

  @override
  Size get preferredSize {
    for (Widget item in tabs) {
      if (item is Tab) {
        final Tab tab = item;
        if (tab.text != null && tab.icon != null)
          return Size.fromHeight(_kTextAndIconTabHeight);
      }
    }
    return Size.fromHeight(_kTabHeight);
  }

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

class _ButtonsTabBarState extends State<ButtonsTabBar>
    with TickerProviderStateMixin {
  TabController _controller;

  ScrollController _scrollController = new ScrollController();
  ScrollPhysics _scrollPhysics;

  AnimationController _animationController;

  List<GlobalKey> _tabKeys;
  GlobalKey _tabsContainerKey = GlobalKey();

  Animation<Color> _colorTweenForegroundActivate;
  Animation<Color> _colorTweenForegroundDeactivate;
  Animation<Color> _colorTweenBackgroundActivate;
  Animation<Color> _colorTweenBackgroundDeactivate;

  Color _unselectedForegroundColor;
  Color _foregroundColor;

  TextStyle _unselectedLabelStyle;
  TextStyle _labelStyle;

  EdgeInsets _contentPadding;
  EdgeInsets _buttonMargin;

  int _currentIndex = 0;
  int _prevIndex = -1;
  int _aniIndex = 0;
  double _prevAniValue = 0;

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

    _tabKeys = widget.tabs.map((Widget tab) => GlobalKey()).toList();

    _scrollPhysics = widget.physics ?? BouncingScrollPhysics();

    _unselectedLabelStyle =
        widget.unselectedLabelStyle ?? TextStyle(color: Colors.black);
    _labelStyle = widget.labelStyle ?? TextStyle(color: Colors.white);

    _foregroundColor = _labelStyle.color ?? Colors.white;
    _unselectedForegroundColor = _unselectedLabelStyle.color ?? Colors.black;

    _contentPadding = widget.contentPadding ?? EdgeInsets.all(4);
    _buttonMargin = widget.buttonMargin ?? EdgeInsets.all(4);

    _animationController = AnimationController(
        vsync: this, duration: Duration(milliseconds: widget.duration));
    _colorTweenBackgroundActivate = ColorTween(
            begin: widget.unselectedBackgroundColor,
            end: widget.backgroundColor)
        .animate(_animationController);
    _colorTweenBackgroundDeactivate = ColorTween(
            begin: widget.backgroundColor,
            end: widget.unselectedBackgroundColor)
        .animate(_animationController);
    _colorTweenForegroundActivate =
        ColorTween(begin: _unselectedForegroundColor, end: _foregroundColor)
            .animate(_animationController);
    _colorTweenForegroundDeactivate =
        ColorTween(begin: _foregroundColor, end: _unselectedForegroundColor)
            .animate(_animationController);

    _animationController.value = 1.0;
  }

  @override
  void dispose() {
    if (_controller != null) _controller.dispose();
    _scrollController.dispose();
    super.dispose();
  }

  Widget _buildButton(int index, Tab tab) {
    final TextStyle textStyle = (index == _currentIndex
        ? TextStyle.lerp(
            _unselectedLabelStyle, _labelStyle, _animationController.value)
        : (index == _prevIndex
            ? TextStyle.lerp(
                _labelStyle, _unselectedLabelStyle, _animationController.value)
            : _unselectedLabelStyle));
    return Container(
      key: _tabKeys[index],
      // padding for the buttons
      margin: _buttonMargin,
      child: FlatButton(
        padding: _contentPadding,
        materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
        // get the color of the button's background (dependent of its state)
        color: index == _currentIndex
            ? _colorTweenBackgroundActivate.value
            : (index == _prevIndex
                ? _colorTweenBackgroundDeactivate.value
                : widget.unselectedBackgroundColor),
        // make the button a rectangle with round corners
        shape: RoundedRectangleBorder(
            borderRadius: new BorderRadius.circular(widget.radius)),
        onPressed: () {
          _goToIndex(index);
        },
        child: Row(
          children: <Widget>[
            IconTheme.merge(
                data: IconThemeData(
                    size: 24.0,
                    color: index == _currentIndex
                        ? _colorTweenForegroundActivate.value
                        : (index == _prevIndex
                            ? _colorTweenForegroundDeactivate.value
                            : _unselectedForegroundColor)),
                child: tab.icon),
            SizedBox(
              width: tab.icon != null && tab.text != null
                  ? widget.labelSpacing
                  : 0
            ),
            tab.text != null
                ? Text(
                    tab.text,
                    style: textStyle,
                  )
                : Container()
          ]
        )
      )
    );
  }

  @override
  Widget build(BuildContext context) {
    if (_controller == null) {
      _controller = widget.controller ?? DefaultTabController.of(context);
      // this will execute the function every time there's a swipe animation
      _controller.animation.addListener(_handleTabAnimation);
    }
    return AnimatedBuilder(
      animation: _colorTweenBackgroundActivate,
      builder: (context, child) => SizedBox(
        key: _tabsContainerKey,
        height: _kTabHeight,
        child: ListView.builder(
            physics: _scrollPhysics,
            controller: _scrollController,
            scrollDirection: Axis.horizontal,
            itemCount: widget.tabs.length,
            itemBuilder: (BuildContext context, int index) =>
                _buildButton(index, widget.tabs[index]))
      )
    );
  }

  _handleTabAnimation() {
    _aniIndex = ((_controller.animation.value > _prevAniValue)
            ? _controller.animation.value
            : _prevAniValue)
        .round();
    if (!_controller.indexIsChanging && _aniIndex != _currentIndex) {
      setState(() {
        _setCurrentIndex(_aniIndex);
      });
    }
    _prevAniValue = _controller.animation.value;
  }

  _goToIndex(int index) {
    if (index != _currentIndex) {
      _setCurrentIndex(index);
      _controller.animateTo(index);
    }
  }

  _setCurrentIndex(int index) {
    setState(() {
      // change the index
      _prevIndex = _currentIndex;
      _currentIndex = index;
      _scrollTo(index); // scroll TabBar if needed
    });
    _triggerAnimation();
  }

  _triggerAnimation() {
    // reset the animation so it's ready to go
    _animationController.reset();

    // run the animation!
    _animationController.forward();
  }

  _scrollTo(int index) {
    // get the screen width. This is used to check if we have an element off screen
    RenderBox tabsContainer =
        _tabsContainerKey.currentContext.findRenderObject();
    double screenWidth = tabsContainer.size.width;

    // get the button we want to scroll to
    RenderBox renderBox = _tabKeys[index].currentContext.findRenderObject();
    // get its size
    double size = renderBox.size.width;
    // and position
    double position = renderBox.localToGlobal(Offset.zero).dx;

    // this is how much the button is away from the center of the screen and how much we must scroll to get it into place
    double offset = (position + size / 2) - screenWidth / 2;

    // if the button is to the left of the middle
    if (offset < 0) {
      // get the first button
      renderBox = _tabKeys[0].currentContext.findRenderObject();
      // get the position of the first button of the TabBar
      position = renderBox.localToGlobal(Offset.zero).dx;

      // if the offset pulls the first button away from the left side, we limit that movement so the first button is stuck to the left side
      if (position > offset) offset = position;
    } else {
      // if the button is to the right of the middle

      // get the last button
      renderBox = _tabKeys.last.currentContext.findRenderObject();
      // get its position
      position = renderBox.localToGlobal(Offset.zero).dx;
      // and size
      size = renderBox.size.width;

      // if the last button doesn't reach the right side, use it's right side as the limit of the screen for the TabBar
      if (position + size < screenWidth) screenWidth = position + size;

      // if the offset pulls the last button away from the right side limit, we reduce that movement so the last button is stuck to the right side limit
      if (position + size - offset < screenWidth) {
        offset = position + size - screenWidth;
      }
    }

    // scroll the calculated ammount
    _scrollController.animateTo(offset + _scrollController.offset,
        duration: new Duration(milliseconds: widget.duration),
        curve: Curves.easeInOut);
  }
}

main.dart

import 'package:flutter/material.dart';
import 'buttons_tabbar.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Example(),
    );
  }
}

class Example extends StatefulWidget {
  Example({Key key}) : super(key: key);

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

class _ExampleState extends State<Example> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: SafeArea(
            child: DefaultTabController(
                length: 6,
                child: Column(children: <Widget>[
                  ButtonsTabBar(
                      backgroundColor: Colors.red,
                      unselectedBackgroundColor: Colors.grey[300],
                      unselectedLabelStyle: TextStyle(color: Colors.black),
                      labelStyle: TextStyle(
                          color: Colors.white, fontWeight: FontWeight.bold),
                      tabs: [
                        Tab(icon: Icon(Icons.directions_car), text: "car"),
                        Tab(
                          icon: Icon(Icons.directions_transit),
                          text: "transit",
                        ),
                        Tab(icon: Icon(Icons.directions_bike)),
                        Tab(icon: Icon(Icons.directions_car)),
                        Tab(icon: Icon(Icons.directions_transit)),
                        Tab(icon: Icon(Icons.directions_bike))
                      ]),
                  Expanded(
                      child: TabBarView(children: <Widget>[
                    Center(child: Icon(Icons.directions_car)),
                    Center(child: Icon(Icons.directions_transit)),
                    Center(child: Icon(Icons.directions_bike)),
                    Center(child: Icon(Icons.directions_car)),
                    Center(child: Icon(Icons.directions_transit)),
                    Center(child: Icon(Icons.directions_bike))
                  ]))
                ]))));
  }
}

The flutter tutorial  is a website that bring you the latest and amazing resources of code. All the languages codes are included in this website. The languages like flutter, android, java,kotlin etc.with the help of this languages any user can develop the beautiful application

For more information about Flutter. visit www.fluttertutorial.in