How to access a widgets state from a stateful widget class Flutter - flutter

Sorry if this has been already answered somewhere else but I am new to Flutter. I have a toString method in my widget below that needs to access the state of the widget to output the string. The widget is a card that contains a text field and other text-related operations. To store information on what a user types into the card I need to get all the data into one string which toString returns.
class TextCard extends StatefulWidget {
_TextCardState cardState = _TextCardState();
TextCard({String text = ""}) {
cardState.textController.text = text;
}
#override
_TextCardState createState() => cardState = new _TextCardState();
String toString({DiagnosticLevel minLevel = DiagnosticLevel.debug}) {
return delimiter2 +
"TextCard" +delimiter3 +
cardState.getText() +
delimiter3 +
(cardState.center.toString()) +
delimiter3 +
cardState.bold.toString() +
delimiter3 +
cardState.italic.toString() +
delimiter3 +
cardState.size.toString() +
delimiter2;
}
}
The widget also takes in a string value to set the initial value of a text field in the state below
class _TextCardState extends State<TextCard> {
double size = 18;
bool bold = false;
bool italic = false;
bool center = false;
var textController = TextEditingController();
#override
Widget build(BuildContext context) {
return Container(
height: _cardSizeY,
width: _cardSizeX,
child: Card(
elevation: _elevation,
child: Center(
child: Column(children: [
ListTile(leading: Icon(Icons.text_fields)),
ButtonBar(children: [
IconButton(
icon: Icon(Icons.format_bold),
onPressed: () {
updateText(size, !bold, italic, center);
},
),
IconButton(
icon: Icon(Icons.format_italic),
onPressed: () {
updateText(size, bold, !italic, center);
},
),
Slider(
value: size,
max: 80,
min: 1,
onChanged: (size) {
updateText(size, bold, italic, center);
})
]),
TextField(
maxLines: null,
style: TextStyle(
fontWeight: (bold) ? FontWeight.bold : FontWeight.normal,
fontStyle: (italic) ? FontStyle.italic : FontStyle.normal,
fontSize: size),
textAlign: (center) ? TextAlign.center : TextAlign.start,
controller: textController,
decoration: InputDecoration(
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey),
borderRadius: BorderRadius.all(Radius.circular(10)))))
]))));
}
void updateText(double size, bool bold, bool italic, bool center) {
setState(() {
this.size = size;
this.bold = bold;
this.italic = italic;
this.center = center;
});
}
String getText() {
return textController.value.text;
}
}
When I run this code I get the error the create state function returned an old invalid state instance.
I have looked into putting the text controller into the _TextCardState() class but I would not be able to change the initial value of the TextField.

So I see what you are trying to do here but there are better ways to access the value of a textfield from outside of the class.
Instead of access your toString method from outside, which relies on a values from the private state class, I suggest a state management solution that will make this way easier and cleaner. You'll also have easier access to all those variables you need.
What you're doing here is not something that's meant to be done, which is why you're getting those state errors.
_TextCardState cardState = _TextCardState();
Here's a way to do it using GetX.
All your data will live in a GetX Controller class below and will be used in your now stateless TextCard widget.
class Controller extends GetxController {
var textController = TextEditingController();
String textfieldString = '';
double size = 18;
bool bold = false;
bool italic = false;
bool center = false;
#override
void onInit() {
super.onInit();
// updates the value of textfieldString anytime the user types
textController.addListener(() {
textfieldString = textController.text;
debugPrint(textController.text);
});
}
// this method lives in this class and is accessible from anywhere. The
// only thing not clear is what delimier2 is and where it comes from
// toString is not a good name because that is an overridden method that lives
// in most Dart classes
String buildString({DiagnosticLevel minLevel = DiagnosticLevel.debug}) {
return delimiter2 +
"TextCard" +delimiter3 +
textfieldString +
delimiter3 +
(center.toString()) +
delimiter3 +
bold.toString() +
delimiter3 +
italic.toString() +
delimiter3 +
size.toString() +
delimiter2;
}
// single responsibility methods as opposed to firing one big function
// multiple times when its only affecting one variable
void toggleBold() {
bold = !bold;
update();
}
void toggleItalic() {
italic = !italic;
update();
}
void toggleCenter() {
center = !center;
update();
}
void updateSize(double sliderValue) {
size = sliderValue;
update();
}
}
Put this in your main before running your app. Can be done anywhere as long as its before you try and access the controller.
Get.put(Controller());
And here is your TextCard widget
class TextCard extends StatelessWidget {
#override
Widget build(BuildContext context) {
final controller =
Get.find<Controller>(); // finding the initalized controller
return Container(
height: _cardSizeY,
width: _cardSizeX,
child: Card(
elevation: 20,
child: Center(
child: Column(
children: [
ListTile(leading: Icon(Icons.text_fields)),
ButtonBar(children: [
IconButton(
icon: Icon(Icons.format_bold),
onPressed: () {
controller.toggleBold();
},
),
IconButton(
icon: Icon(Icons.format_italic),
onPressed: () {
controller.toggleItalic(); // accessing method via controller
},
),
// GetBuilder rebuilds children when value of controller variable changes
GetBuilder<Controller>(
builder: (_) {
return Slider(
value: controller
.size, // accessing size in other class via controller
max: 80,
min: 1,
onChanged: (value) {
controller.updateSize(value);
});
},
)
]),
GetBuilder<Controller>(
builder: (_) {
return TextField(
maxLines: null,
style: TextStyle(
fontWeight: (controller.bold)
? FontWeight.bold
: FontWeight.normal,
fontStyle: (controller.italic)
? FontStyle.italic
: FontStyle.normal,
fontSize: controller.size),
textAlign: (controller.center)
? TextAlign.center
: TextAlign.start,
controller: controller.textController,
decoration: InputDecoration(
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey),
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
),
);
},
)
],
),
),
),
);
}
}
So where ever you are in your app where you need that function, find the controller and get your value.
final controller = Get.find<Controller>():
final newString = controller.buildString();
This will be easier and use less memory because TextCard is now stateless.

Related

I can't change the hinttext in the dropdownButton

This is my code, and i dont know why the hint doesnt change, when i click the options, obviously all this code is inside a class, I tried to use a for loop to identify the list positions , and if they were right, it would appear on the screen.
final listgenre = ["Masc", "Fem", "Não Binário"];
var listgenre_value;
String newValue = "";
var valueChoose;
String hintValue = "";
Padding(
padding: EdgeInsets.all(16),
child: Container(
padding: EdgeInsets.only(left: 16, top: 10, right: 15),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 2),
borderRadius: BorderRadius.circular(20)),
child: DropdownButton<String>(
hint: Text("Genero :$hintValue"),
dropdownColor: Colors.white,
icon: Icon(Icons.arrow_drop_down),
iconSize: 36,
iconEnabledColor: Colors.black,
isExpanded: true,
style: TextStyle(fontSize: 17, color: Colors.black),
value: valueChoose,
underline: SizedBox(
width: 320,
height: 200,
),
items: listgenre.map((valueitem) {
return DropdownMenuItem(
value: valueitem,
child: Text(valueitem),
);
}).toList(),
onChanged: (newValue) {
setState(() {
for (int i = 0; i <= listgenre.length; i++) {
if (listgenre[i] != newValue) {
listgenre_value = i + 1;
} else {
hintValue = "$newValue";
// ignore: void_checks
return listgenre_value;
}
}
Object? valueChoose = newValue;
String valueChoosen = valueChoose.toString();
});
},
))),
As per your comments, the same code doesn't give the expected output.
It is only possible if you are also initialising the variables inside the build method, which shouldn't be the case.
Incorrect Code
#override
Widget build(BuildContext context) {
// ---- your variables INSIDE build method
final listgenre = ["Masc", "Fem", "Não Binário"];
var listgenre_value;
String newValue = "";
var valueChoose;
String hintValue = "";
// ------- rest of the code continued
Correct Code
final listgenre = ["Masc", "Fem", "Não Binário"];
var listgenre_value;
String newValue = "";
var valueChoose;
String hintValue = "";
#override
Widget build(BuildContext context) {
// ------- rest of the code continued
As the build method is called every time setState is called, hintValue was being initialised to "" i.e empty string.
The correct way to fix this error is abdev's comment. It will help if you put the variables used by the DropdownButton widget above the build method.
I hope it was helpful!

Flutter ValueListenableBuilder result updates state to show/ hide widget

I am trying to use my ValueListenableBuilder, which generates Yes/ No buttons, to determine whether or not a textformfield will be visible. I.e. user selects the "Yes" button and the state of the app changes to allow the hidden textformfield to be displayed. I am extremely puzzled as to how I can either use setState, NotifyListeners, or ChangeNotifier to accomplish this task.
I am trying to avoid using either a radio button or making buttons outside of the ValueListenableBuilder because my ValueListenableBuilder is designed to generate a lot of my other buttons and I was hoping to incorporate one more function into them. Thanks in advance!
ValueListenableBuilder
ValueListenableBuilder<Option>(
valueListenable: yesNo,
builder: (context, option, _) => MakeButtons(
num0: 0,
num1: 1,
makeButtonWidth: MediaQuery.of(context).size.width * 0.20,
selected: option,
onChanged: (newOption) =>
yesNo.option = newOption,
ifSelected: (newOption) {
setState(() {
yesNo.option = newOption;
yesNo;
});
},
),
),
Make Buttons
enum Option {
option0,
option1,
}
class MakeButtons extends StatefulWidget {
MakeButtons({
this.num0,
this.num1,
this.selected,
this.onChanged,
this.ifSelected,
this.makeButtonWidth,
});
final int num0;
final int num1;
final double makeButtonWidth;
final Option selected;
final Function ifSelected;
final ValueChanged<Option> onChanged;
#override
_MakeButtonsState createState() => _MakeButtonsState();
}
class _MakeButtonsState extends State<MakeButtons> {
List<Widget> makeButtons(int num0, int num1, List<Widget> children,
List<Color> colors, List<Function> onPresses) {
List<Widget> buttons = new List();
for (int i = num0; i < num1; i++) {
buttons.add(Container(
constraints: BoxConstraints(
minWidth: widget.makeButtonWidth,
),
child: RectButton(
buttonChild: children[i],
bgColor: colors[i],
onPress: onPresses[i]),
));
}
return buttons;
}
Option selectedOption;
#override
Widget build(BuildContext context) {
List<Widget> children = [
AutoSizeText(
'Yes',
textAlign: TextAlign.center,
style: TextStyle(fontWeight: FontWeight.w600, color: Colors.white),
),
AutoSizeText(
'No',
textAlign: TextAlign.center,
style: TextStyle(fontWeight: FontWeight.w600, color: Colors.white),
),
];
List<Color> colors = [
selectedOption == Option.option0
? kActiveButtonColor
: kInactiveButtonColor,
selectedOption == Option.option1
? kActiveButtonColor
: kInactiveButtonColor,
];
List<Function> onPresses = [
() {
setState(() {
selectedOption = Option.option0;
});
return widget.onChanged(Option.option0);
},
() {
setState(() {
selectedOption = Option.option1;
});
return widget.onChanged(Option.option1);
},
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children:
makeButtons(widget.num0, widget.num1, children, colors, onPresses),
);
}
}
Visibility
visible: yesNo.title == 'A' ||
yesNo == 'Yes',
child: InputRow(
myUnit: defaultUnit,
inputParameter: 'Units',
textField: unitController,
colour: kEmoryDBlue,
),
),

Show counter to number of elements hidden when overflow occurs in flutter row widget

Can anyone please help to implement this feature of Gmail that shows the counter to number of emails hidden when the email list becomes large ? I want to implement this in row widget where instead of being scrollable extra elements count is shown when overflow occurs.Gmail shows +15 counter for hidden emails
I was Curious to give a try to achieve the same effect, as asked.
Just in case, If anyone want a start for writing a custom one, then below code may help.
Here is my Code, Feel free to give any suggestions,
(For Now delete button in chips is not working bcoz of some logic problem, I will make it work another day)
import 'package:flutter/material.dart';
class Demo3 extends StatefulWidget {
#override
_Demo3State createState() => _Demo3State();
}
class _Demo3State extends State<Demo3> {
String temp = "";
bool showChips = false;
List<Widget> chipsList = new List();
TextEditingController textEditingController = new TextEditingController();
final _focusNode = FocusNode();
int countChipsToDeleteLater = 0;
#override
void initState() {
super.initState();
_focusNode.addListener(() {
print("Has focus: ${_focusNode.hasFocus}");
if (!_focusNode.hasFocus) {
showChips = false;
setState(() {});
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(new FocusNode());
},
child: new Container(
height: 500,
child: new Center(
child: Container(
width: 300,
child: !showChips
? Row(
children: [
buildTextField(),
showNumberWidgetIfAny(),
],
)
: Center(
child: Wrap(
children: [
Wrap(
children: buildChips(),
),
buildTextField(),
],
),
),
),
),
),
),
);
}
buildChips() {
return chipsList;
}
buildTextField() {
return Container(
width: 200,
child: new TextField(
showCursor: true,
focusNode: _focusNode,
autofocus: true,
cursorColor: Colors.black,
style: new TextStyle(fontSize: 22.0, color: Colors.black),
controller: textEditingController,
// decoration: InputDecoration.collapsed(
// hintText: "",
// ),
onChanged: (value) {
if (value.contains(" ")) {
checkWhatToStoreInChips(value, countChipsToDeleteLater);
textEditingController.clear();
setState(() {
showChips = true;
});
countChipsToDeleteLater++;
}
},
),
);
}
checkWhatToStoreInChips(String val, int chipsIndex) {
temp = "";
for (int i = 0; i < val.length; i++) {
if (val[i] == " ") {
break;
}
temp = temp + val[i];
}
addToChips(temp, chipsIndex);
}
addToChips(String tmp, int chipsIndex) {
chipsList.add(Chip(
// onDeleted: () {
// if (chipsList.length == 0) {
// countChipsToDeleteLater = 0;
// }
// chipsList.removeAt(chipsIndex);
// print(chipsList.length);
// print(chipsIndex);
// setState(() {});
// },
avatar: CircleAvatar(
backgroundColor: Colors.grey.shade800,
child: Text(tmp[0]),
),
label: Text(temp),
));
}
showNumberWidgetIfAny() {
int len = chipsList.length;
if (len >= 1) {
return GestureDetector(
onTap: () {
showChips = true;
setState(() {});
},
child: new Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.blue,
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: new Text(
"${chipsList.length.toString()} ",
style: new TextStyle(color: Colors.white, fontSize: 22),
),
),
),
);
}
return Container();
}
}
How it works:
Write something in text field, then press space, showChips boolean will become true
onChanged will detect the space and will send the string to a function.
That function will extract the string before space and then will add the string to a chip,
Finally the chip will be added to a chipslist.
We will have a boolean variable to check if the textfield is in focus and when to show the textfield and numberwidget (a widget which will keep count of the total chips, same like you asked in your question) or when to show the chipslist and textfield wraped in a wrap widget.
You can play around by changing the decoration of textfield to collapsed, to it look like the same as gmail.
Check this package, if you want to use custom package for ease.
I was facing a similar issue. I found a way to implement the Overflow count text.
Sample image
You basically have to paint the overflow text, and get its width like below
final TextPainter textPainter = TextPainter(
text: TextSpan(text: text, style: style),
textDirection: TextDirection.ltr,
textScaleFactor: WidgetsBinding.instance.window.textScaleFactor,
)..layout();
var textSize = textPainter.size;
textSize.width;
Then subtract that from the width available. Lets call it x.
Then create a sum of width for each row item(using TextPainter.layout() method mentioned above), till its value is less than x.
This way you'll know how many items can be shown in the row.
I have created a Flutter library to help with this.

