Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/basic_dart_check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ jobs:

- name: 🛠️ Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
flutter-version: 3.22.3
- run: flutter --version

- name: ⚙️ Install dependencies
run: flutter pub get
Expand Down
21 changes: 12 additions & 9 deletions example/lib/screens/examples/box_example_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ class BoxExampleScreen extends StatelessWidget {
TextDecorator.boxed(
style: BoxStyle.bubble,
text: const Text(
'Bubble Text 2',
style: TextStyle(fontSize: 32),
'Franz jagt im komplett verwahrlosten Taxi quer durch Berlin',
style: TextStyle(fontSize: 16),
),
strokeWidth: 2,
borderRadius: 16,
Expand All @@ -63,14 +63,17 @@ class BoxExampleScreen extends StatelessWidget {
borderRadius: 16,
),
const SizedBox(height: 32),
TextDecorator.boxed(
style: BoxStyle.curled,
text: const Text(
'Franz jagt im komplett verwahrlosten Taxi quer durch Berlin',
style: TextStyle(fontSize: 16),
Padding(
padding: const EdgeInsets.all(16.0),
child: TextDecorator.boxed(
style: BoxStyle.curled,
text: const Text(
'Franz jagt im komplett verwahrlosten Taxi quer durch Berlin',
style: TextStyle(fontSize: 16),
),
strokeWidth: 2,
borderRadius: 16,
),
strokeWidth: 2,
borderRadius: 16,
),
],
),
Expand Down
3 changes: 1 addition & 2 deletions lib/flutter_text_decorator.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@

export 'src/modules/base/styles.dart';
export 'src/widgets/text_draw_decorator.dart';
export 'src/widgets/text_decorator.dart';
17 changes: 13 additions & 4 deletions lib/src/modules/box/enums/box_style.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,31 @@ enum BoxStyle {
/// - `strokeWidth`: Intended for the thickness of the box outline.
///
/// See individual enum value documentation for notes on parameter usage for specific styles.
CustomPainter getPainter(Text text, double borderRadius, double strokeWidth) {
CustomPainter getPainter(Text text, double borderRadius, double strokeWidth, EdgeInsets padding) {
switch (this) {
case BoxStyle.rounded:
return RoundedBoxPainter(text: text, borderRadius: borderRadius, strokeWidth: strokeWidth);
return RoundedBoxPainter(
text: text,
borderRadius: borderRadius,
strokeWidth: strokeWidth,
padding: padding,
);
case BoxStyle.bubble:
return BubbleBoxPainter(
text: text,
padding: 4,
bubbleColor: Colors.orange,
tip: const BubbleBoxTip(
position: TipPosition.left,
orientation: TipOrientation.left,
),
padding: padding,
);
case BoxStyle.curled:
return WavyBoxPainter(text: text, borderColor: Colors.black);
return WavyBoxPainter(
text: text,
borderColor: Colors.black,
padding: padding,
);
}
}
}
36 changes: 20 additions & 16 deletions lib/src/modules/box/painter/bubble_box_painter.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_text_decorator/src/modules/box/decorations/bubble_box_tip.dart';

Expand All @@ -22,14 +24,14 @@ import 'package:flutter_text_decorator/src/modules/box/decorations/bubble_box_ti
class BubbleBoxPainter extends CustomPainter {
BubbleBoxPainter({
required this.text,
required this.padding,
required this.bubbleColor,
required this.padding,
super.repaint,
this.borderRadius = 8,
this.tip = const BubbleBoxTip(),
});
final Text text;
final double padding; //! TODO: fix text not being centered
final EdgeInsets padding;
final Color bubbleColor;
final double borderRadius;
final BubbleBoxTip tip;
Expand All @@ -48,29 +50,31 @@ class BubbleBoxPainter extends CustomPainter {
)..layout();

final textWidth = textPainter.width;
final availableWidth = min(textWidth, size.width);
final textHeight = textPainter.height;
final availableHeight = max(textHeight, size.height);

// Calculate bubble size
final bubbleWidth = textWidth + padding * 2;
final bubbleHeight = textHeight + padding * 2;
final bubbleWidth = availableWidth + padding.horizontal;
final bubbleHeight = availableHeight + padding.vertical;

// Calculate tail size
//! TODO: extract
// !TODO: extract
final tailHeight = bubbleHeight * 0.25;

final path = Path()

// Top left corner
..moveTo(0, borderRadius)
..quadraticBezierTo(0, 0, borderRadius, 0)
..moveTo(0 - padding.left, borderRadius - padding.top)
..quadraticBezierTo(0 - padding.left, 0 - padding.top, borderRadius - padding.left, 0 - padding.top)

// Top right corner
..lineTo(bubbleWidth - borderRadius, 0)
..quadraticBezierTo(bubbleWidth, 0, bubbleWidth, borderRadius)
..lineTo(bubbleWidth - borderRadius - padding.left, 0 - padding.top)
..quadraticBezierTo(bubbleWidth - padding.left, 0 - padding.top, bubbleWidth - padding.left, borderRadius - padding.top)

// Bottom right corner
..lineTo(bubbleWidth, bubbleHeight - borderRadius)
..quadraticBezierTo(bubbleWidth, bubbleHeight, bubbleWidth - borderRadius, bubbleHeight);
..lineTo(bubbleWidth - padding.left, bubbleHeight - borderRadius - padding.top)
..quadraticBezierTo(bubbleWidth - padding.left, bubbleHeight - padding.top, bubbleWidth - borderRadius - padding.left, bubbleHeight - padding.top);

final tipOffset = bubbleWidth * 0.05;

Expand All @@ -84,11 +88,11 @@ class BubbleBoxPainter extends CustomPainter {

// Bottom left corner with tail
path
..lineTo(tipStart, bubbleHeight)
..lineTo(tipPeak, bubbleHeight + tailHeight)
..lineTo(tipEnd, bubbleHeight)
..lineTo(borderRadius, bubbleHeight)
..quadraticBezierTo(0, bubbleHeight, 0, bubbleHeight - borderRadius)
..lineTo(tipStart, bubbleHeight - padding.top)
..lineTo(tipPeak, bubbleHeight + tailHeight - padding.top)
..lineTo(tipEnd, bubbleHeight - padding.top)
..lineTo(borderRadius - padding.left, bubbleHeight - padding.top)
..quadraticBezierTo(0 - padding.left, bubbleHeight - padding.top, 0 - padding.left, bubbleHeight - borderRadius - padding.top)
..close();

// Draw the bubble
Expand Down
32 changes: 24 additions & 8 deletions lib/src/modules/box/painter/rounded_box_painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,22 @@ class RoundedBoxPainter extends CustomPainter {
required this.text,
required this.borderRadius,
required this.strokeWidth,
required this.padding,
super.repaint,
});
final Text text;
final double borderRadius;
final double strokeWidth;
final EdgeInsets padding;

@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..strokeWidth = strokeWidth
..style = PaintingStyle.stroke;

// TODO(everyone): Extract and make generic
final textSpan = TextSpan(text: text.data, style: text.style);
final textPainter = TextPainter(
text: textSpan,
textDirection: TextDirection.ltr,
)..layout();
final textPainter = TextPainter(text: textSpan, textDirection: TextDirection.ltr)..layout();

final textWidth = textPainter.width;
final boxWidth = min(textWidth, size.width);
Expand All @@ -60,18 +58,36 @@ class RoundedBoxPainter extends CustomPainter {
boxHeight = nLines * textHeight;

final centerOffset = Offset(
size.width / 2,
size.height / 2,
_getOffsetDx(size),
_getOffsetDy(size),
);

final rrect = RRect.fromRectAndRadius(
Rect.fromCenter(center: centerOffset, width: boxWidth, height: boxHeight),
Rect.fromCenter(center: centerOffset, width: boxWidth + padding.horizontal, height: boxHeight + padding.vertical),
Radius.circular(borderRadius),
);

canvas.drawRRect(rrect, paint);
}

double _getOffsetDx(Size size) {
var dx = size.width / 2;
if (padding.left != padding.right) {
final diff = (padding.left - padding.right).abs() / 2;
dx = padding.left < padding.right ? dx + diff : dx - diff;
}
return dx;
}

double _getOffsetDy(Size size) {
var dy = size.height / 2;
if (padding.top != padding.bottom) {
final diff = (padding.top - padding.bottom).abs() / 2;
dy = padding.top < padding.bottom ? dy + diff : dy - diff;
}
return dy;
}

@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
Expand Down
36 changes: 21 additions & 15 deletions lib/src/modules/box/painter/wavy_box_painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,18 @@ class WavyBoxPainter extends CustomPainter {
WavyBoxPainter({
required this.text,
required this.borderColor,
required this.padding,
this.nHorizontalSegments = 12,
this.nVerticalSegments = 3,
super.repaint,
});
// TODO(everyone): add padding?
// TODO(everyone): add fill color?
final Text text;
final Color borderColor;
final EdgeInsets padding;
final int nHorizontalSegments;
final int nVerticalSegments;

@override
void paint(Canvas canvas, Size size) {
Expand All @@ -63,44 +69,44 @@ class WavyBoxPainter extends CustomPainter {
final nLines = heightFactor.ceil();
textHeight = nLines * textHeight;

const nHorizontalSegments = 10;
final availableWidth = min(textWidth, size.width);
final lengthSegment = (availableWidth / nHorizontalSegments).ceil();
final nVerticalSegments = (textHeight / lengthSegment).ceil();
final availableWidth = min(textWidth, size.width) + padding.horizontal;
final availableHeight = max(textHeight, size.height) + padding.vertical;
final widthHorizontalSegment = availableWidth / nHorizontalSegments;
final heightVerticalSegment = availableHeight / nVerticalSegments;

const arcHeight = -10.0;
const weight = 1.0;
var lastX2 = 0.0;
var lastX2 = 0.0 - padding.left;

// TODO(everyone): Fix corners, maybe with [arcTo]?
final path = Path()..moveTo(0, 0);
final path = Path()..moveTo(0 - padding.left, 0 - padding.top);

// Upper left to upper right
for (var i = 1; i <= nHorizontalSegments; i++) {
final x2 = i * lengthSegment / 1.0;
path.conicTo(lastX2 + lengthSegment / 2, arcHeight, x2 / 1.0, 0, weight);
final x2 = (i * widthHorizontalSegment / 1.0) - padding.left;
path.conicTo(lastX2 + widthHorizontalSegment / 2, arcHeight - padding.top, x2 / 1.0, 0 - padding.top, weight);
lastX2 = x2;
}

// Upper right to lower right
double lastY2 = 0;
var lastY2 = 0 - padding.top;
for (var i = 1; i <= nVerticalSegments; i++) {
final y2 = i * lengthSegment / 1.0;
path.conicTo(lastX2 - arcHeight, lastY2 + lengthSegment / 2, lastX2, y2, weight);
final y2 = (i * heightVerticalSegment / 1.0) - padding.top;
path.conicTo(lastX2 - arcHeight, lastY2 + heightVerticalSegment / 2, lastX2, y2, weight);
lastY2 = y2;
}

// Lower right to lower left
for (var i = nHorizontalSegments - 1; i >= 0; i--) {
final x2 = i * lengthSegment / 1.0;
path.conicTo(lastX2 - lengthSegment / 2, lastY2 - arcHeight, x2 / 1.0, lastY2, weight);
final x2 = (i * widthHorizontalSegment / 1.0) - padding.left;
path.conicTo(lastX2 - widthHorizontalSegment / 2, lastY2 - arcHeight, x2 / 1.0, lastY2, weight);
lastX2 = x2;
}

// Lower left to upper right
for (var i = nVerticalSegments - 1; i >= 0; i--) {
final y2 = i * lengthSegment / 1.0;
path.conicTo(arcHeight, y2 + lengthSegment / 2, lastX2, y2, weight);
final y2 = (i * heightVerticalSegment / 1.0) - padding.top;
path.conicTo(arcHeight - padding.left, y2 + heightVerticalSegment / 2, 0 - padding.left, y2, weight);
lastY2 = y2;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import 'package:flutter_text_decorator/src/modules/underline/enums/underline_sty
/// ```
///
class TextDecorator extends StatelessWidget {

const TextDecorator({
required this.text,
required this.painter,
Expand Down Expand Up @@ -131,10 +130,11 @@ class TextDecorator extends StatelessWidget {
BoxStyle style = BoxStyle.bubble,
double borderRadius = 1,
double strokeWidth = 1,
EdgeInsets padding = const EdgeInsets.all(8),
}) {
return TextDecorator(
text: text,
painter: style.getPainter(text, borderRadius, strokeWidth),
painter: style.getPainter(text, borderRadius, strokeWidth, padding),
);
}

Expand Down
25 changes: 25 additions & 0 deletions test/flutter_test_config.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'dart:async';

import 'package:flutter_test/flutter_test.dart';

import 'utils/local_file_comparer.dart';

const _kGoldenTestsThreshold = 0.5 / 100;

Future<void> testExecutable(FutureOr<void> Function() testMain) async {
if (goldenFileComparator is LocalFileComparator) {
final testUrl = (goldenFileComparator as LocalFileComparator).basedir;

goldenFileComparator = LocalFileComparatorWithThreshold(
Uri.parse('$testUrl/test.dart'),
_kGoldenTestsThreshold,
);
} else {
throw Exception(
'Expected `goldenFileComparator` to be of type `LocalFileComparator`, '
'but it is of type `${goldenFileComparator.runtimeType}`',
);
}

await testMain();
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading