Set the color of selected text - flutter

I'm setting up the theme for my app and I want to go with a minimalistic black & white color palette. For that I want the text to look black in a normal state and white when it's selected with the selection color being black.
So far I have found a way to set the selection color to black but now it just blends with the text because the color of the selected text stays black too. Is there a way to set the color of selected text?
TemeData(
textSelectionTheme: TextSelectionThemeData(
cursorColor: Colors.black,
selectionColor: Colors.black,
),
);
What I want:

I haven't found anything in the Flutter library, but you can try to use this Widget:
class SelectableColoredText extends StatefulWidget {
final String text;
final TextStyle? style;
final Color selectedColor;
final Color selectedBackgroundColor;
const SelectableColoredText(this.text, {
Key? key,
this.style,
required this.selectedColor,
required this.selectedBackgroundColor,
}) : super(key: key);
#override
_SelectableColoredTextState createState() => _SelectableColoredTextState();
}
class _SelectableColoredTextState extends State<SelectableColoredText> {
int? _start;
int? _end;
#override
Widget build(BuildContext context) {
final int start = _start ?? widget.text.length;
final int end = _end ?? widget.text.length;
return SelectableText.rich(
TextSpan(
style: widget.style ?? Theme.of(context).textTheme.bodyText2,
children: [
// Text before the selection
TextSpan(text: widget.text.substring(0, start)),
// Selected text
TextSpan(
text: widget.text.substring(start, end),
style: TextStyle(
color: widget.selectedColor,
backgroundColor: widget.selectedBackgroundColor,
),
),
// Text after the selection
TextSpan(text: widget.text.substring(end, widget.text.length)),
],
),
// Update the start and end positions of the selection
onSelectionChanged: (selection, _) {
final int newStart = min(selection.baseOffset, selection.extentOffset);
final int newEnd = max(selection.baseOffset, selection.extentOffset);
if (_start == newStart && _end == newEnd) return;
setState(() {
_start = newStart;
_end = newEnd;
});
},
);
}
}
You can use it like that:
SelectableColoredText(
'The quick brown fox jumps over the lazy dog',
// The default text style
style: TextStyle(fontSize: 40, color: Colors.black),
// The color of the selected text
selectedColor: Colors.white,
// The background color of the selected text
selectedBackgroundColor: Colors.black,
)

Sett the textSelectionColor for its ThemeData.

Related

Animate specific part of txt?By RichText in Flutter, I changed background color by tween animation How to do brush stroke type animation on highlight

I want to make an Epub Reader with text highlighting, text selections. But I tried using RichText and highlight_text package. But it is static and does not add highlighting animations. Like I want to show the highlight happening (brush stroke from right to left on a word). In Flutter, is there any way to do it?
I want to add animations to each text highlight:
`TextHighlight(
//This widget uses RichText and nested TextSpan for highlighting
//I want to add animations to the text Inside TextSpan
text: text,
words: words as LinkedHashMap<String, HighlightedWord>,
textStyle: TextStyle(
fontSize: 20.0,
color: Colors.black,
backgroundColor: Colors.transparent,
),`
Flutter package I am using highlight_text
``
library highlight_text;
import 'dart:collection';
import 'dart:core';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
class HighlightedWord {
final TextStyle textStyle;
final VoidCallback onTap;
HighlightedWord({
required this.onTap,
required this.textStyle,
});
}
class TextHighlight extends StatefulWidget {
String text;
TextAlign textAlign;
TextDirection? textDirection;
bool softWrap;
TextOverflow overflow;
double textScaleFactor;
int? maxLines;
Locale? locale;
StrutStyle? strutStyle;
bool enableCaseSensitive;
TextHighlight({
required this.text,
required this.words,
this.textStyle = const TextStyle(
fontSize: 25.0,
color: Colors.black,
),
this.textAlign = TextAlign.start,
this.textDirection,
this.softWrap = true,
this.overflow = TextOverflow.clip,
this.textScaleFactor = 1.0,
this.maxLines,
this.locale,
this.strutStyle,
this.enableCaseSensitive = false,
});
#override
_TextHightlightState createState() => _TextHighlightState(
required this.text,
required this.words,
this.textStyle = const TextStyle(
fontSize: 25.0,
color: Colors.black,
),
this.textAlign = TextAlign.start,
this.textDirection,
this.softWrap = true,
this.overflow = TextOverflow.clip,
this.textScaleFactor = 1.0,
this.maxLines,
this.locale,
this.strutStyle,
this.enableCaseSensitive = false,
);
}
class _TextHighlightState extends State<TextHighlight> {
String text;
TextAlign textAlign;
TextDirection? textDirection;
bool softWrap;
TextOverflow overflow;
double textScaleFactor;
int? maxLines;
Locale? locale;
StrutStyle? strutStyle;
bool enableCaseSensitive;
#override
void initState(){
text = widget.text;
textAlign = widget.textAlign;
super.initState();
}
#override
Widget build(BuildContext context) {
List<String> _textWords = _bind();
return AnimatedDefaultTextStyle(
child: RichText(
text: _buildSpan(_textWords),
locale: locale,
maxLines: maxLines,
overflow: overflow,
softWrap: softWrap,
strutStyle: strutStyle,
textAlign: textAlign,
textDirection: textDirection,
textScaleFactor: textScaleFactor,
),
);
}
List<String> _bind() {
String bindedText = text;
for (String word in words.keys) {
if (enableCaseSensitive) {
bindedText = bindedText.replaceAll(
word, '<highlight>${words.keys.toList().indexOf(word)}<highlight>');
} else {
int strIndex = bindedText.toLowerCase().indexOf(word.toLowerCase());
print('$word\n');
bindedText = bindedText.replaceRange(strIndex, strIndex + word.length,
'<highlight>${words.keys.toList().indexOf(word)}<highlight>');
}
}
List<String> splitedTexts = bindedText.split("<highlight>");
splitedTexts.removeWhere((s) => s.isEmpty);
return splitedTexts;
}
TextSpan _buildSpan(List<String> bindedWords) {
if (bindedWords.isEmpty) return TextSpan();
String nextToDisplay = bindedWords.first;
bindedWords.removeAt(0);
int? index = int.tryParse(nextToDisplay);
if (index != null) {
String currentWord = words.keys.toList()[index];
return TextSpan(
text: currentWord,
style: words[currentWord]!.textStyle,
children: [
_buildSpan(bindedWords),
],
recognizer: TapGestureRecognizer()
..onTap = () => words[currentWord]!.onTap(),
);
}
return TextSpan(
text: nextToDisplay,
style: textStyle,
children: [
_buildSpan(bindedWords),
],
);
}
}
Is there any way to do animations, tweaking the package code in some way.. or any other workaround..
Baiscally I want to highlight a specific text, rendered by Text widget. In mobile and Web.

How to write a function (in flutter-dart) so that it accepts certain parameters when we call that function?

