Flutter TextEditingController retain value without hitting done button - flutter

Simple Add Place widget
Title Text Field
Container - Render image from camera
Button - Activates camera device
I thought having a controller connected to TextField would automatically save the state of the input value. However, from my example, if I input the text without click "done" and immediately click on "Take Picture" button. The TextField input value is cleared after coming back from camera operation.
How to Reproduce Problem:
Input text into the field
Immediately click on the Camera button without click done / check or hit enter on the keyboard
Take a picture confirm.
Come back to page the TextField is empty
Example Code:
AddPlacePage StatefulWidget
Column(
children: <Widget>[
TextField(
decoration: InputDecoration(labelText: 'Title'),
controller: _titleController,
),
ImageInput(),
],
),
ImageInput StatefulWidget
class _ImageInputState extends State<ImageInput> {
File _storedImage;
Future<void> _takePicture() async {
final imageFile = await ImagePicker.pickImage(
source: ImageSource.camera,
maxWidth: 600,
);
setState(() {
_storedImage = imageFile;
});
...
}
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Container(
...
child: _storedImage != null
? Image.file(
_storedImage,
fit: BoxFit.cover,
width: double.infinity,
)
: Text(
'No Image Taken',
textAlign: TextAlign.center,
),
alignment: Alignment.center,
),
Expanded(
child: FlatButton.icon(
icon: Icon(Icons.camera),
label: Text('Take Picture'),
textColor: Theme.of(context).primaryColor,
onPressed: () => _takePicture(),
),
),
],
);
}
}
Question:
How can I modify TextField's controller to retain input value even after exiting the application to access device camera?
TextField(
decoration: InputDecoration(labelText: 'Title'),
controller: _titleController,
),
I did try to create a local variable and try to use onChange:
String _inputValue
build(BuildContext context){
...
TextField(
decoration: InputDecoration(labelText: 'Title'),
controller: _titleController,
onChange: (value) => _inputValue = value;
),
However the effect is the same once returning from the camera as Flutter re-reders the page, both _inputValue and _titleController.text is cleared.
Example code:
https://github.com/erich5168/flutter_camera_example

You can use share preferences to save the String to device then call it when you back to text field. This is how I implement:
class LocalStorage {
static SharedPreferences instance;
static Future init() async {
instance = await SharedPreferences.getInstance();
}
static dynamic getValue(String key, dynamic emptyValue) {
if (LocalStorage.instance.containsKey(key)) {
return LocalStorage.instance.get(key);
}
return emptyValue;
}
}
set it to text field:
TextEditingController _usernameController =
new TextEditingController(text: LocalStorage.getValue('UserName', ''));

Related

How to implement search page via TextField in Flutter?

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/

Can a function accept two values in flutter/dart language? if yes than how?

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.

Show counter to number of elements hidden when overflow occurs in flutter row widget

Can anyone please help to implement this feature of Gmail that shows the counter to number of emails hidden when the email list becomes large ? I want to implement this in row widget where instead of being scrollable extra elements count is shown when overflow occurs.Gmail shows +15 counter for hidden emails
I was Curious to give a try to achieve the same effect, as asked.
Just in case, If anyone want a start for writing a custom one, then below code may help.
Here is my Code, Feel free to give any suggestions,
(For Now delete button in chips is not working bcoz of some logic problem, I will make it work another day)
import 'package:flutter/material.dart';
class Demo3 extends StatefulWidget {
#override
_Demo3State createState() => _Demo3State();
}
class _Demo3State extends State<Demo3> {
String temp = "";
bool showChips = false;
List<Widget> chipsList = new List();
TextEditingController textEditingController = new TextEditingController();
final _focusNode = FocusNode();
int countChipsToDeleteLater = 0;
#override
void initState() {
super.initState();
_focusNode.addListener(() {
print("Has focus: ${_focusNode.hasFocus}");
if (!_focusNode.hasFocus) {
showChips = false;
setState(() {});
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(new FocusNode());
},
child: new Container(
height: 500,
child: new Center(
child: Container(
width: 300,
child: !showChips
? Row(
children: [
buildTextField(),
showNumberWidgetIfAny(),
],
)
: Center(
child: Wrap(
children: [
Wrap(
children: buildChips(),
),
buildTextField(),
],
),
),
),
),
),
),
);
}
buildChips() {
return chipsList;
}
buildTextField() {
return Container(
width: 200,
child: new TextField(
showCursor: true,
focusNode: _focusNode,
autofocus: true,
cursorColor: Colors.black,
style: new TextStyle(fontSize: 22.0, color: Colors.black),
controller: textEditingController,
// decoration: InputDecoration.collapsed(
// hintText: "",
// ),
onChanged: (value) {
if (value.contains(" ")) {
checkWhatToStoreInChips(value, countChipsToDeleteLater);
textEditingController.clear();
setState(() {
showChips = true;
});
countChipsToDeleteLater++;
}
},
),
);
}
checkWhatToStoreInChips(String val, int chipsIndex) {
temp = "";
for (int i = 0; i < val.length; i++) {
if (val[i] == " ") {
break;
}
temp = temp + val[i];
}
addToChips(temp, chipsIndex);
}
addToChips(String tmp, int chipsIndex) {
chipsList.add(Chip(
// onDeleted: () {
// if (chipsList.length == 0) {
// countChipsToDeleteLater = 0;
// }
// chipsList.removeAt(chipsIndex);
// print(chipsList.length);
// print(chipsIndex);
// setState(() {});
// },
avatar: CircleAvatar(
backgroundColor: Colors.grey.shade800,
child: Text(tmp[0]),
),
label: Text(temp),
));
}
showNumberWidgetIfAny() {
int len = chipsList.length;
if (len >= 1) {
return GestureDetector(
onTap: () {
showChips = true;
setState(() {});
},
child: new Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.blue,
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: new Text(
"${chipsList.length.toString()} ",
style: new TextStyle(color: Colors.white, fontSize: 22),
),
),
),
);
}
return Container();
}
}
How it works:
Write something in text field, then press space, showChips boolean will become true
onChanged will detect the space and will send the string to a function.
That function will extract the string before space and then will add the string to a chip,
Finally the chip will be added to a chipslist.
We will have a boolean variable to check if the textfield is in focus and when to show the textfield and numberwidget (a widget which will keep count of the total chips, same like you asked in your question) or when to show the chipslist and textfield wraped in a wrap widget.
You can play around by changing the decoration of textfield to collapsed, to it look like the same as gmail.
Check this package, if you want to use custom package for ease.
I was facing a similar issue. I found a way to implement the Overflow count text.
Sample image
You basically have to paint the overflow text, and get its width like below
final TextPainter textPainter = TextPainter(
text: TextSpan(text: text, style: style),
textDirection: TextDirection.ltr,
textScaleFactor: WidgetsBinding.instance.window.textScaleFactor,
)..layout();
var textSize = textPainter.size;
textSize.width;
Then subtract that from the width available. Lets call it x.
Then create a sum of width for each row item(using TextPainter.layout() method mentioned above), till its value is less than x.
This way you'll know how many items can be shown in the row.
I have created a Flutter library to help with this.

