I have three pass fields which have icons to show/hide the pass. The default obscureText is true and when the user clicks in the icon, it calls a method _toggle that will turn the obscure text false, showing the textField content.
But, when the user clicks in the icon, it toggles to all the 3 textfields but i wanted toggle only the field clicked. How can I treat this?
My text fields (X 3):
TextFormField(
controller: _controller1,
decoration: _getInputDecoration("Write your current pass"),
keyboardType: TextInputType.text,
obscureText: _isToggle,
My get input decoration (with the icon inside a Gesture detector) :
suffixIcon:
Padding(
padding: EdgeInsetsDirectional.only(end: 12.0),
child: GestureDetector(
child: _isToggle ? Icon(Icons.lock_outline_rounded, color: Colors.black,) :
Icon(Icons.lock_open_rounded, color: Colors.black,),
onTap: _toggle,
)
),
This is the _toggle method:
void _toggle() {
setState(() {
_isToggle = !_isToggle;
});
}
Please check the code for dynamically setting the obscureText when you have multiple TextEditingController.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List _controller = List<TextEditingController>.generate(
3, (index) => TextEditingController());
List<bool> _isToggle = List<bool>.generate(3, (index) => true);
void _toggle(int index) {
setState(() {
_isToggle[index] = !_isToggle[index];
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Padding(
padding: EdgeInsets.all(10),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
for (int i = 0; i < 3; i++)
TextFormField(
controller: _controller[i],
//decoration: _getInputDecoration("Write your current pass"),
keyboardType: TextInputType.text,
obscureText: _isToggle[i],
decoration: InputDecoration(
suffixIcon: Padding(
padding: EdgeInsetsDirectional.only(end: 12.0),
child: GestureDetector(
child: _isToggle[i]
? Icon(
Icons.lock_outline_rounded,
color: Colors.black,
)
: Icon(
Icons.lock_open_rounded,
color: Colors.black,
),
onTap: () => _toggle(i),
),
),
),
),
],
),
),
),
);
}
}
You need to seperate _isToggle variable for each TextFormField. And set only the tapped TextFormField.
EDIT:
TextEditingController _controller1 = TextEditingController();
TextEditingController _controller2 = TextEditingController();
TextEditingController _controller3 = TextEditingController();
List<bool> toggleList = List<bool>();
void _toggle(int index) {
setState(() {
toggleList[index] = !toggleList[index];
// _isToggle = !_isToggle;
});
}
List<Widget> widgetList = List<Widget>();
InputDecoration _getInputDecoration(String string, int index) {
return InputDecoration(
isDense: true,
suffixIcon: Padding(
padding: EdgeInsetsDirectional.only(end: 12.0),
child: GestureDetector(
child: toggleList[index]
? Icon(
Icons.lock_outline_rounded,
color: Colors.black,
)
: Icon(
Icons.lock_open_rounded,
color: Colors.black,
),
onTap: () {
_toggle(index);
},
),
),
);
}
addList() {
widgetList.add(TextFormField(
controller: _controller1,
decoration: _getInputDecoration("Write your current pass", 0),
keyboardType: TextInputType.text,
obscureText: toggleList[0],
));
widgetList.add(TextFormField(
controller: _controller2,
decoration: _getInputDecoration("Write your current pass", 1),
keyboardType: TextInputType.text,
obscureText: toggleList[1],
));
widgetList.add(TextFormField(
controller: _controller3,
decoration: _getInputDecoration("Write your current pass", 2),
keyboardType: TextInputType.text,
obscureText: toggleList[2],
));
}
#override
Widget build(BuildContext context) {
return ListView.builder(
padding: const EdgeInsets.all(8),
itemCount: widgetList.length,
itemBuilder: (BuildContext context, int index) {
return widgetList[index];
});
It is because you're using a single variable(_isToggle) for all the fields.
Using 3 separate booleans would solve the problem.
bool _isToggle1=false;
bool _isToggle2=false;
bool _isToggle3=false;
suffixIcon:
Padding(
padding: EdgeInsetsDirectional.only(end: 12.0),
child: GestureDetector(
child: _isToggle1 ? Icon(Icons.lock_outline_rounded, color: Colors.black,) :
Icon(Icons.lock_open_rounded, color: Colors.black,),
onTap: ()=>setState(()=>_isToggle1=!_isToggle1),
)
),
Related
I have a form builded with Bloc package.
There are two options with textfields in it.
Switching between option i've made also with bloc and Visibility widget.
When I choose an option widget rebuilds, name and price values deletes.
What is the best way to save this values between choosing options?
Here is my Bloc code
class FormBloc extends Bloc<FormEvent, MyFormState> {
FormBloc() : super(MyFormState()) {
on<RadioButtonFormEvent>(_setRadioButtonState);
}
void _setRadioButtonState(
RadioButtonFormEvent event, Emitter<MyFormState> emit) async {
emit(RadioButtonFormState(
buttonIndex: event.buttonIndex,
buttonName: event.buttonName,
));
}
}
class MyFormState {}
class RadioButtonFormState extends MyFormState {
final int buttonIndex;
final String buttonName;
RadioButtonFormState({
required this.buttonIndex,
required this.buttonName,
});
}
abstract class FormEvent extends Equatable {}
class RadioButtonFormEvent extends FormEvent {
final int buttonIndex;
final String buttonName;
RadioButtonFormEvent({
required this.buttonIndex,
required this.buttonName,
});
#override
List<Object?> get props => [buttonIndex, buttonName,];
}
Here is Form code
class FormInput extends StatelessWidget {
const FormInput({super.key});
#override
Widget build(BuildContext context) {
final formBlocWatcher = context.watch<FormBloc>().state;
final nameController = TextEditingController();
final priceController = TextEditingController();
final formOneController = TextEditingController();
final formTwoController = TextEditingController();
final formThreeController = TextEditingController();
String formOptionController = '';
bool optionOneIsActive = true;
bool optionTwoIsActive = false;
if (formBlocWatcher is RadioButtonFormState) {
switch (formBlocWatcher.buttonIndex) {
case 0:
formOptionController = formBlocWatcher.buttonName;
break;
case 1:
optionTwoIsActive = true;
optionOneIsActive = false;
formOptionController = formBlocWatcher.buttonName;
break;
}
}
return Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
top: 15,
left: 15,
right: 15),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: nameController,
decoration: const InputDecoration(hintText: 'Name'),
),
const SizedBox(height: 10),
TextField(
controller: priceController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(hintText: 'Price'),
),
const SizedBox(height: 15),
OptionsWidget(),
Visibility(
visible: optionOneIsActive,
child: TextField(
controller: formOneController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(hintText: 'Form one'),
)),
Visibility(
visible: optionTwoIsActive,
child: Column(
children: [
TextField(
controller: formTwoController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(hintText: 'Form two'),
),
TextField(
controller: formThreeController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(hintText: 'Form three'),
),
],
)),
const SizedBox(height: 10),
ElevatedButton(
onPressed: () {
BlocProvider.of<ProductsListBloc>(context).add(
AddProductEvent(
productName: nameController.text,
productPrice: int.parse(priceController.text),
productDescOne: formOneController.text,
productDescTwo: formTwoController.text,
productDescThree: formThreeController.text,
formOption: formOptionController,
),
);
},
child: Text('Create New'),
),
],
),
);
}
}
class OptionsWidget extends StatelessWidget {
OptionsWidget({super.key});
int value = 0;
Widget CustomRadioButton(String text, int index, BuildContext context) {
final formBloc = BlocProvider.of<FormBloc>(context);
final blocWatch = context.watch<FormBloc>().state;
if (blocWatch is RadioButtonFormState) {
value = blocWatch.buttonIndex;
}
return OutlinedButton(
onPressed: () {
formBloc.add(RadioButtonFormEvent(
buttonIndex: index,
buttonName: text,
));
},
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
side: BorderSide(color: (value == index) ? Colors.blue : Colors.grey),
),
child: Text(
text,
style: TextStyle(
color: (value == index) ? Colors.blue : Colors.grey,
),
));
}
#override
Widget build(BuildContext context) {
return Row(
children: [
CustomRadioButton("option one", 0, context),
const SizedBox(width: 15),
CustomRadioButton("option two", 1, context),
],
);
}
}
Your FormInput class should be extends from StatefulWidget, not StatelessWidget.
After this change, you should remove all TextEditingController assignments from the build() method and move them into initState().
I'm emulating this search and filter github here and the codes are almost the same but the filtered results do not update instantly while I type and also I faced the following issues:
I will have to press enter on my laptop to finally get the filtered list
When I hit the close icon(which is to clear all the words), I will have to tap the searchbar again so that all my listtile are back on the listview.
Here's my code:
class _CurrencySelectState extends State<CurrencySelect> {
late List<Currency> resCur;
String query = '';
#override
void initState() {
super.initState();
resCur = currencyList;
}
void searchCur(String query) {
final List<Currency> filteredCur = currencyList.where((cur) {
final symbolLower = cur.symbol.toLowerCase(); // Search using symbol
final nameLower = cur.country.toLowerCase(); // Search using country
final searchLower = query.toLowerCase();
return symbolLower.contains(searchLower) ||
nameLower.contains(searchLower);
}).toList();
setState(() {
this.query = query;
resCur = filteredCur;
});
}
#override
Widget build(BuildContext context) {
Widget buildCur(Currency cur) => ListTile(
leading: Padding(
padding: EdgeInset.all(5)
child: SizedBox(
child: Column(
children: <Widget>[
SvgPicture.asset(
cur.assetPath,
),
]),
),
),
title: Column(
children: [
Text(
cur.symbol,
style: TextStyle(
...
),
Text(
cur.name,
style: TextStyle(
...
),
],
),
trailing: Text(
"0.25",
style: TextStyle(
...
),
);
return TextButton(
onPressed: () async {
showModalBottomSheet(
enableDrag: false,
context: context,
isScrollControlled: true,
builder: (BuildContext context) {
return DraggableScrollableSheet(
expand: false,
builder: (context, scrollController) {
return Column(
children: <Widget>[
SearchWidget(
text: query,
onChanged: searchCur,
hintText: "Enter symbol or country"
),
Expanded(
child: ListView.builder(
controller: scrollController,
itemCount: resCur.length,
itemBuilder: (context, int index) {
final cur = resCur[index];
return buildCur(cur);
},
),
)
],
);
},
);
});
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(
...
),
SvgPicture.asset(
...
)
],
));
}
}
Searchwidget code:
import 'package:flutter/material.dart';
class SearchWidget extends StatefulWidget {
final String text;
final ValueChanged<String> onChanged;
final String hintText;
const SearchWidget({
Key? key,
required this.text,
required this.onChanged,
required this.hintText,
}) : super(key: key);
#override
_SearchWidgetState createState() => _SearchWidgetState();
}
class _SearchWidgetState extends State<SearchWidget> {
final controller = TextEditingController();
#override
Widget build(BuildContext context) {
final styleActive = TextStyle(color: Colors.black);
final styleHint = TextStyle(color: Colors.black54);
final style = widget.text.isEmpty ? styleHint : styleActive;
return Container(
height: 42,
margin: const EdgeInsets.fromLTRB(16, 16, 16, 16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Colors.white,
border: Border.all(color: Colors.black26),
),
padding: const EdgeInsets.symmetric(horizontal: 8),
child: TextField(
controller: controller,
decoration: InputDecoration(
icon: Icon(Icons.search, color: style.color),
suffixIcon: widget.text.isNotEmpty
? GestureDetector(
child: Icon(Icons.close, color: style.color),
onTap: () {
controller.clear();
widget.onChanged('');
FocusScope.of(context).requestFocus(FocusNode());
},
)
: null,
hintText: widget.hintText,
hintStyle: style,
border: InputBorder.none,
),
style: style,
onChanged: widget.onChanged,
),
);
}
}
i am new to flutter. recently building a project of a timer app. ok the homepage is when the user open the app. it will enter a number for hours, minute and second they want to countdown. then this three numbers will be send to another page in a code line here
#override
void initState() {
super.initState();
controller = AnimationController(
vsync: this,
duration: Duration(seconds: inputdataint),//inputdataint means the constructor i build. don't know correct or not
);
}
so the problem here is when I want to send the three numbers that input by the user and send it to the second screen that is the Countdowntimer screen it gives me an error of the inputdataint isn't defined. but I already defined the inputdataint in the class. can anyone help.
this is the error part
Navigator.push(context,MaterialPageRoute(builder: (context)=>CountDownTimer(inputdataint:hourController.text )));
and this is my whole code. i don't know whether is my code got problem or maybe ...
class inputdatacountdown extends StatefulWidget {
#override
_inputdatacountdownState createState() => _inputdatacountdownState();
}
class _inputdatacountdownState extends State<inputdatacountdown> {
final hourController = TextEditingController();
final minuteController = TextEditingController();
final secondController = TextEditingController();
final _key = GlobalKey<FormState>();
int inputdata;
_inputdatacountdownState(){
inputdata=int.parse(hourController.text);
}
#override
Widget build(BuildContext context) {
ThemeData themeData = Theme.of(context);
final nametext =
MediaQuery.of(context).platformBrightness == Brightness.dark
? Colors.white
: Colors.black;
final nametextonbutton =
MediaQuery.of(context).platformBrightness == Brightness.dark
? Colors.black
: Colors.white;
return Scaffold(
body: Center(
child: Container(
child: Form(
key: _key,
child: Column(
children: [
TextFormField(
controller: hourController,
decoration: InputDecoration(
hintText: 'e.g 1',
labelText: 'hours',
border:
OutlineInputBorder(),
suffixIcon: IconButton(
icon: Icon(Icons.close),
onPressed: () =>
hourController
.clear(),
),
),
keyboardType:
TextInputType.number,
textInputAction:
TextInputAction.done,
validator: (value) {
if (value.isEmpty) {
return 'hours cannot be empty';
} else
return null;
}),
TextFormField(
controller: minuteController,
decoration: InputDecoration(
hintText: 'e.g 1',
labelText: 'minutes',
border:
OutlineInputBorder(),
suffixIcon: IconButton(
icon: Icon(Icons.close),
onPressed: () =>
minuteController
.clear(),
),
),
keyboardType:
TextInputType.number,
textInputAction:
TextInputAction.done,
validator: (value) {
if (value.isEmpty) {
return 'minutes cannot be empty';
} else
return null;
}),
TextFormField(
controller: secondController,
decoration: InputDecoration(
hintText: 'e.g 1',
labelText: 'second',
border:
OutlineInputBorder(),
suffixIcon: IconButton(
icon: Icon(Icons.close),
onPressed: () =>
secondController
.clear(),
),
),
keyboardType:
TextInputType.number,
textInputAction:
TextInputAction.done,
validator: (value) {
if (value.isEmpty) {
return 'second cannot be empty';
} else
return null;
}),
FlatButton(
color: nametext,
onPressed: () {
if (_key.currentState
.validate()) {
print(
'hour: ${hourController.text}');
print(
'minutes: ${minuteController.text}');
print(
'second: ${secondController.text}');
Navigator.push(context,MaterialPageRoute(builder: (context)=>CountDownTimer(inputdataint:hourController.text )));
}
},
child: Text(
'submit ',
style: TextStyle(
color:
nametextonbutton),
))
],
)),
),
),
);
}
}
class CountDownTimer extends StatefulWidget {
#override
_CountDownTimerState createState() => _CountDownTimerState();
}
class _CountDownTimerState extends State<CountDownTimer>
with TickerProviderStateMixin {
AnimationController controller;
String get timerString {
Duration duration = controller.duration * controller.value;
return '${duration.inMinutes}:${(duration.inSeconds % 60).toString().padLeft(2, '0')}';
}
int inputdataint;
CountDownTimer(int inputdataint){
this.inputdataint = inputdataint;
}
#override
void initState() {
super.initState();
controller = AnimationController(
vsync: this,
duration: Duration(seconds: inputdataint),//come back to you later
);
}
#override
Widget build(BuildContext context) {
ThemeData themeData = Theme.of(context);
final nametext =
MediaQuery.of(context).platformBrightness == Brightness.dark
? Colors.white
: Colors.black;
final nametextonbutton =
MediaQuery.of(context).platformBrightness == Brightness.dark
? Colors.black
: Colors.white;
return Scaffold(
backgroundColor: Colors.white10,
body: AnimatedBuilder(
animation: controller,
builder: (context, child) {
return Stack(children: <Widget>[
Padding(
padding: EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Align(
alignment: FractionalOffset.center,
child: AspectRatio(
aspectRatio: 1.0,
child: Stack(
children: <Widget>[
Positioned.fill(
child: CustomPaint(
painter: CustomTimerPainter(
animation: controller,
backgroundColor: Colors.white,
color: themeData.indicatorColor,
)),
),
Align(
alignment: FractionalOffset.center,
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
crossAxisAlignment:
CrossAxisAlignment.center,
children: <Widget>[
Text(
"Count Down Timer",
style: TextStyle(
fontSize: 20.0, color: nametext),
),
Text(
timerString,
style: TextStyle(
fontSize: 112.0, color: nametext),
),
],
),
),
],
),
),
),
),
AnimatedBuilder(
animation: controller,
builder: (context, child) {
return FloatingActionButton.extended(
onPressed: () {
if (controller.isAnimating)
controller.stop();
else {
controller.reverse(
from: controller.value == 0.0
? 1.0
: controller.value);
}
},
icon: Icon(controller.isAnimating
? Icons.pause
: Icons.play_arrow),
label: Text(
controller.isAnimating ? "Pause" : "Play"));
}),
],
))
]);
}),
);
}
}
What's happening there is that you send an argument named inputdataint to your Widget CountDownTimer by doing so:
Navigator.push(context,MaterialPageRoute(builder: (context)=>CountDownTimer(inputdataint:hourController.text )));
You can see there that you send the value hourController.text to the CountDownTimer as an argument called inputdataint.
The error came from the definition of your CountDownTimer widget definition.
You didn't declare the argument inputdataint but you are trying to use this value.
For doing so, you need to update your code like so:
class CountDownTimer extends StatefulWidget {
final String inputdataint;
const CountDownTimer({this.inputdataint});
#override
_CountDownTimerState createState() => _CountDownTimerState();
}
class _CountDownTimerState extends State<CountDownTimer>
with TickerProviderStateMixin {
// Your widget there...
// Use widget.inputdataint to use the data your passed to this widget.
}
As per: How to shift focus to next textfield in flutter?, I used FocusScope.of(context).nextFocus() to shift focus. But this doesn't work when you use a reusable textfield class. It only works when you directly use TextField class inside Column.
import 'package:flutter/material.dart';
void main() {
return runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final focus = FocusScope.of(context);
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
CustomTextField(
textInputAction: TextInputAction.next,
onEditingComplete: () => focus.nextFocus(),
),
const SizedBox(height: 10),
CustomTextField(
textInputAction: TextInputAction.done,
onEditingComplete: () => focus.unfocus(),
),
],
),
),
),
);
}
}
class CustomTextField extends StatelessWidget {
final TextInputAction textInputAction;
final VoidCallback onEditingComplete;
const CustomTextField({
this.textInputAction = TextInputAction.done,
this.onEditingComplete = _onEditingComplete,
});
static _onEditingComplete() {}
#override
Widget build(BuildContext context) {
return TextField(
textInputAction: textInputAction,
onEditingComplete: onEditingComplete,
);
}
}
In this code, if I click next in keyboard it will not shift focus to next textfield. Please help me with this.
That's because the context doesn't have anything it could grab the focus from. Replace your code with this:
void main() => runApp(MaterialApp(home: MyApp()));
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final focus = FocusScope.of(context);
return Scaffold(
appBar: AppBar(),
body: Column(
children: <Widget>[
CustomTextField(
textInputAction: TextInputAction.next,
onEditingComplete: () => focus.nextFocus(),
),
SizedBox(height: 10),
CustomTextField(
textInputAction: TextInputAction.done,
onEditingComplete: () => focus.unfocus(),
),
],
),
);
}
}
You need to wrap your fields in a form widget with a form key and use a TextFormField instead of textField widget. Set the action to TextInputAction.next and it should work! You can also use TextInput.done to trigger the validation.
Here a fully working exemple:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class LogInPage extends StatefulWidget {
LogInPage({Key key}) : super(key: key);
#override
_LogInPageState createState() => _LogInPageState();
}
class _LogInPageState extends State<LogInPage> {
final _formKey = new GlobalKey<FormState>();
bool isLoading = false;
String firstName;
String lastName;
String password;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
backgroundColor: Colors.black,
body: body(),
);
}
Widget body() {
return Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
showInput(
firstName,
TextInputType.name,
Icons.drive_file_rename_outline,
"FirstName",
TextInputAction.next,
onSaved: (value) => firstName = value.trim()),
showInput(lastName, TextInputType.name,
Icons.drive_file_rename_outline, "LastName", TextInputAction.next,
onSaved: (value) => lastName = value.trim()),
showInput(null, TextInputType.text, Icons.drive_file_rename_outline,
"Password", TextInputAction.done,
isPassword: true, onSaved: (value) => password = value),
Padding(
padding: EdgeInsets.symmetric(vertical: 10),
),
showSaveButton(),
],
),
);
}
Widget showInput(String initialValue, TextInputType textInputType,
IconData icon, String label, TextInputAction textInputAction,
{#required Function onSaved, bool isPassword = false}) {
return Padding(
padding: EdgeInsets.fromLTRB(16.0, 20.0, 16.0, 0.0),
child: new TextFormField(
style: TextStyle(color: Theme.of(context).primaryColorLight),
maxLines: 1,
initialValue: initialValue,
keyboardType: textInputType,
textInputAction: textInputAction,
autofocus: false,
obscureText: isPassword,
enableSuggestions: !isPassword,
autocorrect: !isPassword,
decoration: new InputDecoration(
fillColor: Theme.of(context).primaryColor,
hintText: label,
hintStyle: TextStyle(color: Theme.of(context).primaryColorDark),
filled: true,
contentPadding: new EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0),
border: new OutlineInputBorder(
borderRadius: new BorderRadius.circular(12.0),
),
icon: new Icon(
icon,
color: Theme.of(context).primaryColorLight,
)),
validator: (value) {
return value.isEmpty && !isPassword
? "You didn't filled this field."
: null;
},
onSaved: onSaved,
onFieldSubmitted:
textInputAction == TextInputAction.done ? (value) => save() : null,
),
);
}
Widget showSaveButton() {
return RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(100))),
color: Theme.of(context).primaryColor,
padding: EdgeInsets.symmetric(vertical: 12, horizontal: 25),
child: isLoading
? SizedBox(height: 17, width: 17, child: CircularProgressIndicator())
: Text(
"Sauvegarder",
style: TextStyle(color: Theme.of(context).primaryColorLight),
),
onPressed: save,
);
}
void save() async {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
//TODO
}
}
}
FocusNode textSecondFocusNode = new FocusNode();
TextFormField textFirst = new TextFormField(
onFieldSubmitted: (String value) {
FocusScope.of(context).requestFocus(textSecondFocusNode);
},
);
TextFormField textSecond = new TextFormField(
focusNode: textSecondFocusNode,
);
// render textFirst and textSecond where you want
I have a form where i want the clear button to appear on the right side of the textfield only when user start entering data and disappear if user delete all the data he input in the textfield. currently, i was able to add the clear button but it is there always.
see my code below
this is the code for my textiput
import 'package:flutter/material.dart';
import 'package:finsec/utils/hex_color.dart';
class CustomTextField extends StatelessWidget {
CustomTextField({
this.textInputType,
this.textController ,
this.errorMessage,
this.labelText,
});
TextInputType textInputType;
TextEditingController textController;
String errorMessage, labelText;
#override
Widget build(BuildContext context) {
bool isError = false;
return Container(
child: TextFormField(
keyboardType: textInputType,
style: Theme
.of(context)
.textTheme
.title,
controller: textController,
validator: (String value) {
if (value.isEmpty) {
return errorMessage;
}
},
decoration: InputDecoration(
suffixIcon: IconButton(
onPressed: (){
textController.clear();
},
icon: Icon(
Icons.clear,
color: Colors.grey,
),
),
labelStyle: TextStyle(
color: Colors.grey,
fontSize: 16.0
),
contentPadding: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0), //size of textfield
errorStyle: TextStyle(
color: Colors.red,
fontSize: 15.0
),
border: OutlineInputBorder(
borderSide: BorderSide(width:5.0),
borderRadius: BorderRadius.circular(5.0)
)
)
),
);
}
}
here is my code for the form
import 'package:flutter/material.dart';
import 'package:finsec/widget/row_text_input.dart';
import 'package:finsec/widget/text_form_field.dart';
import 'package:finsec/widget/save_button.dart';
import 'package:finsec/utils/strings.dart';
import 'package:finsec/utils/dimens.dart';
import 'package:finsec/utils/colors.dart';
import 'package:finsec/widget/column_text_input.dart';
void main() {
runApp(MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Simple Interest Calculator App',
home: ThirdFragment(),
theme: ThemeData(
brightness: Brightness.dark,
primaryColor: Colors.indigo,
accentColor: Colors.indigoAccent),
));
}
class ThirdFragment extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _ThirdFragmentState();
}
}
class _ThirdFragmentState extends State<ThirdFragment> {
var _formKey = GlobalKey<FormState>();
var _currencies = ['Rupees', 'Dollars', 'Pounds'];
final double _minimumPadding = 5.0;
var _currentItemSelected = '';
#override
void initState() {
super.initState();
_currentItemSelected = _currencies[0];
// principalController.addListener(onChange);
}
TextEditingController amountController = TextEditingController();
TextEditingController frequencyController = TextEditingController();
TextEditingController datePaidController = TextEditingController();
TextEditingController categoryController = TextEditingController();
TextEditingController depositToController = TextEditingController();
TextEditingController descriptionController = TextEditingController();
var displayResult = '';
#override
Widget build(BuildContext context) {
TextStyle textStyle = Theme.of(context).textTheme.title;
return Scaffold(
appBar: AppBar(
title: Text('Simple Interest Calculator'),
),
body: Form(
key: _formKey,
onChanged: ,
child: SingleChildScrollView(
child: Column (children: [
Padding(
padding: EdgeInsets.only(top: 10.0, bottom: 5.0, left: 15.0, right: 15.0),
child: CustomTextField(textInputType:TextInputType.number,
textController: amountController,
errorMessage:'Enter Income Amount',
labelText:'Income Amount for testing'),
),
RowTextInput(inputName: 'Frequency:',
textInputType: TextInputType.number,
textController: frequencyController,
errorMessage: 'Choose Income Frequency',
labelText: 'Income Amount for testing'
),
RowTextInput(inputName: 'Date Paid:',
textInputType: TextInputType.number,
textController: datePaidController,
errorMessage: 'Pick Income Payment Date',
labelText: 'Income Amount for testing'
),
RowTextInput(inputName: 'Category:',
textInputType: TextInputType.number,
textController: categoryController,
errorMessage: 'Enter Income Category',
labelText: 'Income Amount for testing'
),
RowTextInput(inputName: 'Deposit To:',
textInputType: TextInputType.number,
textController: depositToController,
errorMessage: 'Choose Bank Acct Where Income Is Deposited',
labelText: 'Income Amount for testing'
),
RowTextInput(inputName: 'Description:',
textInputType: TextInputType.number,
textController: descriptionController,
errorMessage: 'Please enter principal amount',
labelText: 'Income Amount for testing'
),
SizedBox(height: 20),
//saveButton()
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
MaterialButton(
height: margin_40dp,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(margin_5dp)),
minWidth: (MediaQuery.of(context).size.width * .9) / 2,
color: Theme.of(context).primaryColor,
textColor: white,
child: new Text(save),
onPressed: () => {
setState(() {
if (_formKey.currentState.validate()) {
// amountController.text.isEmpty ? amountController.text='Value require' : amountController.text='';
//this.displayResult = _calculateTotalReturns();
}
})
},
splashColor: blueGrey,
),
MaterialButton(
height: margin_40dp,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(margin_5dp)),
minWidth: (MediaQuery.of(context).size.width * .9) / 2,
color: Theme.of(context).primaryColor,
textColor: white,
child: new Text(save_and_continue),
onPressed: () => {},
splashColor: blueGrey,
)
])
]
),
),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:finsec/widget/text_form_field.dart';
class RowTextInput extends StatelessWidget {
RowTextInput({
this.inputName,
this.textInputType,
this.textController ,
this.errorMessage,
this.labelText,
// this.hint,
// this.height,
// this.padding,
// this.headerRadius,
});
TextInputType textInputType;
TextEditingController textController;
String inputName, errorMessage, labelText;
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(
top: 5.0, bottom: 5.0, left: 15.0, right: 15.0),
child: Row(children: [
Expanded(
child: Text(this.inputName, maxLines: 1,)
),
Expanded(
flex: 3,
child: CustomTextField(textInputType:TextInputType.number,
textController: this.textController,
errorMessage: this.errorMessage
),
),
]),
);
}
}
i am expecting the clear (x button) to disappear when textfield is empty and appear when user type or select a value from dropdown etc. can someone help? thanks in advance
You can make use of Dart's conditional expression to check if textfield is empty then don't show X button else show it. For ex, the textController is used to retrieve value of textfield. You can check if the value retrieved is greater than 0 then show X button, else show empty container().
textController.text.length > 0 ? IconButton(icon: Icon(Icons.clear), onPressed: () {} : Container()
Note: You will need to adjust above line w.r.t your code as applicable.
Hope this helps and resolves your issue.