I was trying to code a stateful widget on a statelessWidget class to put a bool and a stateState((), (show or hide password). So I followed some post which said that the solution is to convert.
I converted my class and I have some errors, maybe I should update my code but I don't know how to do that and I'm always getting errors like this:
36:32: Error: The setter '_isSecret' isn't defined for the class '_RoundedPasswordFieldState'.
-'_RoundedPasswordFieldState' is from 'package:yona/Login/Components/rounded_password_field.dart' ('lib/Login/Components/rounded_password_field.dart').
Try correcting the name to the name of an existing setter, or defining a setter or field named '_isSecret'.
Can you help me please ?
The problem is resolved, this is the new code
class _RoundedPasswordFieldState extends State<RoundedPasswordField> {
bool _isSecret = true;
#override
Widget build(BuildContext context) {
return TextFieldContainer(
child: TextFormField(
obscureText: _isSecret,
decoration: InputDecoration(
hintText: "Password",
border: InputBorder.none,
icon: Icon(
Icons.lock,
color: DarkTurquoise,
),
suffixIcon: InkWell(
onTap: () =>
setState(() => _isSecret = !_isSecret),
child: Icon(!_isSecret
? Icons.visibility
: Icons.visibility_off, color: DarkTurquoise),
),
),
),
);
}
}
To access _isSecret, you need to use widget.:
class RoundedPasswordField extends StatefulWidget {
final ValueChanged<String> onChanged;
const RoundedPasswordField({
required this.onChanged,
Key? key,
}) : super(key: key);
#override
_RoundedPasswordFieldState createState() => _RoundedPasswordFieldState();
}
class _RoundedPasswordFieldState extends State<RoundedPasswordField> {
bool _isSecret = true;
#override
Widget build(BuildContext context) {
return TextFieldContainer(
child: TextFormField(
obscureText:_isSecret,
decoration: InputDecoration(
hintText: "Password",
icon: Icon(
Icons.lock,
color: DarkTurquoise,
),
suffixIcon: InkWell(
onTap: () =>
setState(() => _isSecret = !_isSecret),
child: Icon(!_isSecret
? Icons.visibility
: Icons.visibility_off),
),
),
),
);
}
}
in setState you should change _isSecret to RoundedPasswordField._isSecret and the other problem is you are not calling onChanged.
working code:
class RoundedPasswordField extends StatefulWidget {
static bool _isSecret = true;
final ValueChanged<String> onChanged;
const RoundedPasswordField({
required this.onChanged,
Key? key,
}) : super(key: key);
#override
_RoundedPasswordFieldState createState() => _RoundedPasswordFieldState();
}
class _RoundedPasswordFieldState extends State<RoundedPasswordField> {
#override
Widget build(BuildContext context) {
return TextFieldContainer(
child: TextFormField(
obscureText: RoundedPasswordField._isSecret,
onChanged: widget.onChanged,
decoration: InputDecoration(
hintText: "Password",
icon: Icon(
Icons.lock,
color: DarkTurquoise,
),
suffixIcon: InkWell(
onTap: () =>
setState(() => RoundedPasswordField._isSecret = !RoundedPasswordField._isSecret),
child: Icon(!RoundedPasswordField._isSecret
? Icons.visibility
: Icons.visibility_off),
),
),
),
);
}
}
If you don't want to change _isSecret from outside of your widget then just use this code
class RoundedPasswordField extends StatefulWidget {
final ValueChanged<String> onChanged;
const RoundedPasswordField({
required this.onChanged,
Key? key,
}) : super(key: key);
#override
_RoundedPasswordFieldState createState() => _RoundedPasswordFieldState();
}
class _RoundedPasswordFieldState extends State<RoundedPasswordField> {
bool _isSecret = true;
#override
Widget build(BuildContext context) {
return TextFieldContainer(
child: TextFormField(
obscureText: _isSecret,
onChanged: widget.onChanged,
decoration: InputDecoration(
hintText: "Password",
icon: Icon(
Icons.lock,
color: DarkTurquoise,
),
suffixIcon: InkWell(
onTap: () => setState(() => _isSecret =
!_isSecret),
child: Icon(!_isSecret
? Icons.visibility
: Icons.visibility_off),
),
),
),
);
}
}
Related
in my Todo App list, I'm trying to make the global state in my task list view but I'm facing some issues. I'm using StatefulWidget and StatelessWidget. In StatelessWidget Checkbox onChanged I want to toggle the Checkbox value and comes from the parent Widget but it's showing some setState() build issue Please have a look If you can.
tasks_lists.dart
import 'package:flutter/material.dart';
class TasksList extends StatefulWidget {
const TasksList({super.key});
#override
State<TasksList> createState() => _TasksListState();
}
class _TasksListState extends State<TasksList> {
bool isChecked = false;
#override
Widget build(BuildContext context) {
return ListTile(
title: Text(
'List 1',
style: TextStyle(
color: Colors.black,
decoration:
isChecked ? TextDecoration.lineThrough : TextDecoration.none,
),
),
trailing: TaskCheckbox(
checkBoxState: isChecked,
toggleCheckboxState: (bool checkedBoxState) {
setState(
() {
isChecked = checkedBoxState;
},
);
},
),
);
}
}
class TaskCheckbox extends StatelessWidget {
final bool checkBoxState;
final Function? toggleCheckboxState;
const TaskCheckbox({
super.key,
required this.checkBoxState,
this.toggleCheckboxState,
});
#override
Widget build(BuildContext context) {
return Checkbox(
value: checkBoxState,
onChanged: toggleCheckboxState!(checkBoxState),
activeColor: Colors.lightBlueAccent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(2.0),
),
side: MaterialStateBorderSide.resolveWith(
(states) => BorderSide(
width: 2.0,
color: Colors.black,
),
),
);
}
}
This means that the function is called during build,
onChanged: toggleCheckboxState!(checkBoxState),
while this calls the function when Checkbox is tapped.
onChanged: (checkBoxState) => toggleCheckboxState!(checkBoxState),
Take a look of Introduction to widgets.
Your toggleCheckboxState function call during building the UI, so you need to put it inside addPostFrameCallback so it run after UI builds, like this:
toggleCheckboxState: (bool checkedBoxState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(
() {
isChecked = checkedBoxState;
},
);
});
},
You are changing value/state so you have to make TaskCheckbox a Stateful widget.
onChange is a Fuction(bool). try to change TaskCheckbox to this:
class TaskCheckbox extends StatelessWidget {
final bool checkBoxState;
final Function(bool)? toggleCheckboxState;
const TaskCheckbox({
super.key,
required this.checkBoxState,
this.toggleCheckboxState,
});
#override
Widget build(BuildContext context) {
return Checkbox(
value: checkBoxState,
onChanged: toggleCheckboxState!,
activeColor: Colors.lightBlueAccent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(2.0),
),
side: MaterialStateBorderSide.resolveWith(
(states) => BorderSide(
width: 2.0,
color: Colors.black,
),
),
);
}
}
How onTap clear TextField in below example?
Note TextField is positioned inside trailing in ListView
trailing: FittedBox(
fit: BoxFit.fill,
child: SizedBox(
height:40, width:100,
child: TextField(
controller: TextEditingController(text: _docData[index].quantity.toString()),
decoration: InputDecoration(
suffix: InkWell(
onTap: () {
setState(() {
});
},
child: Icon(
Icons.clear,
),
)
),
),
),
),
Try this
Create a list of TextEditingController
List<TextEditingController> controllers = List.empty(growable: true);
Then inside ListView.builder, create TextEditingController one by one and add it to controllers list.
Then assign to TextField and clear like this
child: TextField(
controller: controllers[index],
decoration: InputDecoration(
suffix: InkWell(
onTap: () {
setState(() {
controllers[index].clear();
});
},
child: Icon(
Icons.clear,
),
)
),
),
When we call the function:
setState(() {
});
The whole widget rebuilds hence your TextEditingController is initialized again so the text inside is reset to default which is empty
Full Code
1)First Screen
import 'package:flutter/material.dart';
import 'home_page.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyCustomForm(),
);
}
}
2)Second Screen
import 'package:flutter/material.dart';
class MyCustomForm extends StatefulWidget {
const MyCustomForm({Key? key}) : super(key: key);
#override
State<MyCustomForm> createState() => _MyCustomFormState();
}
class _MyCustomFormState extends State<MyCustomForm> {
TextEditingController _controller =TextEditingController();
#override
Widget build(BuildContext context) {
// question: delete textField inside the ListView
return Scaffold(
body: Container(
child: ListView(
children: [
TextField(
onChanged: (value){
_controller.text;
},
controller: _controller,
decoration: InputDecoration(
hintText: 'Enter Name',
suffix: InkWell(
onTap: (){
_controller.clear();
},
child:Icon(Icons.clear),
)
),
)
],
),
),
);
}
}
I'm using TextFormField in Flutter to input email for register page.
What I want is if there's no char '#' and '.' then it will get rejected.
This is the result I hope for :
input : name
errorText : "You should enter valid email address"
input : name#gmail.com
response : success
And this is my code :
TextFormField(
keyboardType: TextInputType.emailAddress,
inputFormatters: [
FilteringTextInputFormatter.allow(
RegExp("[a-z0-9#._-]")),
],
onChanged: (text) {
_onSearchChanged(text);
},
controller: emailController,
decoration: InputDecoration(
errorText:
isEmailInvalid ? "Email is already taken" : null,
hintText: 'Enter your email',
suffixIcon: isEmailvalid
? const Icon(
Icons.check,
color: Colors.green,
)
: const Icon(
Icons.check_circle,
color: Colors.transparent)
)),
You can use it to validate email, tell me if have any problem
bool isEmail = RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+#[a-zA-Z0-9]+\.[a-zA-Z]+").hasMatch(your_email_here);
You need to wrap your TextFieldForm inside a Form and set the validator for it. You can refer to this example code:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
#override
Widget build(BuildContext context) {
return Material(
child: Center(
child: Form(
autovalidateMode: AutovalidateMode.always,
onChanged: () {
Form.of(primaryFocus!.context!)!.save();
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ConstrainedBox(
constraints: BoxConstraints.tight(const Size(200, 50)),
child: TextFormField(
validator: (String? value) {
return value != null && RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+#[a-zA-Z0-9]+\.[a-zA-Z]+").hasMatch(value) ? null : 'Invalid email';
}
),
),
),
),
),
);
}
}
I want to add an item to my list the easiest way, but it doesn't change somehow. It must be simple, but somehow I cannot update my list.
If the whole project is needed to solve this problem, I can share the GitHub link. This project is Angela Yu's flutter course last project on Udemy.
This is my add_task screen:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:todoey/model/task.dart';
import 'package:todoey/model/task_bank.dart';
class AddTaskScreen extends StatefulWidget {
const AddTaskScreen({Key? key}) : super(key: key);
static const data = "Add Task";
static const data2 = "Add";
#override
State<AddTaskScreen> createState() => _AddTaskScreenState();
}
class _AddTaskScreenState extends State<AddTaskScreen> {
TextEditingController textEditingController = TextEditingController();
TaskBank taskBank = TaskBank();
String newTaskText = "";
void addItem() {
setState(() {
taskBank.allTasks.add(
Task(
text: newTaskText,
isDone: false,
),
);
textEditingController.clear();
});
Navigator.pop(context);
}
#override
Widget build(BuildContext context) {
return Container(
color: const Color(0xFF757575),
child: Container(
padding: const EdgeInsets.all(20),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20), topRight: Radius.circular(20)),
),
child: Column(
children: [
const Text(
AddTaskScreen.data,
style: TextStyle(
color: Colors.lightBlueAccent,
fontSize: 30.0,
),
),
TextField(
controller: textEditingController,
autofocus: true,
textAlign: TextAlign.center,
onChanged: (newText) {
newTaskText = newText;
},
),
const SizedBox(height: 10),
CupertinoButton(
child: const Text(AddTaskScreen.data2),
onPressed: () {
addItem();
},
color: Colors.lightBlueAccent,
)
],
),
),
);
}
}
This is listview
import 'package:flutter/material.dart';
import 'package:todoey/model/task_bank.dart';
class TasksListView extends StatefulWidget {
const TasksListView({
Key? key,
}) : super(key: key);
#override
State<TasksListView> createState() => _TasksListViewState();
}
class _TasksListViewState extends State<TasksListView> {
TaskBank taskBank = TaskBank();
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
),
child: ListView.builder(
itemCount: taskBank.allTasks.length,
itemBuilder: (context, index) {
bool isChecked = taskBank.allTasks[index].isDone;
return ListTile(
title: Text(taskBank.allTasks[index].text),
trailing: Checkbox(
checkColor: Colors.lightBlueAccent,
value: isChecked,
onChanged: (newVlue) {
setState(() {
isChecked
? isChecked = taskBank.taskDidntDone(index)
: isChecked = taskBank.taskDone(index);
});
print(taskBank.allTasks.length);
},
),
);
},
),
);
}
}
And these are my models.
class Task {
String text;
bool isDone;
Task({required this.text, this.isDone = false});
}
import 'package:todoey/model/task.dart';
class TaskBank {
List<Task> allTasks = [
Task(
text: "Complete to Listview Challange",
isDone: false,
),
Task(
text: "Complate to Udemy Course",
isDone: false,
),
];
bool taskDone(int index) {
return allTasks[index].isDone = true;
}
bool taskDidntDone(int index) {
return allTasks[index].isDone = false;
}
}
The issue is here TasksListView is on separate context and Ui is not getting updated. A simple solution will be passing TaskBank on TasksListView. I will encourage you to look on state-management.
Run on dartPad
class TasksListView extends StatefulWidget {
final TaskBank taskBank;
const TasksListView({
Key? key,
required this.taskBank,
}) : super(key: key);
#override
State<TasksListView> createState() => _TasksListViewState();
}
class _TasksListViewState extends State<TasksListView> {
late TaskBank taskBank;
#override
void initState() {
super.initState();
taskBank = widget.taskBank;
}
class PasswordTextField extends StatefulWidget {
final String hintText;
final IconData icon;
final TextEditingController controller;
final FormFieldValidator<String> validator;
PasswordTextField({
Key key,
this.hintText ,
this.icon = Icons.lock,
this.validator,
this.controller
}) : super(key: key);
#override
_PasswordTextFieldState createState() => _PasswordTextFieldState();
}
class _PasswordTextFieldState extends State<PasswordTextField> {
var _passwordVisible;
var _iconColor;
#override
void initState() {
_passwordVisible = true;
_iconColor = Colors.grey;
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return TextFieldContainer(
child: TextFormField(
controller: controller, //here
validator: validator, //here
obscureText: _passwordVisible,
cursorColor: kPrimaryightColor,
decoration: InputDecoration(
labelText: "Your Password",
labelStyle: TextStyle(color: kPrimaryColor) ,
hintText: hintText, /ere
border: InputBorder.none,
icon: Icon(
icon, //here
color: kPrimaryColor,
),
suffixIcon: IconButton(
icon: Icon(Icons.remove_red_eye),
color: _iconColor,
onPressed: (){
setState(() {
if(_passwordVisible == true){
_passwordVisible = false;
_iconColor = kPrimaryColor;
}else{
_passwordVisible = true;
_iconColor = Colors.grey;
}
});
},
),
),
),
);
}
#override
State<StatefulWidget> createState() {
// TODO: implement createState
throw UnimplementedError();
}
}
I want to access some attribute from my stateful widget, but I don't know how, it work like this in stateless, is there any another way to use this on another file?
I use it like this
PasswordTextField(
validator:(value){
if(value.isEmpty){
return "Please insert something";
}else if(value.length < 6){
return "Enter Correct Password Format (6 character)";
}else{
return null;
}
You can access the fields declared in your Stateful widget class in the state class by using the predefined field widget i.e. widget.controller. So your code will look like this:
#override
Widget build(BuildContext context) {
return TextFieldContainer(
child: TextFormField(
controller: widget.controller, // here
validator: widget.validator, // here
obscureText: _passwordVisible,
cursorColor: kPrimaryightColor,
decoration: InputDecoration(
labelText: "Your Password",
labelStyle: TextStyle(color: kPrimaryColor) ,
hintText: widget.hintText, // here
border: InputBorder.none,
icon: Icon(
widget.icon, // here
color: kPrimaryColor,
),
// Define other controls
),
),
);
}