Flutter- How to have differing Text Styles in List - ios

I have a list that is populated with the return data of various var's
This list also has a text element preceding the data
I want to have separate weight, size etc for the initial text and then the String being passed in
I have tried RichText and TextStyle but needed a text function, not a list for it to work correctly
Here is the List which is then passed into a function which pushes it to ShowDialog
For example, Line: to be FontWeight.w600 & $pln String to be FontWeight.w400
List data = [
"Line: $pln",
"Departed from: $originname",
"Destination: $destname",
"Last seen: $recordedattime",
];
ShowDialog, where message is the list from previous function
title: Text(title),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text(message,
style: TextStyle(
fontSize: 18,
color: Color(0xff2F2F2F),
fontWeight: FontWeight.w500,
...

Try creating a new widget for it, this way you can modify the build however you desire easily.
class NewTextWidget extends StatelessWidget {
final String prefix;
final String value;
NewTextWidget({required String message, Key? key})
: prefix = message.split(": ")[0],
value = message.split(": ")[1],
super(key: key);
#override
Widget build(BuildContext context) {
return RichText(
text: TextSpan(
text: prefix,
style: const TextStyle(fontWeight: FontWeight.w600),
children: [
TextSpan(
text: value,
style: const TextStyle(fontWeight: FontWeight.w400))
]),
);
}
}
The NewTextWidget expects the message with the prefix: value format.
So when you call it you need to update your ListBody as:
ListBody(
children: data.map((String message) => NewTextWidget(message: message)).toList()
)

Related

How to highlight a word in string in flutter programmatically

Is there any way to change color on particular word in a string ?
Text("some long string")
now i want to give color to only long word.
can someone tell me how can i do this programatically ?
eg:-
I am long a really long and long string in some variable, a long one
now here i want to seperate long word.
I can seperate simple string to highlight one word but not sure how to find and highlight each of these words.
Wrap the word in a TextSpan and assign style properties to change the text appearance and use RichText instead of Text
RichText(
text: TextSpan(
text: 'Hello ',
style: DefaultTextStyle.of(context).style,
children: <TextSpan>[
TextSpan(text: 'bold', style: TextStyle(fontWeight: FontWeight.bold)),
TextSpan(text: ' world!'),
],
),
)
or use the Text.rich constructor https://docs.flutter.io/flutter/widgets/Text-class.html
const Text.rich(
TextSpan(
text: 'Hello', // default text style
children: <TextSpan>[
TextSpan(text: ' beautiful ', style: TextStyle(fontStyle: FontStyle.italic)),
TextSpan(text: 'world', style: TextStyle(fontWeight: FontWeight.bold)),
],
),
)
Here is my code.
import 'package:flutter/material.dart';
class HighlightText extends StatelessWidget {
final String text;
final String highlight;
final TextStyle style;
final TextStyle highlightStyle;
final Color highlightColor;
final bool ignoreCase;
HighlightText({
Key key,
this.text,
this.highlight,
this.style,
this.highlightColor,
TextStyle highlightStyle,
this.ignoreCase: false,
}) : assert(
highlightColor == null || highlightStyle == null,
'highlightColor and highlightStyle cannot be provided at same time.',
),
highlightStyle = highlightStyle ?? style?.copyWith(color: highlightColor) ?? TextStyle(color: highlightColor),
super(key: key);
#override
Widget build(BuildContext context) {
final text = this.text ?? '';
if ((highlight?.isEmpty ?? true) || text.isEmpty) {
return Text(text, style: style);
}
var sourceText = ignoreCase ? text.toLowerCase() : text;
var targetHighlight = ignoreCase ? highlight.toLowerCase() : highlight;
List<TextSpan> spans = [];
int start = 0;
int indexOfHighlight;
do {
indexOfHighlight = sourceText.indexOf(targetHighlight, start);
if (indexOfHighlight < 0) {
// no highlight
spans.add(_normalSpan(text.substring(start)));
break;
}
if (indexOfHighlight > start) {
// normal text before highlight
spans.add(_normalSpan(text.substring(start, indexOfHighlight)));
}
start = indexOfHighlight + highlight.length;
spans.add(_highlightSpan(text.substring(indexOfHighlight, start)));
} while (true);
return Text.rich(TextSpan(children: spans));
}
TextSpan _highlightSpan(String content) {
return TextSpan(text: content, style: highlightStyle);
}
TextSpan _normalSpan(String content) {
return TextSpan(text: content, style: style);
}
}
Extending on zhpoo's awesome answer, here's a widget snippet that would allow you to style/highlight anything in a string programmatically using regular expressions (dart's RegExp).
Link to dartpad demo: https://dartpad.dev/d7a0826ed1201f7247fafd9e65979953
class RegexTextHighlight extends StatelessWidget {
final String text;
final RegExp highlightRegex;
final TextStyle highlightStyle;
final TextStyle nonHighlightStyle;
const RegexTextHighlight({
#required this.text,
#required this.highlightRegex,
#required this.highlightStyle,
this.nonHighlightStyle,
});
#override
Widget build(BuildContext context) {
if (text == null || text.isEmpty) {
return Text("",
style: nonHighlightStyle ?? DefaultTextStyle.of(context).style);
}
List<TextSpan> spans = [];
int start = 0;
while (true) {
final String highlight =
highlightRegex.stringMatch(text.substring(start));
if (highlight == null) {
// no highlight
spans.add(_normalSpan(text.substring(start)));
break;
}
final int indexOfHighlight = text.indexOf(highlight, start);
if (indexOfHighlight == start) {
// starts with highlight
spans.add(_highlightSpan(highlight));
start += highlight.length;
} else {
// normal + highlight
spans.add(_normalSpan(text.substring(start, indexOfHighlight)));
spans.add(_highlightSpan(highlight));
start = indexOfHighlight + highlight.length;
}
}
return RichText(
text: TextSpan(
style: nonHighlightStyle ?? DefaultTextStyle.of(context).style,
children: spans,
),
);
}
TextSpan _highlightSpan(String content) {
return TextSpan(text: content, style: highlightStyle);
}
TextSpan _normalSpan(String content) {
return TextSpan(text: content);
}
}
I recently developed a package called Dynamic Text Highlighting. It let's you to highlight programatically some given words inside a given text.
Take a look at https://pub.dev/packages/dynamic_text_highlighting
Example
Widget buildDTH(String text, List<String> highlights) {
return DynamicTextHighlighting(
text: text,
highlights: highlights,
color: Colors.yellow,
style: TextStyle(
fontSize: 18.0,
fontStyle: FontStyle.italic,
),
caseSensitive: false,
);
}
It is a stateless widget, so for any changes just call setState(() {...}).
void applyChanges(List<String> newHighlights) {
setState(() {
highlights = newHighlights;
});
}
Use this code it would even highlight the query letters, check once
List<TextSpan> highlight(
String main, String query) {
List<TextSpan> children = [];
List<String> abc = query.toLowerCase().split("");
for (int i = 0; i < main.length; i++) {
if (abc.contains(main[i])) {
children.add(TextSpan(
text: main[i],
style: TextStyle(
backgroundColor: Colors.yellow[300],
color: Colors.black,
decoration: TextDecoration.none,
fontFamily: fontName,
fontWeight: FontWeight.bold,
fontSize: 16)));
} else {
children.add(TextSpan(
text: main[i],
style: TextStyle(
color: Colors.black,
decoration: TextDecoration.none,
fontFamily: fontName,
fontWeight: FontWeight.w300,
fontSize: 16)));
}
}
return children;
}
To achieve this, there are several possibilities :
1- Using the Text.rich constructor instead of the Text widget and then inside the constructor, you will use the TextSpan widgets that will allow you to add style through the style property. The first TextSpan directly in Text.rich and then the other TextSpan in the first TextSpan through its children property.
Text.rich(
TextSpan(
text: 'Some ',
children: <TextSpan>[
TextSpan(
text: ' Long ',
style: TextStyle(fontWeight: FontWeight.bold , background: Paint()..color = Colors.red)),
TextSpan(
text: ' world',
style: TextStyle(backgroundColor: Colors.yellow)),
],
),
)
Output :
2- The use of RichText widget . Same as Text.rich but this time the first TextSpan will be put on the text property of the RichText widget.
RichText(
text:TextSpan(
text: 'Some ',
style: TextStyle(color: Colors.blue),
children: <TextSpan>[
TextSpan(
text: ' Long ',
style: TextStyle(color:Colors.black ,fontWeight: FontWeight.bold , background: Paint()..color = Colors.red)),
TextSpan(
text: ' world',
style: TextStyle(backgroundColor: Colors.yellow)),
],
),
)
Output :
3- The use of external packages .
I propose you highlight_text or substring_highlight
You can use this flutter plugin Highlight Text plugin. It is quite a good option to try
that's Good Answer #Gauter Zochbauer. if You want to change dynamically then Follow following answer.
import 'package:flutter/material.dart';
void main() => runApp(new MaterialApp(
title: 'Forms in Flutter',
home: new LoginPage(),
));
class LoginPage extends StatefulWidget {
#override
State<StatefulWidget> createState() => new _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
Color color =Colors.yellowAccent;
#override
Widget build(BuildContext context) {
final Size screenSize = MediaQuery.of(context).size;
return new Scaffold(
appBar: new AppBar(
title: new Text('Login'),
),
body: new Container(
padding: new EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
Text.rich(
TextSpan(
text: 'Hello', // default text style
children: <TextSpan>[
TextSpan(text: ' beautiful ', style: TextStyle(fontStyle: FontStyle.italic,color: color)),
TextSpan(text: 'world', style: TextStyle(fontWeight: FontWeight.bold)),
],
),
),
new RaisedButton(
onPressed: (){
setState(() {
color == Colors.yellowAccent ? color = Colors.red : color = Colors.yellowAccent;
});
},
child: Text("Click Me!!!")
),
],
)
));
}
}

How do i concatenate two strings where one needs to be a static variable from a Widget in flutter

I have this code from a stateful widget which looks like
static String code = '+1';
String phone;
String finalphone = '$code' + '$phone'; =>this declaration brings an error
that 'Only static members can be accessed in initializers'
How am I supposed to bring the two variables together so that i have something that looks like +1535465345 i am collecting user information
//the widget
Widget form() {
return Form(
key: _formKey,
child: TextFormField(
decoration: InputDecoration(
contentPadding: EdgeInsets.all(0.0),
),
style: TextStyle(
letterSpacing: 2.0,
fontSize: 19.0,
fontWeight: FontWeight.bold,
color: Colors.black87),
onSaved: (value) => phone = value, //the (value) here is a
//string which is
//assigned
//to phone variable declared at the top
),
),
);
}
also making the phone variable static and printing out the concatenated string brings out +1null
Instead of having a field, you can have a getter like
String get finalphone => '$code' + '$phone';
Refer this answer
You need to specify the class to access static members
String finalphone = '${MyClass.code}$phone';
sure it will bring an error you use the phone variable before give it a value so it will fire null reference exception .
whatever here is a complete fix hope it will work :
static String code = '+1';
String phone;
String finalphone = "";
//the widget
Widget form() {
return Form(
key: _formKey,
child: TextFormField(
decoration: InputDecoration(
contentPadding: EdgeInsets.all(0.0),
),
style: TextStyle(
letterSpacing: 2.0,
fontSize: 19.0,
fontWeight: FontWeight.bold,
color: Colors.black87),
onSaved: (value) {phone = value; finalphone = '$code' + '$phone'; }
),
),
);
you may need to use setState to asign value and rebuild the view .