Flutter using EditableText

I'm trying to figure out how to use the TextEditor in Flutter.
I have "Card Editor" (basically I want to be able to work on the equivalent of a paragraph of text)
new EditableText(
autofocus: true,
maxLines: null,
backgroundCursorColor: Colors.amber,
cursorColor: Colors.green,
style: TextStyle(),
focusNode: FocusNode(),
controller: controller,
onSubmitted: (val) {
_addCard(val);
Navigator.pop(context);
},
)
I adapted this from an example of a TextField.
But I have a couple of questions.
Firstly, it doesn't seem to show anything when I type. The cursor moves, but no text is visible. Is that the default when there's no explicit style?
Secondly, how do I trigger the submit? With a TextField, the CR / Enter button does this. Obviously I see why you don't necessarily want that with EditableText But what should I do instead?
Third, I need to be able to put default text into this widget. I tried adding a "value" attribute to the EditableText, but that doesn't seem to be right. What's the way to do this?
from TextField class - material library - Dart API :
EditableText, which is the raw text editing control at the heart of a TextField. The EditableText widget is rarely used directly unless you are implementing an entirely different design language, such as Cupertino.
here an example of TextField , from my app flutter listview CRUD app using nonsecure rest api
class _TaskEditPageWidgetState extends State<TaskEditPageWidget> {
TextEditingController _noteController;
#override
void initState() {
super.initState();
_noteController = TextEditingController.fromValue(
TextEditingValue(
text: widget.taskOpj.note,
),
);
}
#override
void dispose() {
_noteController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: _appBar(),
body: _body(),
);
}
Widget _appBar() {
return AppBar(
title: new Text("Edit Task"),
actions: <Widget>[
new IconButton(
icon: new Icon(Icons.save),
onPressed: _save,
),
],
);
}
Widget _body() {
return SingleChildScrollView(
child: Column(
children: <Widget>[
Text("Note:"),
TextField(
decoration: InputDecoration(border: InputBorder.none),
autofocus: true,
keyboardType: TextInputType.multiline,
maxLines: null,
controller: _noteController),
],
),
);
}
Future _save() async {
widget.taskOpj.note = _noteController.text;
await Tasks.updateTask(widget.taskOpj);
widget.notifyParent();
Navigator.pop(context);
}
}
Just add this line:
style: TextStyle(
decorationThickness: 0.001,
),

Insert into TextField using buttons from another widget - Flutter

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