Skip to content

Instantly share code, notes, and snippets.

@yeasin50
Last active September 9, 2024 16:25
Show Gist options
  • Save yeasin50/275c99ba9b18575e831f74ef341c3969 to your computer and use it in GitHub Desktop.
Save yeasin50/275c99ba9b18575e831f74ef341c3969 to your computer and use it in GitHub Desktop.
pointyToolTip shapeBox: shapeBorder
import 'package:flutter/material.dart';
///! Question >> https://stackoverflow.com/q/78861360/10157127
/// Create a pointy shape like toolTip
///
/// ```dart
/// Container(
/// decoration: ShapeDecoration(
/// shape: PointyShape(
/// padding: const EdgeInsets.all(12),
/// pointOffsetX: pointX,
/// pointyHeight: 24,
/// radiusValue: 16,
/// isTop : true,
/// ),
/// color: Colors.white,
/// ),
///```
///
class PointyShape extends OutlinedBorder {
const PointyShape({
this.pointOffsetX = .85,
this.radiusValue = 16,
this.pointyHeight = 24,
this.padding = const EdgeInsets.all(12),
this.isTop = true,
}) : assert(pointOffsetX >= 0 && pointOffsetX <= 1, "offset should be within 0,1");
final double pointOffsetX;
final double radiusValue;
final double pointyHeight;
final EdgeInsets padding;
final bool isTop;
@override
OutlinedBorder copyWith({BorderSide? side}) => this;
@override
EdgeInsetsGeometry get dimensions => padding.copyWith(
bottom: padding.bottom + (isTop ? 0 : pointyHeight),
top: padding.top + (isTop ? pointyHeight : 0),
);
@override
Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
return getOuterPath(rect, textDirection: textDirection);
}
@override
Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
final rRect = RRect.fromRectAndRadius(
Rect.fromLTRB(
rect.left,
rect.top + (isTop ? pointyHeight : 0),
rect.right,
rect.bottom - (isTop ? 0 : pointyHeight),
),
Radius.circular(radiusValue),
);
final pointyStartX = rect.right - (rect.width * (1 - pointOffsetX)) - pointyHeight / 2;
final toolTip = isTop
? (Path()
..moveTo(pointyStartX + pointyHeight / 2, rect.top)
..lineTo(pointyStartX, rect.top + pointyHeight)
..lineTo(pointyStartX + (pointyHeight), rect.top + pointyHeight)
..lineTo(pointyStartX + pointyHeight / 2, rect.top))
: Path()
..moveTo(
pointyStartX,
rect.bottom - pointyHeight,
)
..lineTo(pointyStartX + (pointyHeight / 2), rect.bottom)
..lineTo(pointyStartX + (pointyHeight), rect.bottom - pointyHeight)
..lineTo(pointyStartX, rect.bottom - pointyHeight);
return Path.combine(PathOperation.union, Path()..addRRect(rRect), toolTip);
}
@override
void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {}
@override
ShapeBorder scale(double t) => this;
}
void main() => runApp(const MaterialApp(home: MyApp()));
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
double pointX = .85;
bool isTop = false;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.deepPurple,
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
decoration: ShapeDecoration(
shape: PointyShape(
padding: const EdgeInsets.all(12),
pointOffsetX: pointX,
isTop: isTop,
pointyHeight: 24,
radiusValue: 16,
),
color: Colors.white,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
"header",
style: TextStyle(fontSize: 34),
),
Text(
"body s " * 22,
),
],
),
),
const SizedBox(height: 32),
SwitchListTile(
value: isTop,
onChanged: (value) {
setState(() {
isTop = value;
});
},
),
Slider(
min: .1,
max: .9, //consider padding+radius
value: pointX,
onChanged: (v) {
setState(() {
pointX = v;
});
},
)
],
),
),
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment