Clear TextField inside ListView Flutter - flutter

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),
)
),
)
],
),
),
);
}
}

Related

How to continuously get whether the TextField's text is empty in Flutter?

I have a TextField. I want its text not to be empty. (so I want to know if the text is empty)
I have tried using the following code, but it doesn't work:
controller.text.trim().isEmpty()
My code:
TextFormField(
controller: controller,
),
controller.text.trim().isEmpty()
How to continuously get whether the TextField's text is empty in Flutter? I would appreciate any help. Thank you in advance!
full example:
code:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController _controller = TextEditingController();
String _text = '';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter Demo Home Page'),
),
body: Container(
padding: const EdgeInsets.all(16),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_text),
const SizedBox(height: 20),
TextField(
controller: _controller,
onChanged: (value) {
setState(() {
_text = value;
});
},
decoration: const InputDecoration(
hintText: 'Enter text',
),
),
// submit
ElevatedButton(
onPressed: _text.isEmpty
? null
: () {
setState(() {
_text = _controller.text;
});
},
child: const Text('Submit'),
),
],
),
),
),
);
}
}
It can be done without any temporary variable using ValueListenableBuilder
After some research figured out
controller.text by itself is not listenable
TextEditingController extends ValueNotifier<TextEditingValue> i.e you can use ValueListenableBuilder from material package to listen to text changes.
Code:
class _MyWidgetState extends State<MyWidget> {
late TextEditingController textEditingController;
#override
void initState() {
textEditingController = TextEditingController();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
TextField(
controller: textEditingController,
),
ValueListenableBuilder<TextEditingValue>(
valueListenable: textEditingController,
builder: (context, value, child) {
return ElevatedButton(
onPressed: value.text.isNotEmpty ? () {} : null,
child: const Text('I am disabled when text is empty'),
);
},
),
],
),
),
);
}
}
Without text:
With text:
You can add listener to your TextEditingController and call setState to update the UI.
late TextEditingController controller = TextEditingController()..addListener(() {
setState((){}); // to update the ui
});
The place you will use controller.text.trim().isEmpty() will show the updated state.
Example
class Test extends StatefulWidget {
const Test({super.key});
#override
State<Test> createState() => _TestState();
}
class _TestState extends State<Test> {
late TextEditingController controller = TextEditingController()
..addListener(() {
setState(() {}); // to update the ui
});
#override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
controller: controller,
),
ElevatedButton(
onPressed: controller.text.trim().isEmpty ? null : () {},
child: Text("Button"))
],
);
}
}

Not able to use a List present in another widget

I am making a List App which a list of items followed by a checkBox .Then there is a floating action button with a plus sign .On click of it u get a bottomSheet .In the bottom sheet you can enter your task to the textField .On clicking on the add button the tasks gets added .
The ui Looks like this
I am not able to access tasks List that is defined in the task_screen.dart from add_taskScreen.dart . I am getting the below error despite importing the required files
error: Undefined name 'tasks'. (undefined_identifier at [todoey_flutter] lib\add_task_screen.dart:43)
here is my Code
Main.dart
import 'package:flutter/material.dart';
import 'tasks_screen.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: TaskScreen(),
);
}
}
Task_tile.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class TaskTile extends StatelessWidget {
#override
late final bool isChecked ;
late final String taskTitle;
final Function checkBoxCallBack;
TaskTile({required this.isChecked,required this.taskTitle,required this.checkBoxCallBack});
#override
Widget build(BuildContext context) {
return ListTile(
title: Text(taskTitle,
style:TextStyle(
decoration: isChecked ? TextDecoration.lineThrough:null
),
),
trailing: Checkbox(
value:isChecked,
activeColor:Colors.lightBlueAccent,
onChanged: (newValue) {//onChanged here if we select the check box the value becomes true else it will become false
checkBoxCallBack(newValue);//widget.toggleCheckBoxState(value);
},
)
);
}
}
Task_list.dart
import 'package:flutter/material.dart';
import 'task_title.dart';
import 'Models/task.dart';
class TaskList extends StatefulWidget {
final List<Task> tasks;
TaskList(this.tasks);
#override
State<TaskList> createState() => _TitleListState();
}
class _TitleListState extends State<TaskList> {
#override
Widget build(BuildContext context) {
return ListView.builder(itemBuilder: (context,index){//in the listView Builder index is already defined and gets updated by itself
return TaskTile(
taskTitle:widget.tasks[index].name,
isChecked: widget.tasks[index].isDone,
checkBoxCallBack:(bool checkBoxState){
setState((){
widget.tasks[index].toggleDone() ;
});
}
);
},
itemCount: widget.tasks.length,//max no tasks that can fit in the screen ie..how many ever tasks are there on 'tasks' it will build that many
);
}
}
task.dart
import 'package:flutter/material.dart';
class Task{
late final String name;
late bool isDone;
Task
({required this.name,this.isDone=false});//give a default value to isDone
void toggleDone()
{
isDone = !isDone;
}
}
task_Screen.dart
import 'package:flutter/material.dart';
import 'tasks_list.dart';
import 'add_task_screen.dart';
import 'package:todoey_flutter/Models/task.dart';
class TaskScreen extends StatefulWidget {
const TaskScreen({Key? key}) : super(key: key);
#override
State<TaskScreen> createState() => _TaskScreenState();
}
class _TaskScreenState extends State<TaskScreen> {
List<Task> tasks = [
Task(name: 'Buy milk'),
Task(name: 'Buy eggs'),
Task(name: 'Buy bread'),
];
#override
Widget build(BuildContext context) {
return Scaffold(//Scaffold contains everything
backgroundColor: Colors.lightBlueAccent,
body:Column(
children: [
Container(
padding:const EdgeInsets.only(top:60,left:30,right:30,bottom: 30),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children:const <Widget>[
CircleAvatar(
child: Icon(
Icons.list,
color: Colors.lightBlueAccent,
size:30.0,
),
backgroundColor:Colors.white,
radius:30,
),
SizedBox(height:10),
Text(
'Todoey',
style:TextStyle(
color:Colors.white,
fontSize: 50,
fontWeight: FontWeight.w700,
)
),
Text('12 Tasks',
style:TextStyle(
color:Colors.white,
fontSize: 18,
)
),
]
),
),
Expanded(
child: Container(//designing the white part of the todoey
padding:const EdgeInsets.symmetric(horizontal: 20),
height:300,
decoration: const BoxDecoration(
color:Colors.white,
borderRadius: BorderRadius.only(
topLeft:Radius.circular(20.0),
topRight:Radius.circular(20.0),
),
),
child: TaskList(tasks),
),
),
],
),
floatingActionButton: FloatingActionButton(
backgroundColor:Colors.lightBlueAccent,
child: const Icon(
Icons.add,
),
onPressed: (){ //call the bottomSheet in builder
showModalBottomSheet(builder:(context)=>AddTaskScreen(
(newTaskTitle)
{
setState(){
tasks.add(Task(name:newTaskTitle));
}
}), context: context);//to create bottom drawer widget on pressing the button a new pop up appears at the bottom
},
),
);
}
}
add_Task_Screen.dart
import 'package:flutter/material.dart';
import 'task_title.dart';
import 'tasks_list.dart';
import 'tasks_screen.dart';
import 'package:todoey_flutter/Models/task.dart';
class AddTaskScreen extends StatelessWidget {
late String newTaskTitle;
final Function addTaskCallBack;
AddTaskScreen( this.addTaskCallBack, {Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(20.0),
child: Container(
//color:const Color(0xff757575),//you cant add color 2 times it will throw an exception
decoration:const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(topLeft:Radius.circular(20.0) ,topRight:Radius.circular(20.0)),
),
child:Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children:[
const Center(
child: Text('Add Task',
style: TextStyle(
fontSize:30.0,
color:Colors.lightBlueAccent,
),
),
),
TextField(
autofocus: true,
autocorrect: true,
textAlign: TextAlign.center,
onChanged: (newText){
newTaskTitle = newText;
},
),
FlatButton(
onPressed: (){
print(newTaskTitle);
tasks.add(name:newTaskTitle);//here is the error
},
child: const Text('Add'),
color: Colors.lightBlueAccent,
)
]
)
),
);
}
}
In task_screen.dart create a function like this
void _handleAddTask(String _name)=>setState(()=>tasks.add(Task(name: _name))));
Then modify showModalBottomSheet to look like this
showModalBottomSheet(builder:(context)=>AddTaskScreen(_handleAddTask,"Add task"), context: context);},),);}}
In add_Task_Screen.dart, modify
AddTaskScreen( this.addTaskCallBack, {Key? key}) : super(key: key); to this
AddTaskScreen( this.addTaskCallBack, this.newTaskTitle, {Key? key}) : super(key: key);
create a TextEditingController for your TextField and pass it to it like this
TextField(
autofocus: true,
controller: _textFieldController,
autocorrect: true,
textAlign: TextAlign.center,
onChanged: (newText){
newTaskTitle = newText;
},
),
Finally, in the onPress of Add button, modify it to something like this:
FlatButton(
onPressed: (){
print(newTaskTitle);
addTaskCallBack(_textFieldController.text);
},
child: const Text('Add'),
color: Colors.lightBlueAccent,
)
The tasks list does not exist in the child widget. You can access the widget via the callback method you provided.
Refactor your FlatButton to look like this:
FlatButton(
onPressed: () {
addTaskCallBack(newTaskTitle);
},
child: const Text('Add'),
color: Colors.lightBlueAccent,
)

How can I get the input of my textfield inside my custom dialog widget?

I am working on a custom dialog called "Alertbox" where the user inserts a name into a textfield and after he pushes the button a function called "addTeam" created a team out of the string.
This is how I created my dialog "Alertbox":
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:trainings_app/config/palette.dart';
class Alertbox extends StatelessWidget {
final Function addTeamFunction;
const Alertbox(this.addTeamFunction);
#override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: Colors.transparent,
elevation: 0,
insetPadding: EdgeInsets.all(10),
child: Center(
child: Container(
decoration: new BoxDecoration(
borderRadius: new BorderRadius.all(const Radius.circular(20)),
color: Colors.white,
),
width: 350,
height: 200,
child: Row(
children: [
SizedBox(width: 12),
Expanded(
child: TextField(
textAlign: TextAlign.center,
autofocus: true,
),
),
SizedBox(width: 12),
ElevatedButton(
onPressed: () => addTeamFunction(),
child: const Text('✓'),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Palette.orange),
),
),
SizedBox(width: 8),
],
),
),
),
);
}
}
And here I am using it:
void newTeam() {
showDialog<AlertDialog>(
context: context,
builder: (BuildContext context) {
return Alertbox(() {
Navigator.of(context).pop();
});
},
);
}
void addTeam(String name) {
setState(() {
teams.add(name);
});
Navigator.of(context).pop();
sharedPreferences.setStringList('teams', teams);
}
But I can't find a way to parse the input from the textfield into the function "addTeam" where it is needed. Can anyone help me please?
You Should try below code hope its helps you:
import 'package:flutter/material.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(
title: 'Testing',
home: MyCustomForm(),
);
}
}
class MyCustomForm extends StatefulWidget {
const MyCustomForm({Key? key}) : super(key: key);
#override
_MyCustomFormState createState() => _MyCustomFormState();
}
class _MyCustomFormState extends State<MyCustomForm> {
final myController = TextEditingController();
#override
void dispose() {
myController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Retrieve Text Input'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: TextField(
controller: myController,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text(myController.text),
);
},
);
},
tooltip: 'Show the value!',
child: const Icon(Icons.add),
),
);
}
}
Your Screen like ->
Use a TextFormField instead of a TexiField widget contained in a Form widget that has a GlobalKey, which will be useful to you during validation!
How to get the value which is already entered on the keyboard?
Uses a TextEditingController or the onSaved method of the TextFormField.

Disable button while typing on Input Flutter