Deleting a widget from a list of widgets [Flutter]

In a flutter application that I'm building(a to-do list basically), I wish to delete all the completed tasks(active checkboxes) with a single button.
As of now, it functions as expected ONLY WHEN the last checkbox is active and all the other active checkboxes(if any) are right above it, simultaneously.
The app malfunctions if there is even 1 inactive checkbox after an active checkbox.
Here's the code for the class that creates the task widget :
class addnote extends StatefulWidget {
final String task_txt;
final int index;// receives the value
addnote({ Key key, this.task_txt, this.index }): super(key: key);
#override
_addnoteState createState() => _addnoteState();
}
class _addnoteState extends State<addnote> {
bool value=false;
Widget txt_strike(String to_strike, bool str_value){
return str_value ?
AutoSizeText(to_strike,style: TextStyle(
decoration: TextDecoration.lineThrough,
fontFamily: "Quicksand",
fontSize: 20.0,
color: Colors.grey,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
)
:
AutoSizeText(to_strike,style: TextStyle(
//decoration: TextDecoration.lineThrough,
fontFamily: "Quicksand",
fontSize: 20.0,
color: Colors.black,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
);
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius:BorderRadius.circular(4.0),
boxShadow: [BoxShadow(
color:Colors.grey,
offset: Offset(-6.0,4.0),
blurRadius: 4.0,
)],
color: Colors.white
),
margin: EdgeInsets.symmetric(vertical: 5.0,horizontal: 3.0),
child:InkWell(
onTap: (){setState(() {
value=!value;
w_checks[widget.index]=!w_checks[widget.index];
});},
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(flex:1,
child: CircularCheckBox(
disabledColor: Colors.white,
activeColor: Colors.green,
value: value,
onChanged:(bool _changed)
{
// print(w_checks[Index]);
setState(() {
value=!value;
w_checks[widget.index]=!w_checks[widget.index];
});
}),
),
Expanded(flex:5,
child: txt_strike(widget.task_txt, value)),
],
),
)
);
}
}
And here's the code for the func that deletes the task widget :
void del_task(){
int count=0;
for(int i=0; i<w_checks.length; i++)
{
if(w_checks[i]==true)
count++;
}
while(count>=0)
{
for(int j=0; j<w_checks.length; j++)
{
if(w_checks[j]==true)
{
setState(() {
{ w_tasks.removeAt(j); //w_tasks is the list of all the tasks
w_checks.removeAt(j); //w_checks is the list that stores bool values corresponding to each checkbox
}
});
for(int k=j; k<w_checks.length; k++)
{
w_tasks[k]=w_tasks[k+1];
w_checks[k]=w_checks[k+1];
}
break;
}
}
count--;
}
}
Hope I was able to describe my problem, kindly help.
I'm not really shure about what are you doing inside del_task() but I think your problem is caused by the for cycle in which you are moving all later objects down by one position.
That operation is already performed by the "list.removeAt(index)" method, as u can see from the documentation: https://api.dart.dev/stable/2.10.2/dart-core/List/removeAt.html
I would have changed the code like this:
void del_task(){
for(int i=0; i<w_checks.length; i++)
{
if(w_checks[i]==true)
{
setState(() {
w_tasks.removeAt(i);
w_checks.removeAt(i);
});
i--;
}
}
}
I don't understand why you needed the count variable.
The code I posted should iterate throught w_checks.
When it found a "true" value, it will call removeAt(i) and then decrease index by one.

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});