This is my code:
Text ButtonText = Text(
_buttonText, style: TextStyle(
color: Colors.white,
fontFamily: 'San francisco',
//fontSize: 21.0.ssp,
letterSpacing: 2.0,
wordSpacing: 2.0
),
);
when I use this Text in my button widget, I want to set font size explicitly. How can I do that?
you can create a class for your situation we can call it customtext
here is an example code :
import 'package:flutter/material.dart';
class CustomText extends StatelessWidget {
final String text;
final double size;
final Color color;
final FontWeight weight;
// name constructor that has a positional parameters with the text required
// and the other parameters optional
CustomText({#required this.text, this.size,this.color,this.weight});
#override
Widget build(BuildContext context) {
return Text(
text,style: TextStyle(fontSize: size ?? 16, color: color ?? Colors.black, fontWeight: weight ?? FontWeight.normal),
);
}
}

I want to change the color of focused otp text field's single element in flutter

In my OTP text field, the focused color of the bottom border of a single element, or textfield is blue by default. How can i change it to orange?? Also how can i change the unfocused color of that border from black to white. That is by default, without entering any number, color of all blocks or bottom border is white??? I will highlight the property in the image.
Check out this image, if you didn't understand properly that what i am trying to say - MobileImage
Here's the code -
import 'package:flutter/material.dart';
import 'package:otp_text_field/style.dart';
class OTPTextField extends StatefulWidget {
/// Number of the OTP Fields
final int length;
/// Total Width of the OTP Text Field
final double width;
/// Width of the single OTP Field
final double fieldWidth;
/// Manage the type of keyboard that shows up
TextInputType keyboardType;
/// The style to use for the text being edited.
final TextStyle style;
/// Text Field Alignment
/// default: MainAxisAlignment.spaceBetween [MainAxisAlignment]
final MainAxisAlignment textFieldAlignment;
/// Obscure Text if data is sensitive
final bool obscureText;
/// Text Field Style for field shape.
/// default FieldStyle.underline [FieldStyle]
final FieldStyle fieldStyle;
/// Callback function, called when a change is detected to the pin.
final ValueChanged<String> onChanged;
/// Callback function, called when pin is completed.
final ValueChanged<String> onCompleted;
OTPTextField(
{Key key,
this.length = 4,
this.width = 20,
this.fieldWidth = 50,
this.keyboardType = TextInputType.number,
this.style = const TextStyle(),
this.textFieldAlignment = MainAxisAlignment.spaceBetween,
this.obscureText = false,
this.fieldStyle = FieldStyle.underline,
this.onChanged,
this.onCompleted})
: assert(length > 1);
#override
_OTPTextFieldState createState() => _OTPTextFieldState();
}
class _OTPTextFieldState extends State<OTPTextField> {
List<FocusNode> _focusNodes;
List<TextEditingController> _textControllers;
List<Widget> _textFields;
List<String> _pin;
#override
void initState() {
super.initState();
_focusNodes = List<FocusNode>(widget.length);
_textControllers = List<TextEditingController>(widget.length);
_pin = List.generate(widget.length, (int i) {
return '';
});
_textFields = List.generate(widget.length, (int i) {
return buildTextField(context, i);
});
}
#override
void dispose() {
_textControllers
.forEach((TextEditingController controller) => controller.dispose());
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
width: widget.width,
child: Row(
mainAxisAlignment: widget.textFieldAlignment,
crossAxisAlignment: CrossAxisAlignment.center,
children: _textFields,
),
);
}
/// This function Build and returns individual TextField item.
///
/// * Requires a build context
/// * Requires Int position of the field
Widget buildTextField(BuildContext context, int i) {
if (_focusNodes[i] == null) _focusNodes[i] = new FocusNode();
if (_textControllers[i] == null)
_textControllers[i] = new TextEditingController();
return Container(
width: widget.fieldWidth,
child: TextField(
cursorColor: Colors.white,
controller: _textControllers[i],
keyboardType: widget.keyboardType,
textAlign: TextAlign.center,
maxLength: 1,
style: widget.style,
focusNode: _focusNodes[i],
obscureText: true,
decoration: InputDecoration(
fillColor: Colors.white,
counterText: "",
border: widget.fieldStyle == FieldStyle.box
? OutlineInputBorder(borderSide: BorderSide(width: 4.0, color: Colors.green))
: null),
onChanged: (String str) {
// Check if the current value at this position is empty
// If it is move focus to previous text field.
if (str.isEmpty) {
if (i == 0) return;
_focusNodes[i].unfocus();
_focusNodes[i - 1].requestFocus();
}
// Update the current pin
setState(() {
_pin[i] = str;
});
// Remove focus
if (str.isNotEmpty) _focusNodes[i].unfocus();
// Set focus to the next field if available
if (i + 1 != widget.length && str.isNotEmpty)
FocusScope.of(context).requestFocus(_focusNodes[i + 1]);
String currentPin = "";
_pin.forEach((String value) {
currentPin += value;
});
// if there are no null values that means otp is completed
// Call the `onCompleted` callback function provided
if (!_pin.contains(null) &&
!_pin.contains('') &&
currentPin.length == widget.length) {
widget.onCompleted(currentPin);
}
// Call the `onChanged` callback function
widget.onChanged(currentPin);
},
),
);
}
}
Your default Theme color is blue, this will be applied to your TextField. You would need to change your Theme color for your TextField to change your border color. Take note that this will only change the Theme color for TextField and not for your whole Flutter app.
child: Theme(
data: ThemeData(
primaryColor: Colors.orangeAccent,
primaryColorDark: Colors.orange,
),
child: TextField(
...
));
From the package here
Easy fix is by adding the otpFieldStyle property and edit the colors as preferred for example:
Padding(
padding: EdgeInsets.only(top:18.0),
child: OTPTextField(
length: 4,
width: MediaQuery.of(context).size.width,
textFieldAlignment: MainAxisAlignment.spaceAround,
fieldWidth: 55,
fieldStyle: FieldStyle.underline,
otpFieldStyle: OtpFieldStyle(
focusBorderColor: Colors.orange //(here)
),
outlineBorderRadius: 15,
style: TextStyle(fontSize: 17, color: Colors.white, ),
onChanged: (pin) {
print("Changed: " + pin);
},
onCompleted: (pin) {
print("Completed: " + pin);
}),
),
which has more properties as below
OtpFieldStyle(
{this.backgroundColor: Colors.transparent,
this.borderColor: Colors.black26,
this.focusBorderColor: Colors.blue,
this.disabledBorderColor: Colors.grey,
this.enabledBorderColor: Colors.black26,
this.errorBorderColor: Colors.red});

unable to style text size in flutter widget

I am trying to style the field where i type in the address, I want to make my font size smaller. I have tried changing the fontsize through the various "textstyle" properties in "Inputdecoration" but had no luck. How do i achieve it?
There are also these yellow lines underscoring my text. Is it an error?
Please help me out :/ would really appreciate it~
return MaterialApp(
home: Scaffold(
body: Stack(
children: <Widget>[
GoogleMap(
onMapCreated: (GoogleMapController controller) {
mapController = controller;
},
initialCameraPosition: currentPosition,
markers: marks,
),
SafeArea(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: PlacesAutocompleteField(
apiKey: 'AIzaSyDVxxxxxxxxxxxxxx',
hint: 'Search Places',
style: TextStyle(fontSize: 50.0),
inputDecoration: InputDecoration(
icon: Icon(Icons.search),
hintStyle: TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.bold,
),
),
onChanged: (value) async {
placeName = value;
print(placeName);
List<Placemark> placemark =
await Geolocator().placemarkFromAddress(placeName);
print(placemark[0].position);
Set<Marker> markers =
await getMarkers(placemark[0].position);
updateUI(placemark[0].position, markers);
mapController.animateCamera(
CameraUpdate.newCameraPosition(currentPosition));
},
),
),
),
],
),
),
);
code for the PlacesAutocompleteField:
class PlacesAutocompleteField extends StatefulWidget {
/// Creates a text field like widget.
///
/// To remove the decoration entirely (including the extra padding introduced
/// by the decoration to save space for the labels), set the [decoration] to
/// null.
const PlacesAutocompleteField({
Key key,
#required this.apiKey,
this.style,
this.controller,
this.leading,
this.hint = "Search",
this.trailing,
this.trailingOnTap,
this.mode = Mode.fullscreen,
this.offset,
this.location,
this.radius,
this.language,
this.sessionToken,
this.types,
this.components,
this.strictbounds,
this.onChanged,
this.onError,
this.inputDecoration = const InputDecoration(),
}) : super(key: key);
/// Controls the text being edited.
///
/// If null, this widget will create its own [TextEditingController].
final TextEditingController controller;
/// Icon shown inside the field left to the text.
final Icon leading;
/// Icon shown inside the field right to the text.
final Icon trailing;
/// Callback when [trailing] is tapped on.
final VoidCallback trailingOnTap;
/// Text that is shown, when no input was done, yet.
final String hint;
final TextStyle style;
/// Your Google Maps Places API Key.
///
/// For this key the Places Web API needs to be activated. For further
/// information on how to do this, see their official documentation below.
///
/// See also:
///
/// * <https://developers.google.com/places/web-service/autocomplete>
final String apiKey;
/// The decoration to show around the text field.
///
/// By default, draws a horizontal line under the autocomplete field but can be
/// configured to show an icon, label, hint text, and error text.
///
/// Specify null to remove the decoration entirely (including the
/// extra padding introduced by the decoration to save space for the labels).
final InputDecoration inputDecoration;
/// The position, in the input term, of the last character that the service
/// uses to match predictions.
///
/// For example, if the input is 'Google' and the
/// offset is 3, the service will match on 'Goo'. The string determined by the
/// offset is matched against the first word in the input term only. For
/// example, if the input term is 'Google abc' and the offset is 3, the service
/// will attempt to match against 'Goo abc'. If no offset is supplied, the
/// service will use the whole term. The offset should generally be set to the
/// position of the text caret.
///
/// Source: https://developers.google.com/places/web-service/autocomplete
final num offset;
final Mode mode;
final String language;
final String sessionToken;
final List<String> types;
final List<Component> components;
final Location location;
final num radius;
final bool strictbounds;
/// Called when the text being edited changes.
final ValueChanged<String> onChanged;
/// Callback when autocomplete has error.
final ValueChanged<PlacesAutocompleteResponse> onError;
#override
_LocationAutocompleteFieldState createState() =>
_LocationAutocompleteFieldState();
}
class _LocationAutocompleteFieldState extends State<PlacesAutocompleteField> {
TextEditingController _controller;
TextEditingController get _effectiveController =>
widget.controller ?? _controller;
#override
void initState() {
super.initState();
if (widget.controller == null) _controller = TextEditingController();
}
#override
void didUpdateWidget(PlacesAutocompleteField oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.controller == null && oldWidget.controller != null)
_controller = TextEditingController.fromValue(oldWidget.controller.value);
else if (widget.controller != null && oldWidget.controller == null)
_controller = null;
}
Future<Prediction> _showAutocomplete() async => PlacesAutocomplete.show(
context: context,
apiKey: widget.apiKey,
offset: widget.offset,
onError: widget.onError,
mode: widget.mode,
hint: widget.hint,
language: widget.language,
sessionToken: widget.sessionToken,
components: widget.components,
location: widget.location,
radius: widget.radius,
types: widget.types,
strictbounds: widget.strictbounds,
);
void _handleTap() async {
Prediction p = await _showAutocomplete();
if (p == null) return;
setState(() {
_effectiveController.text = p.description;
if (widget.onChanged != null) {
widget.onChanged(p.description);
}
});
}
#override
Widget build(BuildContext context) {
final TextEditingController controller = _effectiveController;
var text = controller.text.isNotEmpty
? Text(
controller.text,
softWrap: true,
)
: Text(
widget.hint ?? '',
style: TextStyle(color: Colors.black38),
);
Widget child = Row(
children: <Widget>[
widget.leading ?? SizedBox(),
SizedBox(
width: 16.0,
),
Expanded(
child: text,
),
widget.trailing != null
? GestureDetector(
onTap: widget.trailingOnTap,
child: widget.trailingOnTap != null
? widget.trailing
: Icon(
widget.trailing.icon,
color: Colors.grey,
),
)
: SizedBox()
],
);
if (widget.inputDecoration != null) {
child = InputDecorator(
decoration: widget.inputDecoration,
child: child,
);
}
return GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: _handleTap,
child: child,
);
}
}
The reason being you are not providing any widget to your Text which has got material design, there are many ways to fix it, like you can provide Material or a basic Scaffold. The recommended way would be to use Scaffold as it can provide many basic functionalities too.
So you need to wrap your Stack in Scaffold
Scaffold(
body: Stack(...)
)
Update (for text size) :
In the build() method of PlacesAutocompleteField class, replace yours with following
var text = controller.text.isNotEmpty
? Text(
controller.text,
softWrap: true,
style: TextStyle(fontSize: 50),
)
: Text(
widget.hint ?? '',
style: TextStyle(color: Colors.black38, fontSize: 50),
);