I want disable a button while I'm typing on input.
But the code below that I 've wrote doesn't work because the button is disabled only when I "confirm" input with keyboard, but I want disabled input while I'm typing on input.
TextEditingController myController = TextEditingController();
bool isValid = false;
#override
Widget build(BuildContext context) {
Column(
children: <Widget>[
TextField(
controller: myController,
onChanged: (value){
setState(() {
isValid = (value.isEmpty || double.tryParse(value) == null) ? false : true;
});
},
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Enter a search term'
),
),
RaisedButton(
disabledColor: Colors.grey,
child: Text("${AppLocalizations.of(context).translate("test")}"),
onPressed: isValid ? () { print("test") }:null,
),
],
)
}
You can Also use myController.addListener()
To check result just copy paste below code in DartPad
When you enter number in TextField the button will enable
SAMPLE CODE
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController myController = TextEditingController();
bool isValid = false;
#override
void dispose() {
// Clean up your controller when the Widget is disposed
myController.dispose();
super.dispose();
}
#override
void initState() {
// TODO: implement initState
super.initState();
myController.text = '';
myController.addListener((){
print("Get Value: ${myController.text}");
setState(() {
isValid = (myController.text.isEmpty || double.tryParse(myController.text) == null)
? false
: true;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
children: <Widget>[
TextField(
controller: myController,
onChanged: (value) {
setState(() {
});
},
decoration: InputDecoration(
border: InputBorder.none, hintText: 'Enter a search term'),
),
RaisedButton(
disabledColor: Colors.grey,
child: Text("Click Me"),
onPressed: isValid
? () {
print("test");
}
: null,
),
],
),
);
}
}
Use FocusNode
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DemoPage(),
debugShowCheckedModeBanner: false,
theme: ThemeData(primaryColor: Colors.white),
);
}
}
class DemoPage extends StatefulWidget {
#override
_DemoPageState createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
TextEditingController textField1Ctrl;
TextEditingController textField2Ctrl;
FocusNode focusNode1;
FocusNode focusNode2;
#override
void initState() {
textField1Ctrl = TextEditingController();
textField2Ctrl = TextEditingController();
focusNode1 = FocusNode()..addListener(_rebuildOnFocusChange);
focusNode2 = FocusNode()..addListener(_rebuildOnFocusChange);
super.initState();
}
void _rebuildOnFocusChange() => setState(() {});
void _onButton1Pressed() {}
void _onButton2Pressed() {}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("Disable Button When Text Field has focus"),
Row(
children: <Widget>[
Expanded(
child: TextField(
controller: textField1Ctrl,
focusNode: focusNode1,
),
),
RaisedButton(
child: Text("Button 1"),
onPressed: focusNode1.hasFocus ? null : _onButton1Pressed,
)
],
),
const SizedBox(height: 40.0),
Text("Disable Button When TextField is Empty or has focus"),
Row(
children: <Widget>[
Expanded(
child: TextField(
controller: textField2Ctrl,
focusNode: focusNode2,
),
),
RaisedButton(
child: Text("Button 2"),
onPressed: focusNode2.hasFocus || textField2Ctrl.text.isEmpty
? null
: _onButton2Pressed,
)
],
),
],
),
),
);
}
}
Demo: DartPad

break a form into multiple widget and interact with those widget in flutter

