I have a register page in which there are 3 input fields and on next button i need to change the input field and show the other so i have simply use boolean to show/hide input. But when i click on button its not changing the fields (Mean change the boolean to false/true) And even i try to simply add print so i can check but its not even printing the value. But if i use navigator to change page its working fine in button.
My code
class Body extends StatefulWidget {
#override
_BodyState createState() => _BodyState();
}
class _BodyState extends State<Body> {
bool first = true;
bool second = false;
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Background(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"SIGNUP",
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(height: size.height * 0.03),
SvgPicture.asset(
"assets/icons/signup.svg",
height: size.height * 0.35,
),
first ? RoundedInputField(
hintText: "Your Name",
icon: Icons.person,
onChanged: (value) {},
) : Container(),
first ? RoundedInputField(
hintText: "Your Email",
icon: Icons.mail,
onChanged: (value) {
print(value);
},
) : Container() ,
first ? RoundedPasswordField(
onChanged: (value) {},
) : Container() ,
second ? RoundedInputField(
hintText: "Your Number",
icon: Icons.phone,
onChanged: (value) {
print(value);
},
) : Container() ,
first ? Container(
margin: EdgeInsets.symmetric(vertical: 10),
width: size.width * 0.8,
child: ClipRRect(
borderRadius: BorderRadius.circular(29),
child: FlatButton(
padding: EdgeInsets.symmetric(vertical: 20, horizontal: 40),
color: kPrimaryColor,
onPressed: (){
print('working');
setState(){
first = false;
second = true;
}
},
child: Text(
'NEXT',
style: TextStyle(color: Colors.white),
),
),
),
): Container(),
second ? RoundedButton(
text: "REGISTER",
press: () {
print('working');
},
) : Container() ,
SizedBox(height: size.height * 0.03),
AlreadyHaveAnAccountCheck(
login: false,
press: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return LoginScreen();
},
),
);
},
),
],
),
),
);
}
}
i am using setState to change the boolean value which isn't working also try to simply print the string but its also not working. If i use navigator in this to change page its working fine
You should declare first and second at instance level, otherwise they will created again with default values after setStateis executed.
class Body extends StatelessWidget {
bool first = true;
bool second = false;
#override
Widget build(BuildContext context) {
....
}
}
setState is used incorrectly. You need to have
setState((){
first = false;
second = true;
});
Your code is calling setState without any action and after that, you change first and second variables (but outside setState).
Your code is equivalent to
setState();
first = false;
second = true;
You should not declare again first and second inside onPressed, otherwise you'll be dealing with new variables which only scope is this method
setState((){
first = false;
second = true;
});
Your widget needs to be stateful and not stateless, otherwise it can't change the state.
Related
Creating a Screen where I want to perform Flutter-FireBase Searching.But Visibility Toggle is not working as desired.
Desired Toggle Behaviour : When clicked on TextForm field , prefix icon and result card should be visible. Upon Clicking the Prefix Icon(Back Arrow) , Result List (Card) and the prefix icon itself should become invisible and TextField should unfocus .
Actual Behaviour : On clicking the prefix icon , Result set and prefix icon don't disappear , Prefix icon remains there and result set becomes invisible but occupies some space beneath the TextFormField
class AddAppointmentWidget extends StatefulWidget {
#override
_AddAppointmentWidgetState createState() => _AddAppointmentWidgetState();
}
class _AddAppointmentWidgetState extends State<AddAppointmentWidget> {
bool searchbartapped = false;
var queryResultSet = [];
var tempSearchStore = [];
// Search Function
initiateSearch(value) {
//body
}
#override
Widget build(BuildContext context) {
return ListView(
children: [
SizedBox(
height: 15,
),
Padding(
padding: const EdgeInsets.all(18.0),
child: Text('Search',
style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold)),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(
flex: 5,
child: TextFormField(
style: TextStyle(color: Color(0xff2a2a2a), fontSize: 18),
keyboardType: TextInputType.name,
onChanged: (value) {
initiateSearch(value);
},
onTap: () {
setState(() {
searchbartapped = true;
});
},
cursorColor: Color(0xff2a2a2a),
cursorWidth: 1.5,
decoration: InputDecoration(
hintText: "Search by Name",
prefixIcon: Visibility(
visible: searchbartapped,
child: IconButton(
icon: Icon(Icons.arrow_back),
color: Colors.black54,
onPressed: () {
setState(() {
searchbartapped = !searchbartapped;
queryResultSet = [];
tempSearchStore = [];
});
FocusScope.of(context).unfocus();
}),
),
)),
),
],
),
),
Visibility(
visible: searchbartapped,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
padding: EdgeInsets.all(5.0),
primary: false,
shrinkWrap: true,
children: tempSearchStore.map((element) {
print(element['name']);
return buildResult(context, element);
}).toList()),
),
),
],
);
}
}
Note The buildResult widget is working perfectly fine.
Problem is only with the visibilty toggle
The issue: When you tap the prefixIcon:
onPressed is called, setting searchbartapped to false which is what you want.
The onTap method of your TextFormField is also called (since prefixIcon is inside it), setting searchbartapped to true.
So what you want is to prevent the second event from happening. I tried to prevent the notification from bubbling up the tree but I couldn't. So what I ended up doing is a bit more manual but works just as well.
Solution: Add a variable (for example hideSearchTapped) which is set to true when the prefixIcon is called. Then when the onTap method of your TextFormField is called, check this variable:
If hideSearchTapped is true, set it to false
Else change searchbartapped as you did
Here is a working example:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() async {
runApp(
MaterialApp(
home: Scaffold(
body: new AddAppointmentWidget(),
),
),
);
}
class AddAppointmentWidget extends StatefulWidget {
#override
_AddAppointmentWidgetState createState() => _AddAppointmentWidgetState();
}
class _AddAppointmentWidgetState extends State<AddAppointmentWidget> {
bool searchbartapped = false;
bool hideSearchTapped = false;
var queryResultSet = [];
var tempSearchStore = [];
// Search Function
initiateSearch(value) {
//body
}
#override
Widget build(BuildContext context) {
return ListView(
children: [
SizedBox(
height: 15,
),
Padding(
padding: const EdgeInsets.all(18.0),
child: Text('Search', style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold)),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(
flex: 5,
child: TextFormField(
style: TextStyle(color: Color(0xff2a2a2a), fontSize: 18),
keyboardType: TextInputType.name,
onChanged: (value) {
initiateSearch(value);
},
onTap: () {
setState(() {
if (hideSearchTapped) {
hideSearchTapped = false;
} else {
searchbartapped = true;
}
});
},
cursorColor: Color(0xff2a2a2a),
cursorWidth: 1.5,
decoration: InputDecoration(
hintText: "Search by Name",
prefixIcon: Visibility(
visible: searchbartapped,
child: IconButton(
icon: Icon(Icons.arrow_back),
color: Colors.black54,
onPressed: () {
hideSearchTapped = true;
searchbartapped = !searchbartapped;
queryResultSet = [];
tempSearchStore = [];
setState(() {
});
FocusScope.of(context).unfocus();
return true;
}),
),
)),
),
],
),
),
Visibility(
visible: searchbartapped,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
padding: EdgeInsets.all(5.0),
primary: false,
shrinkWrap: true,
children: tempSearchStore.map((element) {
print(element['name']);
}).toList()),
),
),
],
);
}
}
Note: you should use lowerCamelCase to name your variable. So searchbartapped would become searchBarTapped.
I'm trying to create a crossword. I make the crossword grid using GridView.count. I get the template of the crossword from a List, the black squares are marked with # and are just black squares that you cannot do nothing with, as they should be. The rest of the grid cells are TextFields where the user can write the letters to make the crossword. Every crossword is different, so I have to create the grid programmatically. My problem is that I need to figure out if the word that the user wants to write is horizontal or vertical and to get the focus to switch from the first to the next cell (white square) while the user is writing. How to tell it which way to go (horizontal or vertical)? I also found out about FocusTraversalGroup and FocusTraversalOrder but no example whatsoever on how to use them.
Here is the code (in the code s is the List containing the crossword template):
edited code after #DungNgo suggestion:
class Cells extends StatefulWidget {
#override
_CellsState createState() => _CellsState();
}
class _CellsState extends State<Cells> {
final FocusScopeNode _node = FocusScopeNode(); //new code
#override
void dispose() {
_node.dispose(); //new code
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Crossword'),
),
body: SafeArea(
child: GridView.count(
crossAxisCount: 15,
mainAxisSpacing: 2,
crossAxisSpacing: 2,
children: List.generate(s.length, (index) {
return FocusScope( //new code
node: _node, //new code
child: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: s[index] == '#' ? Colors.black : Colors.white,
border: Border.all(color: Colors.black)
),
child:
s[index] == '#' ?
Text( '#' ) :
TextField(
cursorColor: Colors.black,
textAlign: TextAlign.center,
textAlignVertical: TextAlignVertical.bottom,
decoration: InputDecoration.collapsed(hintText: ""),
style: TextStyle( decoration: TextDecoration.none),
onChanged: (text){
_node.nextFocus(); //new code
},
),
)
);
}),
)
)
);
}
}
Now the cursor focuses automatically on the next cell but only horizontally. Anyone knows how to make it "move" vertically? I know that DirectionalFocusTraversalPolicyMixin exists and I see this
focusInDirection(TraversalDirection direction) => FocusTraversalGroup.of(context!)!.inDirection(this, direction);
but I have no idea how to implement any it!
Any help would be greatly appreciated.
Thanks
After much tinkering I finally found a way to do it. This is the code:
class Cells extends StatefulWidget {
#override
_CellsState createState() => _CellsState();
}
class _CellsState extends State<Cells> {
isVertical = false; // added a flag
final FocusScopeNode _node = FocusScopeNode();
#override
void dispose() {
_node.dispose();
super.dispose();
}
// added a diolog to let the user choose the writing direction (horizontal or vertical)
void _showDialog() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Direzione"),
content: Text("Scrivi in verticale?"),
actions: <Widget>[
FlatButton(
child: Text("SI"),
onPressed: () {
setState(() {
isVertical = true;
});
Navigator.of(context).pop();
},
),
FlatButton(
child: Text("NO"),
onPressed: () {
setState(() {
isVertical = false;
});
Navigator.of(context).pop();
},
),
],
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Crossword'),
),
body: SafeArea(
child: GridView.count(
crossAxisCount: 15,
mainAxisSpacing: 2,
crossAxisSpacing: 2,
children: List.generate(s.length, (index) {
return FocusScope(
node: _node,
child: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: s[index] == '#' ? Colors.black : Colors.white,
border: Border.all(color: Colors.black)
),
child:
s[index] == '#' ?
Text( '#' ) :
TextField(
cursorColor: Colors.black,
textAlign: TextAlign.center,
textAlignVertical: TextAlignVertical.bottom,
decoration: InputDecoration.collapsed(hintText: ""),
style: TextStyle( decoration: TextDecoration.none),
onChanged: (text){
isVertical == false ? _node.focusInDirection(TraversalDirection.right) : _node.focusInDirection(TraversalDirection.down); // this is what does it !!
},
),
)
);
}),
)
)
);
}
}
There are still many things to fix but my main question is answered.
i want to change the indexvalue (pictogramindex) of one page when we click nextbutton on another screen.I will explain briefly , I have 2 screens in my scenario the first screen contains an image and it's name , a textfield and nextbutton (i have provided a dummy data contains a list of image and it's names) the logic behind this is , when we complete the textfield box and click next button(after validate) the textfield value checks with the correctvalue which i was given in the dummy data and show it's synonym which also provided. when we click the next button we will go to another page which contains the correct answer(passed from first page) and a textfield in this the user can write about the correct answer ( validated) when click next button in this page (till this my applicationworks perfectly) i want to load the first page with it's index updated (+1) which i initialised as 0 (var pictogramindex=0). But in my case when coming back to first page the index is not updating it will automatically stores the initialised value. what i want is i want to update index on the first page when i click next button in the Second page .
my source code of first screen is shown here
class Pictogramscreen extends StatefulWidget {
final int length;
const Pictogramscreen({Key key, this.length}) : super(key: key);
#override
_PictogramscreenState createState() => _PictogramscreenState();
}
class _PictogramscreenState extends State<Pictogramscreen> {
#override
final _Key = GlobalKey<FormState>();
Color defaultcolor = Colors.blue[50];
Color trueColor = Colors.green;
Color falseColor = Colors.red;
Widget defcorrect = Text('');
var pictogramindex = 0;
TextEditingController usertitleInput = TextEditingController();
nextPictogram() {
setState(() {
pictogramindex++;
});
}
fillColor() {
setState(() {
usertitleInput.text == pictdata[pictogramindex]['pictcorrectword']
? defaultcolor = trueColor
: defaultcolor = falseColor;
});
}
correctText() {
setState(() {
usertitleInput.text == pictdata[pictogramindex]['pictcorrectword']
? defcorrect = Text(pictdata[pictogramindex]['pictsynonym'])
: defcorrect = Text(pictdata[pictogramindex]['pictcorrectword']);
});
}
reset() {
setState(() {
defaultcolor = Colors.blue[50];
defcorrect = Text('');
usertitleInput.clear();
});
}
void description(BuildContext ctx) {
Navigator.of(context).pushNamed('/user-description', arguments: {
'id': pictdata[pictogramindex]['pictid'],
'word': pictdata[pictogramindex]['pictcorrectword']
});
}
Widget build(BuildContext context) {
int length = pictdata.length;
return Scaffold(
body: pictogramindex < pictdata.length
? ListView(
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 20),
padding: EdgeInsets.all(15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Card(
margin: EdgeInsets.only(top: 20),
child: Image.network(
pictdata[pictogramindex]['pictimg']),
),
SizedBox(
height: 10,
),
Text(
pictdata[pictogramindex]['pictword'],
style: TextStyle(
fontSize: 25,
),
),
SizedBox(
height: 10,
),
//Card(
//color: Colors.blue,
// child: TextField(
// decoration: InputDecoration.collapsed(
// hintText: 'type here'),
//textAlign: TextAlign.center,
// onSubmitted: (value) {
// usertitleInput = value;
// print(usertitleInput);
// },
// ),
//),
Form(
key: _Key,
child: TextFormField(
controller: usertitleInput,
validator: (usertitleInput) {
if (usertitleInput.isEmpty) {
return 'Answer cannot be empty';
} else {
return null;
}
},
textAlign: TextAlign.center,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide:
BorderSide(color: Colors.blueAccent),
borderRadius: BorderRadius.all(
Radius.circular(15),
)),
labelText: 'Type your Answer',
filled: true,
fillColor: defaultcolor,
),
onFieldSubmitted: (value) {
usertitleInput.text = value;
fillColor();
correctText();
print(usertitleInput.text);
}),
),
SizedBox(
height: 10,
),
defcorrect,
SizedBox(
height: 10,
),
RaisedButton(
onPressed: () {
if (_Key.currentState.validate()) {
description(context);
// nextPictogram();
reset();
}
//
//if (_Key.currentState.validate() == correctText()) {
// nextPictogram;
// }
},
child: Text('Next'),
)
],
),
),
],
)
: Center(
child: Text('completed'),
));
}
}
my source code of the second screen is show here
class Userinputscreen extends StatefulWidget {
final String id;
final String word;
const Userinputscreen({Key key, this.id, this.word}) : super(key: key);
#override
_UserinputscreenState createState() => _UserinputscreenState();
}
class _UserinputscreenState extends State<Userinputscreen> {
final _Keey = GlobalKey<FormState>();
TextEditingController userdescription = TextEditingController();
var pictogramindex;
void nextpict(BuildContext context) {
Navigator.of(context).pushNamed('/main-screen');
}
// void nextpict(BuildContext context, int index) {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (ctx) => Pictogramscreen(
// index: i = 0,
// )));
// }
#override
Widget build(BuildContext context) {
final routeArgs =
ModalRoute.of(context).settings.arguments as Map<String, String>;
final correctWord = routeArgs['word'];
return MaterialApp(
home: Scaffold(
body: ListView(children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 50),
child: Center(
child: Container(
padding: EdgeInsets.all(20),
margin: EdgeInsets.only(top: 100),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
correctWord,
style: TextStyle(fontSize: 26),
),
SizedBox(
height: 10,
),
Form(
key: _Keey,
child: TextFormField(
controller: userdescription,
validator: (userdescription) {
if (userdescription.isEmpty) {
return 'Answer cannot be empty';
} else {
return null;
}
},
textAlign: TextAlign.center,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blueAccent),
borderRadius: BorderRadius.all(
Radius.circular(15),
)),
labelText: 'Type your Answer',
filled: true,
),
onFieldSubmitted: (value) {
userdescription.text = value;
print(userdescription.text);
}),
),
SizedBox(
height: 10,
),
RaisedButton(
onPressed: () {
if (_Keey.currentState.validate()) {
nextpict(context);
}
},
child: Text('Next'),
)
],
),
),
),
),
])),
);
}
}
If I get it right, you basically want to tell the initial page that it's state is updated(the index) elsewhere. You basically need to make your app "reactive".
As is said in Google Developers Tutorial:
One of the advantages of Flutter is that it uses reactive views, which you can take to the next level by also applying reactive principles to your app’s data model.
Use some sort of state management. You need to choose from and use either Bloc, InheritedWidget and InheritedModel, Provider(ScopedModel), or the like.
Check this article on flutter about state management, or this for a complete list of approaches
Is anyone know how to make the text editable after I press the button? I want it to be editable only when the user clicks the button. Below is the result that I needed.
If I want to edit the text "Festive Leave", then I need to click the edit button.
I think this should do what you want.
TextEditingController _controller =
TextEditingController(text: "Festive Leave");
bool _isEnable = false;
//These are initialize at the top
Row(
children: <Widget>[
Container(
width: 100,
child: TextField(
controller: _controller,
enabled: _isEnable,
),
),
IconButton(
icon: Icon(Icons.edit),
onPressed: () {
setState(() {
_isEnable = true;
});
})
],
),
Row(
children: <Widget>[
Container(
width: 100,
child: TextField(
decoration: InputDecoration(
hintText: "Festive Leave",
hintStyle: TextStyle(color: Colors.black),
),
enabled: _isEnable,
),
),
IconButton(
icon: Icon(Icons.edit),
onPressed: () {
setState(() {
_isEnable = true;
});
})
],
),
your choice this one the text will go away as soon as the user starts to type though
I think this will help you
class SampleDemo extends StatefulWidget {
#override
_SampleDemoState createState() => _SampleDemoState();
}
class _SampleDemoState extends State<SampleDemo> {
String title = "MyTitle";
bool isEditable=false;
#override
Widget build(BuildContext context) {
return Row(children: [
Expanded(
child: !isEditable
? Text(title)
: TextFormField(
initialValue: title,
textInputAction: TextInputAction.done,
onFieldSubmitted: (value) {
setState(() => {isEditable = false, title = value});
})),
IconButton(
icon: Icon(Icons.edit),
onPressed: () {
setState(() => {
isEditable = true,
});
},
)
]);
}
}
I'm trying to insert TextFormField on a click to take the name of the student. This thing is working fine. But the problem is when I integrate remove functionality than it's not working as expected.
I did take List<Student> to insert and remove items and converted this List into Map to plot items to UI and update user input name value to a specific indexed Student object value.
If we try adding items and removing them serially than it'll work fine but the only issue is when I remove a single item from in-between it will only update my List and Map but UI will not get updated. This is my code
import 'package:dynamic_input_add_flutter/student.dart';
import 'package:flutter/material.dart';
class SingleListUse extends StatefulWidget {
static final String tag = 'single-list-use';
#override
_SingleListUseState createState() => _SingleListUseState();
}
class Student1 {
String _name;
int _sessionId;
Student1(this._name, this._sessionId);
String get name => _name;
set name(String value) {
_name = value;
}
int get sessionId => _sessionId;
set sessionId(int value) {
_sessionId = value;
}
#override
String toString() {
return 'Student $_name from session $_sessionId';
}
}
class _SingleListUseState extends State<SingleListUse> {
List<Student1> _studentList = [];
Map<int, Student1> _studentMap = {};
void _addNewStudent() {
setState(() {
_studentList.add(Student1('', 1));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
child: Icon(
Icons.done,
color: Colors.white,
),
onPressed: () {
if (_studentList.length != 0) {
_studentList.forEach((student) => print(student.toString()));
} else {
print('map list empty');
}
},
),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
floatingActionButtonAnimator: FloatingActionButtonAnimator.scaling,
appBar: AppBar(
title: Text('Single Map Use'),
actions: <Widget>[
FlatButton(
onPressed: () {
_addNewStudent();
},
child: Icon(
Icons.add,
color: Colors.white,
),
)
],
),
body: Container(
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0),
child: Builder(
builder: (context) {
print("List : ${_studentList.toString()}");
_studentMap = _studentList.asMap();
print("MAP : ${_studentMap.toString()}");
return ListView.builder(
itemCount: _studentMap.length,
itemBuilder: (context, position) {
print('Item Position $position');
return Padding(
padding: EdgeInsets.only(top: 5.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextFormField(
initialValue: _studentMap[position].name.length != 0
? _studentMap[position].name
: '',
onFieldSubmitted: (name) {
setState(() {
_studentList[position].name = name;
});
},
decoration: InputDecoration(
hintText: 'enter student name',
hintStyle: TextStyle(
fontSize: 16.0,
color: Colors.black26,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.black12,
),
borderRadius: BorderRadius.all(
Radius.circular(15.0),
),
),
),
),
),
IconButton(
icon: Icon(
Icons.delete,
color: Colors.red,
),
onPressed: () {
setState(() {
_studentList.removeAt(position);
});
},
)
],
),
);
},
);
},
),
),
);
}
}
The first image is when we add a Student name in plus icon click.(every item in List is a TextFormField. When I remove the second item from UI it will remove 3rd one while technically from data structure that I've used (List & Map) it's removing 2nd (and that's ok). I have an issue of displayed UI after we perform any delete from between.
Since this is state-full widget make a variable bool showTextFormField = false in state class
now in widget use if(showTextFormField) <Widget>
now on button click
setState(){
showTextFormField = true;
}