I have a field for entering a phone number. The task is that as soon as one character has been entered, a cross appears on the right, which erases everything that the user has entered. It should only appear when at least one character has been entered into the field. If not, then it is invisible. How can this be implemented?
class PhoneScreen extends StatefulWidget {
const PhoneScreen({Key? key}) : super(key: key);
#override
State<PhoneScreen> createState() => _PhoneScreenState();
}
class _PhoneScreenState extends State<PhoneScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Padding(
padding: EdgeInsets.fromLTRB(20, MediaQuery.of(context).size.height * 0.2, 20, 0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new TextField(
decoration: new InputDecoration(labelText: "Enter your number"),
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
),
],
)
)
)
);
}
}
try the code below.
These are the steps with explanation:
Create a TextEditingController and link the controller to the input, now we can get value of the input using _inputController.text
Next add a boolean value to check if we can show the icon.
Change the boolean value in the onChanged function
With the suffixIcon property in the InputDecoration we add the IconButton and call two lines to reset input value and set the _showIcon value to false
PS: remember the setState calls the build method, so you can avoid unnecessary rebuilds by creating a custom input widget and reuse it
EDIT:
fixed keyboard does not work after you erase the entered value
class PhoneScreen extends StatefulWidget {
const PhoneScreen({Key? key}) : super(key: key);
#override
State<PhoneScreen> createState() => _PhoneScreenState();
}
class _PhoneScreenState extends State<PhoneScreen> {
final TextEditingController _inputController = TextEditingController();
bool _showIcon = false;
// Dispose the _inputController to release memory
#override
void dispose() {
_inputController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Padding(
padding: EdgeInsets.fromLTRB(
20, MediaQuery.of(context).size.height * 0.2, 20, 0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
onChanged: (String value) {
setState(() {
_showIcon = value.isNotEmpty;
});
},
controller: _inputController,
decoration: InputDecoration(
labelText: "Enter your number",
suffixIcon: _showIcon
? IconButton(
onPressed: () {
setState(() {
// Use the clear method instead of assigning an empty string
_inputController.clear();
_showIcon = false;
});
},
icon: const Icon(Icons.close),
)
: null,
),
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
),
],
),
),
),
);
}
}
Related
I had a TextFormField widget, a grey container to set the state and a container that will change color according to the bool assosiated with it displayed on my page.
I also have a list of strings (not displayed).
import 'package:flutter/material.dart';
class Test extends StatefulWidget {
const Test({Key? key}) : super(key: key);
#override
State<Test> createState() => _TestState();
}
class _TestState extends State<Test> {
bool activated = false;
TextEditingController textController = TextEditingController();
List<String> listOfStrings = [
'String01',
'String02',
'String03',
'String04',
'String05',
'String06',
'String07',
'String08',
'String09',
'String10',
];
#override
void dispose() {
textController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
SizedBox(
height: 20,
),
Container(
padding: EdgeInsets.symmetric(horizontal: 10),
alignment: Alignment.center,
child: TextFormField(
autofocus: false,
controller: textController,
style: TextStyle(
color: Colors.white,
fontSize: 12,
),
textInputAction: TextInputAction.done,
),
),
SizedBox(
height: 20,
),
InkWell(
onTap: () {
if (textController == listOfStrings) {
activated = true;
}
},
child: Container(
height: 20,
width: 60,
color: Colors.blueGrey,
)),
SizedBox(
height: 60,
),
Container(
width: 20,
height: 20,
color: activated ? Colors.green : Colors.red,
),
],
),
);
}
}
what I'm trying to do is that when a string is entered into the textformfield, I want it to compare the answer to the list and if there is a match, switch the bool to true and delete the string from the list.
I know I have to use an if statement in a setsate which is the grey contaner. (is it possible to setstate with enter on the keyboard instead?) but im not sure what that if statement should be to compare to the list of strings
thanks so much and any help would be greatly appreciated
Here is the answer of your first question
if (listOfStrings.contains(textController.text)) {
activated = true;
}
For your second question
Yes, you can change bool value when you press enter on TextFormField
You just need to check
onFieldSubmitted: (String s) {
//Here you can write whatever you want to do after entered
}
Remove the particular value from list
listOfStrings.removeWhere((element) => element == textController.text)
im trying to put the Textvalue, i have created via Texteditingcontroller into the text on the Neuigkeiten Page via controller.value, so the texteditingvalue gets displayed on the other page, but my texteditingcontroller do not get recognised in the neugkeitenpage and i cant edit anything there at all.
class InformationContentDetails extends StatefulWidget {
const InformationContentDetails({Key key}) : super(key: key);
#override
State<InformationContentDetails> createState() => _InformationContentDetails();
}
class _InformationContentDetails extends State<InformationContentDetails> {
bool isEnable = false;
var _controller = new TextEditingController(text: 'Allgemeine Informationen');
var _controller2 = TextEditingController();
String name = "Allgemeine Informationen";
String name2 = "Herzlich Willkommen ...";
textlistener(){
print("Update: ${_controller.text}");
print("Update: ${_controller2.text}");
}
#override
void initState() {
super.initState();
// Start listening to changes
_controller.addListener(textlistener);
_controller2.addListener(textlistener);
}
#override
Widget build(BuildContext context) {
return ResponsiveBuilder(
builder: (context, sizingInformation) {
var textAlignment;
if (sizingInformation.deviceScreenType == DeviceScreenType.desktop) {
textAlignment = TextAlign.left;
} else {
textAlignment = TextAlign.center;
}
return Container(
width: 650,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"${_controller.text}",
style: titleTextStyle(sizingInformation.deviceScreenType),
textAlign: textAlignment,
),
TextField(
enabled: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Informationen aktualisieren",
),
controller : _controller,
),
FlatButton(
child: Text('bearbeiten'),
onPressed:(){
setState((){
name = _controller.text;
isEnable = !isEnable;
});
},
),
Text(
name2,
style: descriptionTextStyle(sizingInformation.deviceScreenType),
textAlign: textAlignment,
),
Container(
child: TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Informationstext aktualisieren"
),
controller : _controller2,
),
),
Container(
child: FlatButton(
child: Text('bearbeiten'),
onPressed:(){
setState((){
name2 = _controller2.text;
isEnable = !isEnable;
});
},
),
),
],
)),
);
},
);
}
#override
void dispose() {
_controller.dispose();
_controller2.dispose();
super.dispose();
}
}
class NeuigkeitenContentDetails extends StatefulWidget {
const NeuigkeitenContentDetails({Key key}) : super(key: key);
#override
State<NeuigkeitenContentDetails> createState() => _NeuigkeitenContentDetailsState();
}
class _NeuigkeitenContentDetailsState extends State<NeuigkeitenContentDetails> {
#override
Widget build(BuildContext context) {
return ResponsiveBuilder(
builder: (context, sizingInformation) {
var textAlignment;
if (sizingInformation.deviceScreenType == DeviceScreenType.desktop) {
textAlignment = TextAlign.left;
} else {
textAlignment = TextAlign.center;
}
return Container(
width: 650,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Neuigkeiten',
style: titleTextStyle(sizingInformation.deviceScreenType),
textAlign: textAlignment,
),
SizedBox(
height: 30,
),
Text(
'Herzlich Willkommen',
style: descriptionTextStyle(sizingInformation.deviceScreenType),
textAlign: textAlignment,
)
],
),
);
},
);
}
}
It isn't clear from your code what you mean by pages.
How does InformationContentDetails relate to, or interact with, NeuigkeitenContentDetails?
If you are using e.g. named routes you may want to look at e.g. Pass arguments to a named route and or Send data to a new screen, if on the other hand you need to send information between different widgets in the widget tree you may need to look at something like Provider - allowing you to read-only or watch for change and rebuild subscribed code, or other state management solutions.
Otherwise, if they exist simultaneously in the widget tree and you don't want to use a dedicated state management solution you need to pass the necessary data up-and-down through parameters which can get messy. H.t.h.
Please tell me, I have a problem with textfield. When I finished entering data in the field, I press the Done key on the keyboard and I get this error Null check operator used on a null value
although all data is saved. And which operator has a null value, I can’t understand, after all, everything is saved. How can I remove this error or warning?
text_field
class PriceCounter extends StatefulWidget {
PriceCounter({Key? key, required this.price, this.onChanged})
: super(key: key);
double price;
final Function(double)? onChanged;
#override
State<PriceCounter> createState() => _PriceCounterState();
}
class _PriceCounterState extends State<PriceCounter> {
final _priceController = TextEditingController();
#override
void dispose() {
_priceController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
final FilterPriceCubit cubit = BlocProvider.of<FilterPriceCubit>(context);
return BlocBuilder<FilterPriceCubit, FilterPriceState>(
builder: (context, state) {
_priceController.text = state.fitlerPrice.toStringAsFixed(2).toString();
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 21),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
onPressed: () => cubit.priceFilterDecrement(state.fitlerPrice),
icon: SvgPicture.asset(constants.Assets.minus),
constraints: const BoxConstraints(),
padding: EdgeInsets.zero,
),
const SizedBox(width: 20),
SizedBox(
width: 100,
child: TextFormField(
keyboardType: TextInputType.number,
// controller: _priceController
// ..text = widget.price.toStringAsFixed(2),
controller: _priceController,
style: constants.Styles.normalBookTextStyleWhite,
textAlign: TextAlign.center,
decoration: const InputDecoration(
prefix: Text('JC',
style: constants.Styles.normalBookTextStyleWhite),
suffix: Text(
'KWh',
style: constants.Styles.smallerBookTextStyleWhite,
),
contentPadding: EdgeInsets.zero,
border: InputBorder.none,
),
onFieldSubmitted: (value) {
cubit.setPrice(value);
widget.onChanged!(double.parse(value));
},
error
An error occurs at this point:
widget.onChanged!(double.parse(value));
You have to be sure that widget.onChanged isn't null.
The best way is:
if (widget.onChanged != null) {
widget.onChanged!(double.parse(value));
}
or
widget.onChanged?.call(double.parse(value));
When I click on show/hide toggle, both password and username textfield values gets deleted. I understand that, when setState rebuilds the widget tree, it resets password/username textfield values to initial blank values. I know I can get value of textfield, However I am not able to implement logic, how can I first get current username and password value, save it somewhere and then insert it back at the time widget rebuilds alongwith setState.
import 'package:flutter/material.dart';
class LoginScreen extends StatefulWidget {
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
bool _showPassword = true;
#override
Widget build(BuildContext context) {
final usernameController = TextEditingController();
final passwordController = TextEditingController();
return SafeArea(
child: Scaffold(
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image(image: AssetImage('images/logo.png')),
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: usernameController,
decoration: InputDecoration(
hintText: "Enter Username",
labelText: "UserName",
),
),
SizedBox(
height: 20.0,
),
TextField(
obscureText: _showPassword,
controller: passwordController,
decoration: InputDecoration(
hintText: "Enter Password",
labelText: "Password",
suffixIcon: GestureDetector(
onTap: _togglePasswordVisibility,
child: _showPassword
? Icon(Icons.visibility)
: Icon(Icons.visibility_off),
),
),
),
SizedBox(
height: 20.0,
),
RaisedButton(
onPressed: () {
//call api here to authenticate user
},
child: Text("Login"),
),
],
),
),
Column(
children: [
Text("Don't have an account! Get it in 2 easy steps"),
RaisedButton(
onPressed: () {
//take user to registration screen
},
child: Text("Register"),
),
],
),
],
),
),
),
);
}
void _togglePasswordVisibility() {
setState(() {
_showPassword = !_showPassword;
});
}
}
When you use the setState to toggle the visibility, the build method will get called to redraw that widget. Since you are initialising your TextEditingController in the build method, it get initialised again and loose the previous value. To fix this you just remove the initialisation from build method to class level.
class _LoginScreenState extends State<LoginScreen> {
bool _showPassword = true;
final usernameController = TextEditingController();
final passwordController = TextEditingController();
#override
Widget build(BuildContext context) {
return SafeArea(
//... Your code
);
}
//... Your code
}
I have a Form where two text inputs are there. when user enter text in one input and goes to other the text vanishes . what to do ?there are four dart files main.dart,new_transaction.dart,
the form module
import 'package:flutter/material.dart';
class NewTransaction extends StatelessWidget {
final titleController = TextEditingController();
final amountController=TextEditingController();
final Function addTx;
NewTransaction(this.addTx);
void submitData()
{
final enterTitle =titleController.text;
final enterAmount=double.parse(amountController.text);
if (enterTitle.isEmpty||enterAmount<=0)
{return;}
addTx(enterTitle,enterAmount);
}
#override
Widget build(BuildContext context) {
return
Card(
elevation: 7,
child: Container(
padding: EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
TextField(
decoration: InputDecoration(labelText: 'title'),
controller: titleController,
onSubmitted:(_)=>submitData(),
),
TextField(
decoration: InputDecoration(labelText: 'amount'),
controller: amountController,
keyboardType: TextInputType.number,
onSubmitted:(_)=>submitData(),
),
FlatButton(
onPressed: submitData,
child: Text('Add Transaction'),
textColor: Colors.purple,
),
],
),
));
}
}
i am calling this from Main like this
main.dart
Its because NewTransaction is StatelessWidget. When setState is called from its parent titleController and amountController will be recreated. So the value will be empty.
Solution:
Make NewTransaction as StatefulWidget.
Explanation:
StatefulWidget have state for them (Kind of separate runtime memory block storage to store values of the variables). So even if the parent widget rebuilds, this State of the StatefulWidget won't be recreated. It will be just reused with previous persisted values.
But StatelessWidget don't have State (Won't maintain values of the variable). So if parent widget get rebuilds, then this StatelessWidget also rebuild. which means all the variable like titleController and amountController will be deleted and recreated(with empty values).
Try this code.
class _NewTransactionState extends State<NewTransaction> {
String title = "";
int amount = 0;
void submitData() {
if (title == "" || amount <= 0) {
print("Invalid");
//TODO:Handle invalid inputs
return;
}
widget.addTx(title, amount);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Card(
elevation: 7,
child: Container(
padding: EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
TextField(
decoration: InputDecoration(labelText: 'title'),
onChanged: (value) {
title = value;
},
onSubmitted: (_) => submitData(),
),
TextField(
decoration: InputDecoration(labelText: 'amount'),
keyboardType: TextInputType.number,
onSubmitted: (_) => submitData(),
onChanged: (value) {
amount = int.parse(value);
},
),
FlatButton(
onPressed: submitData,
child: Text('Add Transaction'),
textColor: Colors.purple,
),
],
),
)),
),
);
}
}
Hope this will work for you if not then tell me know in comment.