Flutter arrow in border

Viewed 56

I want to add a lightweight navigation bar to switch between Login and Registration. The result should look like this:

enter image description here

The arrow should indicate the current page selected.

2 Answers

Refer to this question, the UI in the question is similar to yours and the concept used in the answers can be tweaked to your desire.

This is what worked for me:

class TapBarDesign extends StatefulWidget {
  const TapBarDesign({Key? key}) : super(key: key);

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

class _TapBarDesignState extends State<TapBarDesign>
    with SingleTickerProviderStateMixin {
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 2,
      child: Scaffold(
        backgroundColor: Colors.blue,
        appBar: AppBar(
          toolbarHeight: 0,
          backgroundColor: Colors.transparent,
          elevation: 0,
          bottom: TabBar(
            indicatorSize: TabBarIndicatorSize.tab,
            indicator: ArrowTabBarIndicator(),
            tabs: const <Widget>[
              Tab(
                child: Text(
                  'Page 1',
                ),
              ),
              Tab(
                child: Text(
                  'Page 2',
                ),
              ),
            ],
          ),
        ),
        body: const TabBarView(
          children: <Widget>[
            Center(child: Text('Page 1')),
            Center(child: Text('Page 2')),
          ],
        ),
      ),
    );
  }
}

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
          ..strokeWidth = 1
          ..strokeCap = StrokeCap.round;

  @override
  void paint(Canvas canvas, Offset offset, ImageConfiguration cfg) {
    const pointMode = ui.PointMode.polygon;

    if (cfg.size != null) {
      final points = [
        Offset(0, cfg.size!.height),
        Offset(cfg.size!.width / 2 - (width / 2), cfg.size!.height) + offset,
        Offset(cfg.size!.width / 2, (cfg.size!.height + height)) + offset,
        Offset(cfg.size!.width / 2 + (width / 2), cfg.size!.height) + offset,
        Offset(cfg.size!.width * 2, cfg.size!.height),
      ];
      canvas.drawPoints(pointMode, points, _paint);
    }
  }
}
Related