How to check if Flutter Text widget was overflowed

I have a Text widget which can be truncated if it exceeds a certain size:
ConstrainedBox(
constraints: BoxConstraints(maxHeight: 50.0),
child: Text(
widget.review,
overflow: TextOverflow.ellipsis,
)
);
Or max number of lines:
RichText(
maxLines: 2,
overflow: TextOverflow.ellipsis,
text: TextSpan(
style: TextStyle(color: Colors.black),
text: widget.review,
));
My goal is to have the text expandable only if an overflow occurred. Is there a proper way of checking if the text overflowed?
What I've tried
I have found that in RichText, there is a RenderParagraph renderObject , which has a private property TextPainter _textPainter which has a bool didExceedMaxLines.
In short, I just need to access richText.renderObject._textPainter.didExceedMaxLines but as you can see, it is made private with the underscore.
I found a way to do it. Full code below, but in short:
Use a LayoutBuilder to determine how much space we have.
Use a TextPainter to simulate the render of the text within the space.
Here's the full demo app:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Text Overflow Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(title: Text("DEMO")),
body: TextOverflowDemo(),
),
);
}
}
class TextOverflowDemo extends StatefulWidget {
#override
_EditorState createState() => _EditorState();
}
class _EditorState extends State<TextOverflowDemo> {
var controller = TextEditingController();
#override
void initState() {
controller.addListener(() {
setState(() {
mytext = controller.text;
});
});
controller.text = "This is a long overflowing text!!!";
super.initState();
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
String mytext = "";
#override
Widget build(BuildContext context) {
int maxLines = 1;
double fontSize = 30.0;
return Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
children: <Widget>[
LayoutBuilder(builder: (context, size) {
// Build the textspan
var span = TextSpan(
text: mytext,
style: TextStyle(fontSize: fontSize),
);
// Use a textpainter to determine if it will exceed max lines
var tp = TextPainter(
maxLines: maxLines,
textAlign: TextAlign.left,
textDirection: TextDirection.ltr,
text: span,
);
// trigger it to layout
tp.layout(maxWidth: size.maxWidth);
// whether the text overflowed or not
var exceeded = tp.didExceedMaxLines;
return Column(children: <Widget>[
Text.rich(
span,
overflow: TextOverflow.ellipsis,
maxLines: maxLines,
),
Text(exceeded ? "Overflowed!" : "Not overflowed yet.")
]);
}),
TextField(
controller: controller,
),
],
),
);
}
}
There is a shorter way to get an answer if text is overflowed or not. You just need to define textStyle and get the answer from this method
bool hasTextOverflow(
String text,
TextStyle style,
{double minWidth = 0,
double maxWidth = double.infinity,
int maxLines = 2
}) {
final TextPainter textPainter = TextPainter(
text: TextSpan(text: text, style: style),
maxLines: maxLines,
textDirection: TextDirection.ltr,
)..layout(minWidth: minWidth, maxWidth: maxWidth);
return textPainter.didExceedMaxLines;
}
You can use a flutter plug-in auto_size_text at pub.dev.
When the text is get overflowed, you can set some widget to be appeared.
int maxLines = 3;
String caption = 'Your caption is here';
return AutoSizeText(
caption,
maxLines: maxLines,
style: TextStyle(fontSize: 20),
minFontSize: 15,
overflowReplacement: Column( // This widget will be replaced.
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
caption,
maxLines: maxLines,
overflow: TextOverflow.ellipsis,
),
Text(
"Show more",
style: TextStyle(color: PrimaryColor.kGrey),
)
],
),
);
Made my own Widget i use it cross the project, it take Text widget in the constructor and reads the properties of it.
import 'package:flutter/material.dart';
class OverflowDetectorText extends StatelessWidget {
final Text child;
OverflowDetectorText({
Key? key,
required this.child,
}) : super(key: key);
#override
Widget build(BuildContext context) {
var tp = TextPainter(
maxLines: child.maxLines,
textAlign: child.textAlign ?? TextAlign.start,
textDirection: child.textDirection ?? TextDirection.ltr,
text: child.textSpan ?? TextSpan(
text: child.data,
style: child.style,
),
);
return LayoutBuilder(
builder: (context, constrains) {
tp.layout(maxWidth: constrains.maxWidth);
final overflowed = tp.didExceedMaxLines;
if (overflowed) {
//You can wrap your Text `child` with anything
}
return child;
},
);
}
}

