I need to make a search page. Made by means of TextField a field on clicking on which the page of search should open. Tell me how to implement clicking on the TextField and so that the back button appears on the left and the buttons disappear on the right?
code
TextFormField(
style: constants.Styles.textFieldTextStyleWhite,
cursorColor: Colors.white,
decoration: InputDecoration(
contentPadding: const EdgeInsets.only(
top: 15, // HERE THE IMPORTANT PART
),
border: InputBorder.none,
prefixIcon: Align(
alignment: Alignment.centerLeft,
child: SvgPicture.asset(
constants.Assets.search,
width: 20,
height: 20,
))),
)
Normal state
After clicking on the line
Wrap everything into a StatefulWidget.
Then, when clicking the TextFormField, change the attributes of the StatefulWidget.
class YourPage extends StatefulWidget {
_YourPageState createState() => _YourPageState();
}
class _YourPageState extends State<YourPage> {
var myBool = false;
// Initialize your Row-buttons here
// ...
void changeRow(){
setState(() {
// Hide or show Row-buttons here.
myBool = true;
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Scaffold(
body: Row(children:[
myBool == true
? Icon( ...) // shows icon
: SizedBox.shrink(), // shows nothing
TextFormField( onTap: () => changeRow() ),
// other BUTTONs here
])
),
);
}
}
There are a few possibilities for an AppBar to show Text or Buttons.
Check these examples:
https://www.fluttercampus.com/tutorial/10/flutter-appbar/
Related
So I have 3 tabs HEX , RGB, and HSL as StatefulWidget.
I have these 3 tabs in a Row in a StatelessWidget.
By default, HEX is selected, which I achieved successfully by initialising initState(). What I want to achieve now is that if I tap on any tab, the other two have to automatically go to a deselected state.
I used GestureDetector but it only changes the state of the tab I tapped on. I toggled the styles of all 3 tabs in the setState() function, but I guess the changes don't take place as the other 2 tabs are not built again, only the tab I tapped on is built again. By built I mean createState().
I am a beginner in Flutter, and I have spent a whole day finding solutions to this but to no avail.
I tried rebuilding the parent Row but I am not able to do so. I am trying to find a method by which I can rebuild widgets by key.
Any solution?
EDIT: The code for that section ->
key arrayKey = GlobalKey();
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//++++++++++++++++++++CHIPS++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
class chipMaker extends StatefulWidget {
chipMaker({required this.label}) : key = ObjectKey(label), chipSelect = false;
final String label;
final Key key;
bool chipSelect;
#override
_chipState createState() => _chipState();
}
class _chipState extends State<chipMaker> {
chipMaker get chip => super.widget;
//need to add setState();
#override
Widget build(BuildContext context) {
// TODO: implement build
return GestureDetector(
onTap: () {
//function
},
child: Container(
margin: EdgeInsets.only(left: 8),
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
border: Border.all(
color: defaultColor,
width: 2,
style: BorderStyle.solid,
),
borderRadius: BorderRadius.all(Radius.circular(4)),
color: chip.chipSelect ? defaultColor : Colors.white,
),
child: Text(
chip.label,
style: TextStyle(
fontSize: 10,
height: 1.2,
color: chip.chipSelect ? Colors.white : defaultColor,
),
),
),
);
}
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//++++++++++++++++++++ARRAY++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
class chipArray extends StatelessWidget {
chipArray({Key? arrayKey,}) : super(key: arrayKey);
#override
Widget build(BuildContext context) {
// TODO: implement build
return Row(
children: [
chipMaker(label: 'HEX',),
chipMaker(label: 'RGB',),
chipMaker(label: 'HSL',),
],
);
}
}
Suppose I have a application with login function there are two text input field Username & Password. and there is on button called Login. Now if user enters Username and password and hit Login button then app will navigate him/her to next page like account page. But if user pressed login button with any or both field empty than a tooltip will called like "username & password cant be empty"...So i think login button will call a function (when pressed) which required two values username & password and if this values are empty then tooltip called otherwise navigate to next page...
Well, I am new to programming so maybe my whole logic can be wrong, and if logic is ok then what can I do??
Here is my code...
import 'package:flutter/material.dart';
import 'package:login_signup/accountpage.dart';
void main(List<String> args) {
runApp(logsign());
}
class logsign extends StatefulWidget {
#override
_logsignState createState() => _logsignState();
}
class _logsignState extends State<logsign> {
#override
Widget build(BuildContext context) {
return MaterialApp(
routes: {
'/account': (context) => AccountPage(),
},
theme: ThemeData(
primarySwatch: Colors.green,
),
title: "logsignApp",
home: homepage(),
);
}
}
class homepage extends StatefulWidget {
#override
_homepageState createState() => _homepageState();
}
class _homepageState extends State<homepage> {
String usrname;
String passwd;
void detailsmust (String usrname, passwd){
if (usrname == "" || passwd == ""){
Tooltip(message: "Username & Password field cant be empty");
}
else{
Navigator.pushNamed(context, '/account')
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Login/Signup"),
),
drawer: Text("Just a drawer", textScaleFactor: 2.0),
body:
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
onChanged: (text){
usrname = text;
},
maxLength: 30,
decoration: InputDecoration(
prefixIcon: Icon(Icons.account_box),
labelText: 'Username',
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8.0)),
),
),
// Padding(
// padding: EdgeInsets.all(2.0),
// ),
TextField(
onChanged: (text){
passwd = text;
},
obscureText: true,
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock_outline),
labelText: 'Password',
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8.0)),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
MaterialButton(
onPressed: detailsmust,
child: Text("Login"),
color: Colors.green,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
),
Padding(
padding: EdgeInsets.all(5.0),
),
MaterialButton(
onPressed: (){},
child: Text("Exit"),
color: Colors.green,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
),
],
),
],
),
);
}
}
Welcome to programming, you're logic is right. There are many ways to check for this value and functionality that you want. In Flutter you can use TextFields or TextFormFields which have TextEditingControllers, and validators and lots of other functionalities that you should look into. And these functionalities can indeed validate user input, and enable or disable certain functions such as logging in users as your case. What ever you can think of can be done. Start reading and working.
edit1:
Regarding the general purpose of your app, I would advise you to use it like this, when working with TextFields, make sure to remember that you have TextEditingControllers that would come in handy, thus, you should modify your submit function to look something like this:
TextEditingController usrname = TextEditingController();
TextEditingController passwd = TextEditingController();
void detailsmust() {
if (usrname.text == "" || passwd.text == "") {
setState(() {//setState is needed in order for the UI to change.
Tooltip(message: "Username & Password field cant be empty");
});
} else {
Navigator.pushNamed(context, '/account'); //was missing a semicolon here
}
}
And in your textFields, you don't have to use the on changed now, just point to your controllers, like this:
//for userName
TextField(
maxLength: 30,
controller:usrname
//rest of your textfields as they were
)
//for password
TextField(
controller:passwd
)
You don't have to declare the variables String username or String passwd anymore, and the text editing controllers willbe listening to the inputs of the fields.
To capture the values being held inside the controllers, you use the property .text, as in username.text.
I have this Widget to register. Inside I want to ask for 6 inputs to register, but as not too much space on the screen, I splitted in 2 pair of 3. I show three at first in a form and when the user press the continue button I show the other 3. However, when I press the continue button, the new 3 pair of TextField appear with the same value of the previous ones. And they move position a little under. I don't know why it happens since each of those 6 fields is different Widget function.
I created two variables form1 and form2 to hold the different forms
#override
void initState() {
super.initState();
form1 = <Widget>[
firstForm(),
Text("Or Sign Up with social media"),
SizedBox(
height: 20,
),
socialMediaButtons(),
SizedBox(
height: 50,
),
Text("Have an account? Login")
];
form2 = <Widget>[
secondForm(),
Text("Or Sign Up with social media"),
SizedBox(
height: 20,
),
socialMediaButtons(),
SizedBox(
height: 50,
),
Text("Have an account? Login")
];
}
All the text field have the same format as the text field below, I only changed the variable for their respecting field.
Widget firstNameField() {
return TextFormField(
initialValue: "",
decoration: InputDecoration(
contentPadding: EdgeInsets.only(top: 10, left: 20, right: 20),
border: InputBorder.none,
hintText: "First Name",
hintStyle: TextStyle(color: Colors.grey[400], fontSize: 15)),
onChanged: (val) {
setState(() => firstName = val);
},
);
}
I combined the text fields in two widgets (firstForm and secondForm). (Shown firstForm but it is the same format as second, just called the functions for the other widgets).
Widget firstForm() {
return Column(
children: <Widget>[
emailPassField(),
SizedBox(
height: 50,
),
continueButton(),
SizedBox(
height: 50,
),
],
);
}
Then this is the continue button widget which when pressed. show the second form. I change the step variable to 2 to go to the second form.
Widget continueButton() {
return ButtonTheme(
minWidth: 185.0,
height: 48.0,
child: RaisedButton(
color: Colors.black,
textColor: Colors.white,
child: Text("continue"),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(50.0)),
onPressed: () => setState(() => step = 2),
));
}
When the variable step is changed, I created this function (getForm) to be called and to show the correct form array variable for the children of the column.
#override
Widget build(BuildContext context) {
return Material(
child: Expanded(
child: Stack(
children: <Widget>[
// banner with picture
Positioned(
child: banner(),
),
// Login Elements Container
Positioned(
child: Container(
margin: EdgeInsets.only(top: 300.0),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 5,
blurRadius: 20,
offset: Offset(0, 0))
],
borderRadius: BorderRadius.only(
topRight: Radius.circular(50),
topLeft: Radius.circular(50))),
child: Center(
child: Column(
children: getForm(step),
),
),
),
)
],
),
),
);
}
//functions for switching forms
getForm(int form) {
if (form == 1) {
return form1;
} else if (form == 2) {
return form2;
}
}
This is how the first step of the form appear.
first form
If I don't enter any data in the text fields and press the continue button, the second form with the correct text fields will appear as shown in this below image. You can see that they have the correct hint text.
second form
However if I enter some data on the first step of the form (seen in second form with data step 1), and then press the continue button, in the second step, the text fields will move down a little bit and the same value entered in the previous text fields will appear in the others too(second form with data step 2). can someone help me please, I don't what's going on there? I hope you understand the code and be able to help me please.
second form with data step 1
second form with data step 2
You need to create a TextEditingController for each TextFormField.
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
...
final _cityController = TextEditingController();
...
// initState
...
// dispose of all TextEditingControllers
#override
void dispose {
_emailController.dispose();
...
_cityController.dispose();
super.dispose();
}
// do this for every TextFormField
Widget firstNameField() {
return TextFormField(
// pass the corresponding controller, no need to set initial value if empty
controller: _firstNameController,
decoration: InputDecoration(
contentPadding: EdgeInsets.only(top: 10, left: 20, right: 20),
border: InputBorder.none,
hintText: "First Name",
hintStyle: TextStyle(color: Colors.grey[400], fontSize: 15)),
onChanged: (val) {
setState(() => firstName = val);
},
);
}
Use a unique TextEditingcontroller for each textfield...by this way the values won't overlap each others textfield value.
I create a profile page and i have 4 textformfield. I want to on tap icon activate textformfield and focus at the same time. Now I need tap twice on icon and first activated field, secondly focused.
How to solve it?
My code:
class UserProfile extends StatefulWidget {
#override
_UserProfileState createState() => _UserProfileState();
}
class _UserProfileState extends State<UserProfile> {
FocusNode myFocusNode;
bool isEnable = false;
#override
void initState() {
super.initState();
myFocusNode = FocusNode();
}
#override
void dispose() {
myFocusNode.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.fromLTRB(40.0, 50.0, 20.0, 0.0),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Flexible(
child: TextFormField(
enabled: isEnable,
focusNode: myFocusNode,
),
),
IconButton(
icon: Icon(Icons.edit),
onPressed: () {
setState(() {
isEnable = true;
FocusScope.of(context).requestFocus(myFocusNode);
});
})
],
),
You should use autofocus: isEnable instead.
just do like below in your ontap
setState(() {
if(isEnable)
Future.delayed(const Duration(milliseconds: 10), ()
{FocusScope.of(context).requestFocus(myFocusNode);
});
isEnable = true;
});
in first time isEnable is false so focusing not call and just enabling work and in other times get focus too.
you can't focus widget until disabled and when you enabling widget. when you do focusing and enabling at same time in ui tread it's try focusing before enabling because of their rendering time.if you post some delay to focusing the problem get solving.
Try using readOnly instead of enabled in TextFormField
I faced similar issue when I had multiple TextFields to enable kinda PIN input. And some of that had to be dynamically enabled and disabled plus prevent users from entering value in the next field while they haven't finished the previous one. I've tried a lot of approaches and focusing field after some delay was not a way to go because I wanted the keyboard to always be available while entering. So I've took a crazy path and solved this next way:
onTap: () => _focusNodes[_currentLetterIndex].requestFocus()
where _focusNodes are for each letter and _currentLetterIndex is calculated programmatically during input (when finished letter 0 -> current becomes 1 and so on). As the result when user tried to tap on next field - it was automatically refocused to the current one which behaves like the next field is disabled.
An example of the full text field looks like this (don't pay attention to decorations etc.)
TextField(
key: ValueKey(index),
controller: _editingControllers[index],
onTap: () => _focusNodes[_currentLetterIndex].requestFocus(),
focusNode: _focusNodes[index],
textAlign: TextAlign.center,
showCursor: false,
maxLength: 2,
enableInteractiveSelection: false,
autocorrect: false,
enableSuggestions: false,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
height: 1.2,
decoration: TextDecoration.none),
decoration: InputDecoration(
fillColor: !_wordCompleted &&
!correct &&
_editingControllers[index].text.isNotEmpty
? const Color(0xFFFFEEF0)
: Colors.white,
filled: !correct,
counterText: "",
border: defaultInputBorder,
focusedBorder: !correct &&
!_wordCompleted &&
_editingControllers[index].text.isNotEmpty
? incorrectInputBorder
: focusedInputBorder,
enabledBorder: _wordCompleted
? focusedInputBorder
: correct
? correctInputBorder
: defaultInputBorder,
errorBorder: defaultInputBorder,
disabledBorder: _wordCompleted
? focusedInputBorder
: correct
? correctInputBorder
: defaultInputBorder,
contentPadding: const EdgeInsets.only(left: 2)),
),
I have a TextField that serves as a search bar where the user can use the built in Android/iOS keyboard to type but also has the possibility to insert a special characters in the search bar from a button. In a way the typing and the other insertion is combined into one string
use case: The user types hell in the search bar then presses the button widget the search bar value becomes : hellö
I set up everything but when I click the button nothing happens (the typing from the keyboard works fine)
Here's my code:
//I have this as a global variable
TextEditingController _searchInputControllor = TextEditingController();
//This is the TextField
class _SearchBarState extends State<SearchBar> {
#override
Widget build(BuildContext context) {
return TextField(
enableInteractiveSelection: false,
controller: _searchInputControllor,
cursorColor: primaryDark,
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 15.0),
border: InputBorder.none,
hintText: "Search...",
suffixIcon: Material(
color: Colors.white,
elevation: 6.0,
borderRadius: BorderRadius.all(Radius.circular(6.0),),
child: InkWell(
splashColor: Colors.greenAccent,
onTap: () {},
child: Icon(Icons.search, color: primaryDark,),
),
),
),
);
}
}
//This is the button widget
//It is supposed to add to the search bar but nothing happens
class _SpecialCharState extends State<SpecialChar> {
#override
Widget build(BuildContext context) {
return ButtonTheme(
minWidth: 40.0,
child: FlatButton(
color: Colors.transparent,
textColor: Colors.black,
padding: EdgeInsets.all(8.0),
splashColor: Colors.blue,
onPressed: () {
setState(() {
_searchInputControllor.text = _searchInputControllor.text + widget.btnVal.toLowerCase();
});
},
child: Text(
widget.btnVal
),
)
);
}
}
A. No problem at all
I think your code is working well as I tried on my Android Phone Demo.
The text field is changed as I tap the buttons.
B. Change cursor position
Nonetheless, I add this code to make the cursor automatically placed on last character.
Rather than directly changed the text, we copy its value which contains selection.
Later we offset its selection by length of newText
void appendCharacters() {
String oldText = _searchInputControllor.text;
String newText = oldText + widget.btnVal.toLowerCase();
var newValue = _searchInputControllor.value.copyWith(
text: newText,
selection: TextSelection.collapsed(
offset: newText.length, //offset to Last Character
),
composing: TextRange.empty,
);
_searchInputControllor.value = newValue;
}
so we can trigger the method with code below :
Widget build(BuildContext context) {
return ButtonTheme(
minWidth: 40.0,
child: FlatButton(
onPressed: appendCharacters, // call a function
),
);
}
Working App Repository
You may look into this repo and build yourself. Github