Make Emojis bigger in text?

Any idea how to make only the fontsize of emojis larger in the Text() widget?
The issue is that the Text() widget parses the emojis automatically. Sure, I can increase the overall text size, but I want the text to stay at the same fontsize.
If you want to auto increase size of emojis in chat message you can use this method:
static final RegExp REGEX_EMOJI = RegExp(
r'(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])');
Widget _buildContent(String content) {
final Iterable<Match> matches = REGEX_EMOJI.allMatches(content);
if (matches.isEmpty)
return Text(
'${content}',
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.w500,
color: Colors.black,
),
);
return RichText(
text: TextSpan(children: [
for (var t in content.characters)
TextSpan(
text: t,
style: TextStyle(
fontSize: REGEX_EMOJI.allMatches(t).isNotEmpty ? 20.0 : 12.0,
color: Colors.black,
)),
]));
}
You can use the widget RichText
RichText(
text: TextSpan(
text: 'hello',
children: <TextSpan>[
TextSpan(text: '🙂', style: TextStyle(fontSize: 30))
]
),
),
I was facing the same issue and find beginning of solution here
I've modified the code a little to allow showing text and emoji with different font size.
import 'package:flutter/material.dart';
/// Widget to render emoji and text with different font size
class EmojisText extends StatelessWidget {
const EmojisText({
Key? key,
required this.text,
required this.color,
required this.emojiSize,
required this.textSize,
}) : super(key: key);
///The text which emoji and alpha-numeric characters
///emoji can be absent
final String text;
/// THe font size to set to emoji
final double emojiSize;
/// The font size to set to text
final double textSize;
/// the color of the text
final Color color;
#override
Widget build(BuildContext context) {
return RichText(
text: _buildText(),
);
}
TextSpan _buildText() {
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(
fontSize: isEmoji ? emojiSize : textSize,
color: color,
),
),
);
}
return TextSpan(children: children);
}
}