Displaying text with Emojis on Flutter

I have some texts that contain emojis and I'm trying to show them on the Text widget. However, they seem to be shown as foreign characters. Does Flutter support showing emojis? should work for both iOS and Android
The Problem
As of now, unfortunately, Flutter uses the default Emojis supported on a given platform. Therefore, when building a cross-platform app you may face issues of Emojis showing on certain devices and not on others.
The Solution
The solution I settled for is to use a custom Emoji font such as Emoji One and RichText widget instead of the basic Text widget.
With this, you can simply have:
RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: 'Hello', // non-emoji characters
),
TextSpan(
text: '🧭 🏳️\u200d🌈', // emoji characters
style: TextStyle(
fontFamily: 'EmojiOne',
),
),
],
),
);
Generalized Solution
With this idea, we can even create a custom widget that given a string, builds a RichText object with all the TextSpans autocreated:
class EmojiText extends StatelessWidget {
const EmojiText({
Key key,
#required this.text,
}) : assert(text != null),
super(key: key);
final String text;
#override
Widget build(BuildContext context) {
return RichText(
text: _buildText(this.text),
);
}
TextSpan _buildText(String text) {
final children = <TextSpan>[];
final runes = text.runes;
for (int i = 0; i < runes.length; /* empty */ ) {
int current = runes.elementAt(i);
// we assume that everything that is not
// in Extended-ASCII set is an emoji...
final isEmoji = current > 255;
final shouldBreak = isEmoji
? (x) => x <= 255
: (x) => x > 255;
final chunk = <int>[];
while (! shouldBreak(current)) {
chunk.add(current);
if (++i >= runes.length) break;
current = runes.elementAt(i);
}
children.add(
TextSpan(
text: String.fromCharCodes(chunk),
style: TextStyle(
fontFamily: isEmoji ? 'EmojiOne' : null,
),
),
);
}
return TextSpan(children: children);
}
}
Which can be used as:
EmojiText(text: 'Hello there: 🧭 🏳️\u200d🌈');
This has the advantage of having the consistent support of Emojis on your app that you can control on different platforms.
The downside is that it will add some MBs to your app.
You can insert emoji in the text field through following way:
If you're on Mac, you can hit Control + Command + Space. Windows users can hit the "Windows key" + ; (semicolon).
Copy pasted the instruction from #Reso Coder
https://www.youtube.com/watch?v=KfuUkq2cLZU&t=15s
I tested on mac and it works.
Flutter supports emoji. Here's some code that demonstrates emoji text entry. (If you're seeing foreign characters, it's likely that you're decoding bytes as ASCII instead of UTF-8; we can show you how to fix this if you update your question with code that demonstrates the problem.)
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String _message = '🐣';
Future<String> _promptForString(String label, { String hintText }) {
final TextEditingController controller = new TextEditingController();
return showDialog(
context: context,
child: new AlertDialog(
title: new Text(label),
content: new TextFormField(
controller: controller,
decoration: new InputDecoration(hintText: hintText),
),
actions: <Widget>[
new FlatButton(
onPressed: () => Navigator.pop(context),
child: const Text('CANCEL'),
),
new FlatButton(
onPressed: () => Navigator.pop(context, controller.text),
child: const Text('OK'),
),
],
),
);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(_message),
),
body: new Center(
child: new Text(_message, style: Theme.of(context).textTheme.display2),
),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.edit),
onPressed: () async {
String message = await _promptForString('New text', hintText: 'Try emoji!');
if (!mounted)
return;
setState(() {
_message = message;
});
},
),
);
}
}
If you just want to include emoji in Text widget, you can copy emoji from somewhere else and paste it inside the text widget.
I use GeteMoji to copy emojis.
See the Output Screenshot
CODE : See 8th Row.
Widget build(BuildContext context) {
return Center(
child: Container(
alignment: Alignment.center,
color: Colors.deepPurple,
//width: 200.0,
//height: 100.0,
child: Text("Emoji 🤣 ",
style: TextStyle(
fontFamily: 'Raleway',
fontSize: 40,
decoration: TextDecoration.none,
color: Colors.white
))));
}
For full emoji compatibility (at least in android, not all emojis are supported in old OS versions) you can use the google free font Noto Color Emoji at https://www.google.com/get/noto/#emoji-zsye-color
Add it to the fonts folder
add in pubspec.yaml
fonts:
- family: NotoEmoji
fonts:
- asset: fonts/NotoColorEmoji.ttf
weight: 400
use with TextStyle
Text("🤑", TextStyle(fontFamily: 'NotoEmoji'))
When an emoji is not showing up, the issue is most likely the font you are using.
Before trying out an emoji font package, you can give the text field an empty string as fontFamily or try out the default font options packaged with flutter.
This will prevent adding dependencies that are not needed.
for example this emoji was not showing on android with 'Product Sans' as fontFamily, i simply added an empty string and font family for the text field, and that fixed the issue.
Text('₦', TextStyle(fontFamily: ''))
You can easily use the fontFamily as a style to solve the problem
I used it with the package
auto_size_text: ^2.1.0
AutoSizeText(
lesson.FullExercise,
textAlign: TextAlign.justify,
style:TextStyle(
fontFamily: 'EmojiOne',
),
),
Good note to mention
If you want to store data in MySQL and the text contains Emojis you need to change the collection of the text to utf8mb4

