Created
February 2, 2023 13:46
-
-
Save kekland/c2b37533c92a348d8595a39583215fd1 to your computer and use it in GitHub Desktop.
Reverse ellipse text
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
const Color darkBlue = Color.fromARGB(255, 18, 32, 47); | |
void main() { | |
runApp(MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
theme: ThemeData.dark().copyWith( | |
scaffoldBackgroundColor: darkBlue, | |
), | |
debugShowCheckedModeBanner: false, | |
home: Scaffold( | |
body: Center( | |
child: MyWidget(), | |
), | |
), | |
); | |
} | |
} | |
class MyWidget extends StatefulWidget { | |
@override | |
State<MyWidget> createState() => _MyWidgetState(); | |
} | |
class _MyWidgetState extends State<MyWidget> { | |
double _width = 300.0; | |
void _increaseWidth() { | |
setState(() => _width += 10.0); | |
} | |
void _decreaseWidth() { | |
setState(() => _width -= 10.0); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Center( | |
child: Column( | |
mainAxisSize: MainAxisSize.min, | |
children: [ | |
Container( | |
constraints: BoxConstraints( | |
minWidth: 0.0, | |
maxWidth: _width, | |
maxHeight: 100.0, | |
), | |
color: Colors.green, | |
child: Center( | |
child: ReverseEllipsisWidget( | |
text: 'hello world world world long world.pdf', | |
style: Theme.of(context).textTheme.bodyLarge!, | |
), | |
), | |
), | |
Row( | |
mainAxisSize: MainAxisSize.min, | |
children: [ | |
IconButton( | |
icon: const Icon(Icons.remove), | |
onPressed: _decreaseWidth, | |
), | |
IconButton( | |
icon: const Icon(Icons.add), | |
onPressed: _increaseWidth, | |
), | |
], | |
), | |
], | |
), | |
); | |
} | |
} | |
class ReverseEllipsisWidget extends StatelessWidget { | |
const ReverseEllipsisWidget( | |
{required this.text, required this.style, super.key}); | |
final String text; | |
final TextStyle style; | |
String _reverseString(String v) => v.split('').reversed.join(''); | |
String _reverseStringAndInsertBreakingSpaces(String v) => | |
v.split('').reversed.join('\u200b'); | |
String _computeTextForReverseEllipsis( | |
BuildContext context, | |
String text, | |
BoxConstraints constraints, | |
) { | |
// Insert breakable spaces between characters. | |
// See https://github.com/flutter/flutter/issues/61081#issuecomment-1103330522 | |
final reversedText = _reverseStringAndInsertBreakingSpaces(text); | |
final textDirection = Directionality.of(context); | |
final painter = TextPainter( | |
text: TextSpan( | |
text: reversedText, | |
style: style, | |
), | |
textDirection: textDirection, | |
); | |
// Check if the text can fit in 1 line | |
painter.layout(maxWidth: constraints.maxWidth); | |
if (painter.computeLineMetrics().length == 1) { | |
return text; | |
} | |
// If not, we need to cut the text. Prepare the ellipsis painter | |
final ellipsisPainter = TextPainter( | |
text: TextSpan(text: '...', style: style), | |
textDirection: textDirection, | |
); | |
ellipsisPainter.layout(); | |
// Compute how much space do we have for the text line including the | |
// ellipsis | |
final maxWidth = constraints.maxWidth; | |
final maxWidthForText = maxWidth - ellipsisPainter.width; | |
painter.layout(maxWidth: maxWidthForText); | |
// Get the number of characters that fit into one line | |
final boundary = painter.getLineBoundary( | |
const TextPosition(offset: 0), | |
); | |
// Cut the text and reverse it back | |
final cutText = _reverseString( | |
reversedText.substring(boundary.start, boundary.end), | |
); | |
return '...$cutText'; | |
} | |
@override | |
Widget build(BuildContext context) { | |
return LayoutBuilder( | |
builder: (context, constraints) { | |
final cutText = | |
_computeTextForReverseEllipsis(context, text, constraints); | |
return Text(cutText, style: style, maxLines: 1); | |
}, | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment