Yusuf  Aloush
Yusuf Aloush

Reputation: 23

Flutter arrow tab bar?

How I can make the cursor of tab bar with an arrow like this? enter image description here

Upvotes: 2

Views: 1409

Answers (3)

Buddhika
Buddhika

Reputation: 325

Null Safety Code for the answer:

class CircleTabIndicator extends Decoration {
  final BoxPainter _painter;

  CircleTabIndicator({required Color color}) : _painter = _CirclePainter(color);

  @override
  BoxPainter createBoxPainter([VoidCallback? onChanged]) {
    return _painter;
  }

}

class _CirclePainter extends BoxPainter {
  final Paint _paint;

  _CirclePainter(Color color)
      : _paint = Paint()
          ..color = color
          ..isAntiAlias = true;

  @override
  void paint(Canvas canvas, Offset offset, ImageConfiguration cfg) {
    Path trianglePath = Path();

    trianglePath.moveTo(cfg.size!.width / 2 - 10, cfg.size!.height);
    trianglePath.lineTo(cfg.size!.width / 2 + 10, cfg.size!.height);
    trianglePath.lineTo(cfg.size!.width / 2, cfg.size!.height - 10);
    trianglePath.lineTo(cfg.size!.width / 2 - 10, cfg.size!.height);
    trianglePath.close();
    canvas.drawPath(trianglePath, _paint);
  }
}

Upvotes: 0

Building on Virens' answer I've made this version of the painter which addresses the issues in the comments and is null safe for use with newer versions of Flutter.

It may also serve to more clearly illustrate what's going on in the paint method.

import 'package:flutter/material.dart';

class ArrowTabBarIndicator extends Decoration {
  final BoxPainter _painter;
  ArrowTabBarIndicator({double width = 20, double height = 10})
      : _painter = _ArrowPainter(width, height);

  @override
  BoxPainter createBoxPainter([VoidCallback? onChanged]) => _painter;
}

class _ArrowPainter extends BoxPainter {
  final Paint _paint;
  final double width;
  final double height;

  _ArrowPainter(this.width, this.height)
      : _paint = Paint()
    ..color = Colors.white
    ..isAntiAlias = true;

  @override
  void paint(Canvas canvas, Offset offset, ImageConfiguration cfg) {
    Path _trianglePath = Path();
    if (cfg.size != null){

      Offset centerTop = Offset(cfg.size!.width / 2, cfg.size!.height - height) + offset;
      Offset bottomLeft = Offset(cfg.size!.width / 2 - (width/2), cfg.size!.height) + offset;
      Offset bottomRight = Offset(cfg.size!.width / 2 + (width/2), cfg.size!.height) + offset;

      _trianglePath.moveTo(bottomLeft.dx, bottomLeft.dy);
      _trianglePath.lineTo(bottomRight.dx, bottomRight.dy);
      _trianglePath.lineTo(centerTop.dx, centerTop.dy);
      _trianglePath.lineTo(bottomLeft.dx, bottomLeft.dy);

      _trianglePath.close();
      canvas.drawPath(_trianglePath, _paint);
  }
  }
}

The main issue with the original answer was that it didn't take into account the 'offset' parameter which controls in this case which tab the indicator is drawn under.

Upvotes: 1

Viren V Varasadiya
Viren V Varasadiya

Reputation: 27177

You can achieve your desire indicator using custom painter and tabindicator.

import 'package:flutter/material.dart';

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

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

class _DeleteState extends State<Delete> with SingleTickerProviderStateMixin {
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 3,
      child: Scaffold(
        appBar: AppBar(
          bottom: TabBar(
            indicatorSize: TabBarIndicatorSize.tab,
            indicator: CircleTabIndicator(color: Colors.orange),
            tabs: <Widget>[
              Tab(
                child: Text(
                  'fruits',
                ),
              ),
              Tab(
                child: Text(
                  'vegetables',
                ),
              ),
              Tab(
                child: Text(
                  'berries',
                ),
              ),
            ],
          ),
        ),
        body: TabBarView(
          children: <Widget>[
            Center(child: Text('Tab 1')),
            Center(child: Text('Tab 2')),
            Center(child: Text('Tab 3')),
          ],
        ),
      ),
    );
  }
}

class CircleTabIndicator extends Decoration {
  final BoxPainter _painter;

  CircleTabIndicator({@required Color color})
      : _painter = _CirclePainter(color);

  @override
  BoxPainter createBoxPainter([onChanged]) => _painter;
}

class _CirclePainter extends BoxPainter {
  final Paint _paint;

  _CirclePainter(Color color)
      : _paint = Paint()
          ..color = color
          ..isAntiAlias = true;

  @override
  void paint(Canvas canvas, Offset offset, ImageConfiguration cfg) {
    Path _trianglePath = Path();

    _trianglePath.moveTo(cfg.size.width / 2 - 10, cfg.size.height);
    _trianglePath.lineTo(cfg.size.width / 2 + 10, cfg.size.height);
    _trianglePath.lineTo(cfg.size.width / 2, cfg.size.height - 10);
    _trianglePath.lineTo(cfg.size.width / 2 - 10, cfg.size.height);
    _trianglePath.close();
    canvas.drawPath(_trianglePath, _paint);
  }
}

Upvotes: 1

Related Questions