Flutter Widget that shows text line by line? - flutter

Is there a widget in Flutter that allows children Text widgets to be shown line by line at every press of the widget? This should act similar to how bulleted lines in a powerpoint presentation act after every click.

I just tested and it works, probably I did not cover some edge cases, but it is not a lot of work...
//I would call it TIM like VIM :P
class TextIMproved extends StatefulWidget {
final String _longString;
final int _numberOfWordsPerRow;
TextIMproved(this._numberOfWordsPerRow, this._longString);
#override
_TextIMprovedState createState() => _TextIMprovedState(_numberOfWordsPerRow, _longString);
}
class _TextIMprovedState extends State<TextIMproved> {
final String longString;
List<String> listString;
int _numberOfWordsPerRow;
String strPopulated;
String strToDisplay='';
_TextIMprovedState(this._numberOfWordsPerRow, this.longString);
#override
void initState() {
super.initState();
listString = longString.split(' ');
splitString();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
child: Container(
child: Text(strToDisplay, maxLines: longString.length ~/ _numberOfWordsPerRow + _numberOfWordsPerRow,),
),
onTap: splitString,
);
}
void splitString() {
//1.prepare empty string
strPopulated = '';
//2.populate string
int len = listString.length <_numberOfWordsPerRow ? listString.length : _numberOfWordsPerRow;
for (int i = 0; i < len; i++) {
strPopulated += listString[i] + ' ';
}
//3. display portion of string
setState(() {
strToDisplay += strPopulated;
});
//4.remove displayed text
List<String> listToremove = strPopulated.split(' ');
for (String str in listToremove) {
listString.remove(str);
}
}
}
EDIT:
you can add animation to this widget...

Related

About securing password field in textformfield in flutter

I want to hide completely the password field, I set obscure text true but it shows the characters as I type them how hide completely the characters?
If you want completely hide it, you must use controller and implement like this:
class TextFieldPassWord extends StatefulWidget {
#override
_TextFieldPassWordState createState() => _TextFieldPassWordState();
}
class _TextFieldPassWordState extends State<TextFieldPassWord> {
String _valueShow = "";
String _value = "";
#override
Widget build(BuildContext context) {
return Scaffold(
body: TextField(
controller: TextEditingController.fromValue(
TextEditingValue(
text: _valueShow,
selection: TextSelection.collapsed(offset: _valueShow.length),
),
),
onChanged: (String str) {
String value = "";
if (str.length > _value.length) {
value += str.substring(_value.length, str.length);
}
if (str.length < _value.length) {
value = _value.substring(1, str.length);
}
String valueToShow = "*" * str.length;
setState(() {
_valueShow = valueToShow;
_value = value;
});
},
),
);
}
}

Flutter How to update widgets one after the other instead of at the same time

I want the function update_list to change the values in the list when it is called. And then I want it to change the text in the column.
That works, but now I want the first value to change (and to see the new value on the screen) and then a second later the next value and so on. But with my code it changes all together at the end.
How can I make the values change one after the other and not all at once?
class class_one extends StatefulWidget {
const class_one({Key? key}) : super(key: key);
#override
State<class_one> createState() => _class_oneState();
}
class _class_oneState extends State<class_one> {
List list = [0,1,2,3];
void update_list(){
setState((){
for (var i = 0; i < 4; i++){
list[i]=list[i]+10;
sleep(Duration(seconds: 1));
}
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
Text('${list[0]}'),
Text('${list[1]}'),
Text('${list[2]}'),
Text('${list[3]}'),
GestureDetector(onTap: (){update_list();},child: Container(color: Colors.red,child: Text("next"),),)
],
),
);
}
}
you can use two solution for this issue:
1: use streaming mechanism(actually use from streamBuilder widget).
2:
void update_list() async{
for (var i = 0; i < 4; i++){
setState((){
list[i]=list[i]+10;
});
await Future.delayed(Duration(seconds: 1));
}
}

Call setState from class that extends StatefulWidget

If I update a variable using class object, the build method should get called, but I am unable to call setState from the StatefulWidget class.
class CustomErrorFormField extends StatefulWidget {
#override
_CustomErrorFormFieldState createState() {
return _CustomErrorFormFieldState();
}
List<String> errorList = []; //this variable will get updated using below function
void setErrorList(List<String> listOfError) {
errorList = listOfError;
}
}
class _CustomErrorFormFieldState extends State<CustomErrorFormField> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
print(widget.errorList); //this is not printing updated value
return .....
}
}
Now in some other class i will update errorList Variable
nameTextFild = CustomErrorFormField(
key: ValueKey(count),
labelName: "Name",
iContext: context,
onChanged: (String value) {
setState(() {
count++;
if (!value.contains(RegExp(r'[0-9]'))) {
nameTextFild!.setErrorList([]); //updating but changes not appearing (setState of this widget is not getting called)
} else {
nameTextFild!.setErrorList(["Invalid characters, use letters only."]);
}
});
},
);
It's not recommended that you change the state of a widget from outside the widget.
What you should do instead is pass the validation logic as a function and let the widget handle the state change.
CustomFormField:
import 'package:flutter/material.dart';
class CustomErrorFormField extends StatefulWidget {
//Take the validation logic as a parameter.
final List<String> Function(String value) validator;
const CustomErrorFormField({required this.validator});
#override
_CustomErrorFormFieldState createState() {
return _CustomErrorFormFieldState();
}
}
class _CustomErrorFormFieldState extends State<CustomErrorFormField> {
//Keep the state inside the widget itself
List<String> errorList = [];
//Update the state from inside the widget
void setErrorList(List<String> listOfError) {
setState(() {
errorList = listOfError;
});
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Form(
child: TextFormField(
validator: (String value){
//Use the validation logic to decide the error.
setErrorList(widget.validator(value))
}
}
),
);
}
}
I have used TextFormField as an example, you can use any widget that accepts a callback upon change.
If you're making everything from scratch you can attach the validator function to a callback that fires when the text is changed. Usually this is done with the help of a controller.
usage:
final nameTextFild = CustomErrorFormField(
key: ValueKey(count),
labelName: "Name",
iContext: context,
validator: (String value) {
if (!value.contains(RegExp(r'[0-9]'))) {
return [];
} else {
return ["Invalid characters, use letters only."];
}
},
);

Change color part of text using index in flutter

I have a one String which contains words: achievement admission advertise pencil. I have a list with pairs of numbers:
class Pair<T1, T2> {
final T1 a;
final T2 b;
Pair(this.a, this.b);
}
String letters = "achievement admission advertise pencil";
List<Pair> words = [Pair(3, 5), Pair(6, 8), Pair(9, 11), Pair(12, 14), Pair(15, 17)];
I want to change color part of String using index from which sign to which sign. For example after 2 seconds letters from 3 to 5 should have color green. After next 2 seconds only letters from 6 to 8 should be green, after next 2 seconds only letters from 9 to 11 should be green, rest letters should return to black. There is any way to do that?
Yes, you can create a custom widget for this. Here is a working example:
(Use with SyllableText(text: letters, parts: words))
class SyllableText extends StatefulWidget {
const SyllableText({
required this.text,
required this.parts,
Key? key,
}) : super(key: key);
final String text;
final List<Pair> parts;
#override
SyllableTextState createState() => SyllableTextState();
}
class SyllableTextState extends State<SyllableText> {
int currentPartIndex = 0;
#override
void initState() {
super.initState();
Future.doWhile(() async {
await Future.delayed(Duration(seconds: 2));
if (mounted && currentPartIndex < widget.parts.length) {
setState(() => currentPartIndex++);
return true;
} else {
return false;
}
});
}
#override
Widget build(BuildContext context) {
if (currentPartIndex < widget.parts.length) {
final part = widget.parts[currentPartIndex];
final startText = widget.text.substring(0, part.a);
final coloredText = widget.text.substring(part.a, part.b + 1);
final endText = widget.text.substring(part.b + 1);
return Text.rich(
TextSpan(
children: [
TextSpan(text: startText),
TextSpan(text: coloredText, style: TextStyle(color: Colors.green)),
TextSpan(text: endText),
],
),
);
} else {
return Text(widget.text);
}
}
}

Get text before/after cursor, on currently edited line, in TextField

I can print out all text before and after the cursor using this code:
controller = TextEditingController();
final selection = controller.value.selection;
final text = controller.value.text;
print("\nBEFORE\n${selection.textBefore(text)}");
print("\nAFTER\n${selection.textAfter(text)}\n");
How can I use the TextEditingController to find only the text before/after the cursor on the same line as the cursor?
For example, I want to use the TextEditingController to query the text before/after the current position of the cursor so the text before is "lin" and the text after the cursor is "e2 contents" (in the image above).
Full app code that gets all text before/after the cursor (not on the same line as required):
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: TextFieldApp()));
class TextFieldApp extends StatefulWidget {
#override
_TextFieldAppState createState() => _TextFieldAppState();
}
class _TextFieldAppState extends State<TextFieldApp> {
TextEditingController controller;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: TextField(
keyboardType: TextInputType.multiline,
controller: controller,
maxLines: 10,
maxLength: null,
),
);
}
#override
void dispose() {
super.dispose();
}
#override
void initState() {
controller = TextEditingController();
controller.text = 'line 1\nline2 contents\nline3';
controller.addListener(() {
final selection = controller.value.selection;
final text = controller.value.text;
print("\nBEFORE\n${selection.textBefore(text)}");
print("\nAFTER\n${selection.textAfter(text)}\n");
});
super.initState();
}
}
You can split the text by a \n then take the last value from the array.
Similarly the first entry for the text after.
final selection = controller.value.selection;
final text = controller.value.text;
final before = selection.textBefore(text);
final after = selection.textAfter(text);
print(before.split('\n').last);
print(after.split('\n').first);
Note:
If the users selects cont in a line with content line2 contents they would be
line2 //before
ents //after
If you need the text 'after' to also have the 'selected text',
you may add the text 'inside' to the text 'after'.
selection.textInside(text) + after.split('\n').first;