7

I am trying to create a custom tooltip with the triangle shape on either side. I have created a bubble but how to add the triangle in there without using any library?

class SdToolTip extends StatelessWidget {
  final Widget child;
  final String message;

  const SdToolTip({
    required this.message,
    required this.child,
  });

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Tooltip(
        child: child,
        message: message,
        padding: const EdgeInsets.all(8),
        decoration: BoxDecoration(
            color: Colors.blueAccent.withOpacity(0.6),
            borderRadius: BorderRadius.circular(22)),
        textStyle: const TextStyle(
            fontSize: 15, fontStyle: FontStyle.italic, color: Colors.white),
      ),
    );
  }
}

enter image description here

1

4 Answers 4

17

You can do it by CustomPainter without any library.

Example 1: Create Custom Painter Class,

class customStyleArrow extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
      ..color = Colors.white
      ..strokeWidth = 1
      ..style = PaintingStyle.fill;

    final double triangleH = 10;
    final double triangleW = 25.0;
    final double width = size.width;
    final double height = size.height;

    final Path trianglePath = Path()
      ..moveTo(width / 2 - triangleW / 2, height)
      ..lineTo(width / 2, triangleH + height)
      ..lineTo(width / 2 + triangleW / 2, height)
      ..close();

    canvas.drawPath(trianglePath, paint);
    final BorderRadius borderRadius = BorderRadius.circular(15);
    final Rect rect = Rect.fromLTRB(0, 0, width, height);
    final RRect outer = borderRadius.toRRect(rect);
    canvas.drawRRect(outer, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

Wrap your text widget with CustomPaint,

return CustomPaint(
      painter: customStyleArrow(),
      child: Container(
        padding: EdgeInsets.only(left: 15, right: 15, bottom: 20, top: 20),
        child: Text("This is the custom painter for arrow down curve",
            style: TextStyle(
              color: Colors.black,
            )),
      ),
    );

enter image description here

Example 2: Check below example code for tooltip shapedecoration

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Customize Tooltip'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final String title;

  const MyHomePage({
    Key? key,
    required this.title,
  }) : super(key: key);

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Tooltip(
          child: const IconButton(
            icon: Icon(Icons.info, size: 30.0),
            onPressed: null,
          ),
          message: 'Hover Icon for Tooltip...',
          padding: const EdgeInsets.all(20),
          showDuration: const Duration(seconds: 10),
          decoration: ShapeDecoration(
            color: Colors.blue,
            shape: ToolTipCustomShape(),
          ),
          textStyle: const TextStyle(color: Colors.white),
          preferBelow: false,
          verticalOffset: 20,
        ),
      ),
    );
  }
}

class ToolTipCustomShape extends ShapeBorder {
  final bool usePadding;

  ToolTipCustomShape({this.usePadding = true});

  @override
  EdgeInsetsGeometry get dimensions =>
      EdgeInsets.only(bottom: usePadding ? 20 : 0);

  @override
  Path getInnerPath(Rect rect, {TextDirection? textDirection}) => Path();

  @override
  Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
    rect =
        Rect.fromPoints(rect.topLeft, rect.bottomRight - const Offset(0, 20));
    return Path()
      ..addRRect(
          RRect.fromRectAndRadius(rect, Radius.circular(rect.height / 3)))
      ..moveTo(rect.bottomCenter.dx - 10, rect.bottomCenter.dy)
      ..relativeLineTo(10, 20)
      ..relativeLineTo(10, -20)
      ..close();
  }

  @override
  void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {}

  @override
  ShapeBorder scale(double t) => this;
}

enter image description here

3
  • thank you for your response!! can you please also guide me how can I use this in a tooltip widget
    – Nasir Khan
    Commented Mar 18, 2022 at 14:39
  • Hi, @NasirKhan I updated my answer check example 2 hope it helps you.
    – Jai Techie
    Commented Mar 19, 2022 at 16:24
  • thank you @Jai Techie for your response it's super helpful... there is one thing i want to discuss is that I have icon which is extreme right and extreme left of the screen but for that icon, the position of the pointing triangle is still on center it's not changing according to the icon. Also is there any way to have a fix the width and height of the tooltip? i am using margin which is working fine but it is not a good approach if i have different size screens
    – Nasir Khan
    Commented Mar 28, 2022 at 10:10
0

Wrap your widget with CustomPaint refer to this article https://medium.com/flutter-community/a-deep-dive-into-custompaint-in-flutter-47ab44e3f216 and documentation for more info, should do the trick.

0

Try this package https://pub.dev/packages/shape_of_view_null_safe

ShapeOfView(
  shape: BubbleShape(
    position: BubblePosition.Bottom,
    arrowPositionPercent: 0.5,
    borderRadius: 20,
    arrowHeight: 10,
    arrowWidth: 10
  ),
//Your Data goes here

  child: ...,
)
0

You can use the package from the pub.dev

the package name is: just_the_tooltip: ^0.0.12

Not the answer you're looking for? Browse other questions tagged or ask your own question.