i have a form which i decided to break into multiple widget for code re- usability. the problem i am having i dont know how to interact with each components. for example, if the main form declare a variable, how do i access that variable in the custom textfield widget which is store in a different dart file.
below is the code i have
form dart file (main.dart)
import 'package:flutter/material.dart';
import 'package:finsec/widget/row_text_input.dart';
import 'package:finsec/widget/text_form_field.dart';
import 'package:finsec/widget/save_button.dart';
import 'package:finsec/utils/strings.dart';
import 'package:finsec/utils/dimens.dart';
import 'package:finsec/utils/colors.dart';
import 'package:finsec/widget/column_text_input.dart';
void main() {
runApp(MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Simple Interest Calculator App',
home: ThirdFragment(),
theme: ThemeData(
brightness: Brightness.dark,
primaryColor: Colors.indigo,
accentColor: Colors.indigoAccent),
));
}
class ThirdFragment extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _ThirdFragmentState();
}
}
class _ThirdFragmentState extends State<ThirdFragment> {
var _formKey = GlobalKey<FormState>();
var _currentItemSelected = '';
bool isError = false;
bool isButtonPressed = false;
#override
void initState() {
super.initState();
}
TextEditingController amountController = TextEditingController();
TextEditingController frequencyController = TextEditingController();
#override
Widget build(BuildContext context) {
TextStyle textStyle = Theme.of(context).textTheme.title;
return Scaffold(
appBar: AppBar(
title: Text('Simple Interest Calculator'),
),
body: Form(
key: _formKey,
child: SingleChildScrollView(
child: Column (children: [
Padding(
padding: EdgeInsets.only(top: 10.0, bottom: 5.0, left: 15.0, right: 15.0),
child: CustomTextField(textInputType:TextInputType.number,
textController: amountController,
errorMessage:'Enter Income Amount',
labelText:'Income Amount for testing'),
),
RowTextInput(inputName: 'Frequency:',
textInputType: TextInputType.number,
textController: frequencyController,
errorMessage: 'Choose Income Frequency',
labelText: 'Income Amount for testing'
),
RowTextInput(inputName: 'Date Paid:',
textInputType: TextInputType.number,
textController: datePaidController,
errorMessage: 'Pick Income Payment Date',
labelText: 'Income Amount for testing'
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
MaterialButton(
height: margin_40dp,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(margin_5dp)),
minWidth: (MediaQuery.of(context).size.width * .9) / 2,
color: Theme.of(context).primaryColor,
textColor: white,
child: new Text(save),
onPressed: () => {
setState(() {
if (_formKey.currentState.validate()) {
// amountController.text.isEmpty ? amountController.text='Value require' : amountController.text='';
//this.displayResult = _calculateTotalReturns();
}
})
},
splashColor: blueGrey,
),
MaterialButton(
height: margin_40dp,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(margin_5dp)),
minWidth: (MediaQuery.of(context).size.width * .9) / 2,
color: Theme.of(context).primaryColor,
textColor: white,
child: new Text(save_and_continue),
onPressed: () => {},
splashColor: blueGrey,
)
])
]
),
),
}
RowTextInput is a different dart file that contains this code. RowTextInput.dart
import 'package:flutter/material.dart';
import 'package:finsec/utils/hex_color.dart';
class CustomTextField extends StatelessWidget {
CustomTextField({
this.textInputType,
this.textController ,
this.errorMessage,
this.labelText,
});
TextInputType textInputType;
TextEditingController textController;
String errorMessage, labelText;
#override
Widget build(BuildContext context) {
bool isError = false;
return Container(
child: TextFormField(
keyboardType: textInputType,
style: Theme
.of(context)
.textTheme
.title,
controller: textController,
validator: (String value) {
if (value.isEmpty) {
return errorMessage;
}
},
decoration: InputDecoration(
labelStyle: TextStyle(
color: Colors.grey,
fontSize: 16.0
),
contentPadding: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0), //size of textfield
errorStyle: TextStyle(
color: Colors.red,
fontSize: 15.0
),
border: OutlineInputBorder(
borderSide: BorderSide(width:5.0),
borderRadius: BorderRadius.circular(5.0)
)
)
),
);
}
}
i want to access isError and isButtonPressed variables located in main.dart from RowTextInput.dart and be able to assign values. main.dart should then be able to see those values assign in RowTextInput.dart file.
also,i want to move the MaterialButton button in its own widget file (button.dart) but then i dont know how this dart file will interact with the main.dart file when button is click or to check values of isError and IS button pressed. basically, i am breaking the form into different components (textfield and button) and store them in their own separate file. but i want all the files main.dart, rowintputtext, button.dart(new) to be able to see values of variables in main.dart and change the values. is this possible? is there an easier way?
thanks in advance
If you think about it. In Flutter the Button and RawMaterialButton are already in other files. And the manage to do exactly what you want.
You should create a File mycustomButtons.dart.
In the file you should create a class that will build your Buttons...
But it must has two parameters in it's constructor actionSave actionSaveAndContinue.
You will then create two functions in your main something like:
void _save() {
setState(() {
if (_formKey.currentState.validate()) {
// amountController.text.isEmpty ? amountController.text='Value require' : amountController.text='';
//this.displayResult = _calculateTotalReturns();
}
})
}
Then you should pass your created functions as parameters:
MyCustomButtons(actionSave: _save, actionSaveAndContinue: _saveAndContinue)
So the button will have all needed information to update your main.dart variables.
The textField is pretty much the same. But you will need pass a validation function and a TextEditingController.
You can see the font of RawnMaterialButton, TextFormField to see how they receive (and pass) data from one class to an other.
I was also looking for breaking a form into multiple classes. This is that I did :
Form
Pass the onSaved function at the form level.
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_CustomFormField(
onSaved: (value) => _myModelForm.field1 = value),
),
_CustomFormField2(
onSaved: (value) => _myModelForm.field2 = value),
)
),
RaisedButton(
onPressed: () {
// Validate will return true if the form is valid, or false if
// the form is invalid.
if (_formKey.currentState.validate()) {
// Process data.
_formKey.currentState.save();
// Observe if your model form is updated
print(myModelForm.field1);
print(myModelForm.field2)
}
},
child: Text('Submit'),
),
],
),
);
}
_CustomFormField1
The onSaved function will be passed as argument. This class can be either in the same file than the form or in another dedicated file.
class _CustomFormField1 extends StatelessWidget {
final FormFieldSetter<String> onSaved;
//maybe other properties...
_CustomFormField1({
#required this.onSaved,
});
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: TextFormField(
// You can keep your validator here
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
onSaved: onSaved,
),
);
}
}
Like onSaved, you can do the same way for focusNode, onFieldSubmitted, validator if needed in
I hope it will help you and others
There's probably a more elegant way to do it but I am currently experimenting with Singletons. See the code below:
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'dart:async';
class AppModel {
TextEditingController nameController;
TextEditingController surnameController;
StreamController<String> fullnameStreamController;
AppModel() {
nameController = TextEditingController();
surnameController = TextEditingController();
fullnameStreamController = StreamController.broadcast();
}
update() {
String fullname;
if (nameController.text != null && surnameController.text != null) {
fullname = nameController.text + ' ' + surnameController.text;
} else {
fullname = 'Please enter both names';
}
fullnameStreamController.add(fullname);
}
}
GetIt getIt = new GetIt();
final appModel = getIt.get<AppModel>();
void main() {
getIt.registerSingleton<AppModel>(AppModel());
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(title: 'Singleton Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String text;
update() {
setState(() {
});
}
#override
void initState() {
text = 'waiting for input';
appModel.fullnameStreamController.stream.listen((data) {
text = data;
update();
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(color: Colors.amberAccent),
child: Column(
children: <Widget> [
Card(
color: Colors.white,
child: Text('Name'),
),
Card(
color: Colors.yellow,
child: NameTextField()
),
Divider(),
Card(
color: Colors.white,
child: Text('Surname'),
),
Card(
color: Colors.yellow,
child: SurnameTextField()
),
OkButton(),
Card(
color: Colors.white,
child: Text('Full name'),
),
Card(
color: Colors.orange,
child: FullnameText(text),
),
],
),
),
);
}
}
class NameTextField extends StatefulWidget {
NameTextField({Key key}) : super(key: key);
_NameTextFieldState createState() => _NameTextFieldState();
}
class _NameTextFieldState extends State<NameTextField> {
#override
Widget build(BuildContext context) {
return Container(
child: TextField(
controller: appModel.nameController,
),
);
}
}
class SurnameTextField extends StatefulWidget {
SurnameTextField({Key key}) : super(key: key);
_SurnameTextFieldState createState() => _SurnameTextFieldState();
}
class _SurnameTextFieldState extends State<SurnameTextField> {
#override
Widget build(BuildContext context) {
return Container(
child: TextField(
controller: appModel.surnameController,
),
);
}
}
class FullnameText extends StatefulWidget {
FullnameText(this.text,{Key key}) : super(key: key);
final String text;
_FullnameTextState createState() => _FullnameTextState();
}
class _FullnameTextState extends State<FullnameText> {
#override
Widget build(BuildContext context) {
return Container(
child: Text(widget.text),
);
}
}
class OkButton extends StatefulWidget {
OkButton({Key key}) : super(key: key);
_OkButtonState createState() => _OkButtonState();
}
class _OkButtonState extends State<OkButton> {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white10,
child: RaisedButton(
color: Colors.white,
child: Icon(Icons.check),
onPressed: () {appModel.update();},
),
);
}
}
Check how I use the three controllers in the update function of the AppModel class.
CustomTextFields must extends parent(widget where is form) in this case it is ThirdFragment
class CustomTextField extends ThirdFragment{
CustomTextField({
this.textInputType,
this.textController,
this.errorMessage,
this.labelText,
});