如何在Flutter中创建自定义气泡形状?

5

我正在尝试创建一个带三角形的自定义工具提示。我已经创建了气泡,但如何在其中添加三角形而不使用任何库?

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


你需要一个自定义的ShapeBorder: https://dev59.com/a7bna4cB1Zd3GeqPZFM1#57943257 - pskink
4个回答

14
你可以通过CustomPainter来完成它,而不需要使用任何库。 示例1: 创建Custom Painter类,
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)
      ..lineTo(width / 2 - triangleW / 2, height);
    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;
}

用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,
            )),
      ),
    );

这里输入图片描述

示例2: 查看下面的工具提示形状装饰示例代码

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


谢谢您的回复!您能否指导我如何在工具提示小部件中使用它? - Nasir Khan
嗨,@NasirKhan,我更新了我的答案,请查看示例2,希望能对你有所帮助。 - Jai Techie
感谢@Jai Techie的回复,非常有帮助。我想要讨论的一件事是,我有一个图标,它位于屏幕的极右和极左位置,但是对于该图标,指向三角形的位置仍然在中心,而不是根据图标改变。另外,是否有办法固定提示框的宽度和高度?我正在使用边距,这很好用,但如果我有不同大小的屏幕,这并不是一个好方法。 - Nasir Khan

0

尝试使用这个包 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

0
您可以使用pub.dev上的软件包。
软件包名称是:just_the_tooltip: ^0.0.12

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接