How do I bold (or format) a piece of text within a paragraph?

How can I have a line of text with different formatting?
e.g.:
Hello World
You should use the RichText widget.
A RichText widget will take in a TextSpan widget that can also have a list of children TextSpans.
Each TextSpan widget can have a different TextStyle.
Here is the example code to render:
Hello World
var text = RichText(
text: TextSpan(
// Note: Styles for TextSpans must be explicitly defined.
// Child text spans will inherit styles from parent
style: const TextStyle(
fontSize: 14.0,
color: Colors.black,
),
children: <TextSpan>[
TextSpan(text: 'Hello'),
TextSpan(text: 'World', style: const TextStyle(fontWeight: FontWeight.bold)),
],
),
);
[UPDATE]
The below answer fits best for couple of words and not for a paragraph,If you have a long sentence or a paragraph where you need to format a particular text prefer using RichText as suggested by #DvdWasibi in the above answer
[OLD ANSWER]
I like keeping my code short and clean this is How I Would do it add two text fields in a row one with Normal font and another bold,
Note: This may not look good for a long paragraph looks good for Headlines etc.
Row(children: [
Text("Hello"),
Text("World", style: const TextStyle(fontWeight: FontWeight.bold))
]);
and you should get a desired output as "Hello World"
return RichText(
text: TextSpan(
text: 'Can you ',
style: TextStyle(color: Colors.black),
children: <TextSpan>[
TextSpan(
text: 'find the',
style: TextStyle(
color: Colors.green,
decoration: TextDecoration.underline,
decorationStyle: TextDecorationStyle.wavy,
),
recognizer: _longPressRecognizer,
),
TextSpan(text: 'secret?'),
],
),
);
You should use the Text.rich constructor from Text class here.
By using the rich constructor you can display a paragraph with differently styled TextSpans.
Why I recommended it instead of RichText is because of by using RichText you will required to define the parent TextStyle in RichText but using the rich constructor of Text you don't need explicitly defined the parent TextStyle in Text.rich
Here is the example how to use it with same result
Using RichText
const text = RichText(
text: TextSpan(
// Here is the explicit parent TextStyle
style: new TextStyle(
fontSize: 16.0,
color: Colors.black,
fontFamily: 'Montserrat',
),
children: <TextSpan>[
new TextSpan(text: 'Hello'),
new TextSpan(text: 'World', style: new TextStyle(fontWeight: FontWeight.bold)),
],
),
);
Using rich constructor of Text
const text = Text.rich(
TextSpan(
// with no TextStyle it will have default text style
text: 'Hello',
children: <TextSpan>[
TextSpan(text: 'World', style: TextStyle(fontWeight: FontWeight.bold)),
],
),
)
I've solved a similar problem by using flutter_html widget with custom styles for different tags.
Actually, I've got the strings in different languages and some parts of them should be bold, so it wasn't easy to determine which part of the string I should make bold since strings was in l10n locale files. Here is example:
Container(
child: Html(
data: "<p>My normal text <b>with bold part</b> in any place</p>",
style: {
"p": Style(
fontSize: FontSize.large,
fontWeight: FontWeight.normal),
"b": Style(
fontWeight: FontWeight.bold,
),
)
);
I think this approach is useful in case you have a lot of differently styled text inside your regular text.
Regex
You can use this widget. The example below always make numbers bold.
import 'package:flutter/material.dart';
class TextBold extends StatelessWidget{
final String text;
final String regex;
static const _separator = " ";
const TextBold({Key key, this.text, this.regex = r'\d+'}) : super(key: key);
#override
Widget build(BuildContext context) {
final parts = splitJoin();
return Text.rich(TextSpan(
children: parts.map((e) => TextSpan(
text: e.text,
style: (e.isBold)
? const TextStyle(fontFamily: 'bold')
: const TextStyle(fontFamily: 'light')))
.toList()));
}
// Splits text using separator, tag ones to be bold using regex
// and rejoin equal parts back when possible
List<TextPart> splitJoin(){
assert(text!=null);
final tmp = <TextPart>[];
final parts = text.split(_separator);
// Bold it
for (final p in parts){
tmp.add(TextPart(p + _separator,p.contains(RegExp(regex))));
}
final result = <TextPart>[tmp[0]];
// Fold it
if (tmp.length>1) {
int resultIdx = 0;
for (int i = 1; i < tmp.length; i++)
if (tmp[i - 1].isBold != tmp[i].isBold) {
result.add(tmp[i]);
resultIdx++;
}
else
result[resultIdx].text = result[resultIdx].text
+ tmp[i].text;
}
return result;
}
}
class TextPart{
String text;
bool isBold;
TextPart(this.text, this.isBold);
}
Not fully tested but you can try this helper function that uses Text.rich and takes in the fullText and the textToBold then returns a Text:
static Text boldTextPortion(
String fullText,
String textToBold,
) {
final texts = fullText.split(textToBold);
final textSpans = List.empty(growable: true);
texts.asMap().forEach((index, value) {
textSpans.add(TextSpan(text: value));
if (index < (texts.length - 1)) {
textSpans.add(TextSpan(
text: textToBold,
style: const TextStyle(fontWeight: FontWeight.bold),
));
}
});
return Text.rich(
TextSpan(
children: <TextSpan>[...textSpans],
),
);
}
RichText()
Or if you receiving text from for example 'someText'.tr,
so use styled_text pub package.

Resources