Wrap text in a TextField flutter without creating a newline - flutter

Using the maxLines = null property, I can make the text wrap in a TextField. However, this also uses the Enter to create newlines.
I don't want that - I want to reserve the Enter key to onSubmitted() function. How do I do that?

You can achieve this with the onKey of either the TextField's FocusNode (WidgetOne) or a RawKeyboardListener (WidgetTwo):
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
void main() {
runApp(
MaterialApp(
title: 'Wrapping Single Line Text Fields',
home: _Page(),
),
);
}
class _Page extends HookWidget {
const _Page({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
final result = useState('');
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_WidgetOne(onSubmitted: (value) => result.value = value),
_WidgetTwo(onSubmitted: (value) => result.value = value),
Container(
margin: const EdgeInsets.all(15.0),
padding: const EdgeInsets.all(3.0),
decoration:
BoxDecoration(border: Border.all(color: Colors.black)),
child: Text('Result: ${result.value}')),
],
),
),
);
}
}
class _WidgetOne extends StatelessWidget {
final ValueChanged<String> onSubmitted;
const _WidgetOne({Key key, this.onSubmitted}) : super(key: key);
#override
Widget build(BuildContext context) {
final _controller = TextEditingController();
final _focusNode = FocusNode(onKey: (node, event) {
if (event.isKeyPressed(LogicalKeyboardKey.enter)) {
onSubmitted(_controller.text);
node.unfocus();
return true;
}
return false;
});
return TextField(
decoration: InputDecoration(hintText: 'With FocusNode onKey'),
controller: _controller,
focusNode: _focusNode,
maxLines: null,
);
}
}
class _WidgetTwo extends StatelessWidget {
final ValueChanged<String> onSubmitted;
const _WidgetTwo({Key key, this.onSubmitted}) : super(key: key);
#override
Widget build(BuildContext context) {
final _controller = TextEditingController();
final _focusNode = FocusNode(onKey: (node, event) {
if (event.isKeyPressed(LogicalKeyboardKey.enter)) {
onSubmitted(_controller.text);
node.unfocus();
return true;
}
return false;
});
return RawKeyboardListener(
focusNode: _focusNode,
onKey: (event) {
if (event.isKeyPressed(LogicalKeyboardKey.enter)) {
onSubmitted(_controller.text);
}
},
child: TextField(
decoration: InputDecoration(hintText: 'With RawKeyboardListener onKey'),
controller: _controller,
maxLines: null,
),
);
}
}

Related

i can't call setState() method in stf Widget

i try to save value in EMAIL variable with onChanged by used setState but the setState function not defined. i can't call it .
MyTextField is statefulWidget. what is problem i don't know .is problem related with Widget class (_buildAllTextFormField)? or what i'm beginner can anyone help me please?
import 'package:ecommernce_application/screens/signup.dart';
import 'package:flutter/material.dart';
import '../widgets/move_to_sign_or_login_screen.dart';
import '../widgets/sign_login_button.dart';
import '../widgets/text_field.dart';
class Login extends StatefulWidget {
const Login({Key? key}) : super(key: key);
#override
State<Login> createState() => _LoginState();
}
final _formKey = GlobalKey<FormState>();
bool obscureText = true;
// for validate Email
String pEmail = r'^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regExp = RegExp(pEmail);
void validate(){
final FormState? form = _formKey.currentState;
if(form!.validate())
{ debugPrint('Yes'); }
else { debugPrint('No'); }
}
String EMAIL='';
Widget _buildAllTextFormField(BuildContext context){
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.only(top: 100),
width: double.infinity,
height: 240,
alignment: Alignment.center,
child: const Text('Login',style: TextStyle(fontSize: 40,),),
),
const SizedBox(height: 10,),
Form(
key: _formKey,
autovalidateMode: AutovalidateMode.always,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 10,),
MyTextField(
onChanged:(v){
///////////////cant call setState The function 'setState' isn't defined
//setState((){});
},
controller: email,
validator: (String? value) {
if(value == null || value.trim().isEmpty) {
return 'Please enter your email address';
} // Check if the entered email has the right format
if (!regExp.hasMatch(value)) {
return 'Please enter a valid email address';
} // Return null if the entered email is valid
return null;
},
name: 'Email',),
const SizedBox(height: 10,),
MyTextField(
onChanged: (value){
},
controller: password,
name: 'Password',
validator: (value){
if (value == null || value.trim().isEmpty) {
return 'This field is required';
}
if (value.trim().length < 8) {
return 'Password must be at least 8 characters in length';
} // Return null if the entered password is valid
return null;
}),
const SizedBox(height: 10,),
_buildBottomPart(context),
],
),
),
],
);
}
Widget _buildBottomPart(BuildContext context){
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SignLoginButton(
name: 'Login',
onPressed:() { validate(); } ,
color: Colors.grey,
),
const SizedBox( height: 10, ),
Padding(
padding: const EdgeInsets.only( left:8.0 ),
child:MoveToScreen(
text1:'I Have Noy Account',
text2:'SignUp',
onTap: () {
Navigator.of(context).pushReplacement ( MaterialPageRoute(builder: (context) => const SignUP() ) );
},
),
),
],
);
}
final TextEditingController email = TextEditingController();
final TextEditingController password = TextEditingController();
class _LoginState extends State<Login> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: SingleChildScrollView(
child:TextField(
onChanged: (v){
setState(() {
});
},
),
// _buildAllTextFormField(context),
),
),
);
}
MyTextField
import 'package:flutter/material.dart';
class MyTextField extends StatefulWidget {
final TextEditingController controller;
final String name;
final ValueChanged<String> onChanged;
final FormFieldValidator<String> validator;
const MyTextField({Key? key ,required this.onChanged,required this.controller, required this.name, required this.validator,}) : super(key: key);
#override
State<MyTextField> createState() => _MyTextFieldState();
}
class _MyTextFieldState extends State<MyTextField> {
#override
Widget build(BuildContext context) {
return SizedBox(
width: 400,
height: 50,
child: TextFormField(
controller: widget.controller,
validator: widget.validator,
onChanged: widget.onChanged,
decoration: InputDecoration(
labelText: widget.name,
border: const OutlineInputBorder(),
),
),
);
}
}
PasswordTextField
import 'package:flutter/material.dart';
class PasswordTextField extends StatefulWidget {
final TextEditingController controller;
final String name;
final FormFieldValidator<String> validator;
//final ValueChanged<String> onChange;
const PasswordTextField({Key? key, required this.controller, required this.name, required this.validator}) : super(key: key);
#override
State<PasswordTextField> createState() => _PasswordTextFieldState();
}
class _PasswordTextFieldState extends State<PasswordTextField> {
bool _obscureText = true ;
#override
Widget build(BuildContext context) {
return SizedBox(
width: 400,
height: 50,
child: TextFormField(
obscureText: _obscureText,
validator: widget.validator,
//onChanged: widget.onChange,
controller: widget.controller,
decoration: InputDecoration(
suffixIcon: GestureDetector(
onTap: (){
setState(() {
_obscureText =!_obscureText;
});
FocusScope.of(context).unfocus();
},
child: Icon( _obscureText == true ? Icons.visibility : Icons.visibility_off,color: Colors.black,),),
labelText: widget.name,
border: const OutlineInputBorder(),
),
),
);
}
}
You can do it in this way:
Widget _buildAllTextFormField(BuildContext context, Function() changeCallback){...}
then replace:
_buildAllTextFormField(context)
with
_buildAllTextFormField(context, () => setState((){}))
and then replace:
onChanged:(v){
///////////////cant call setState The function 'setState' isn't defined
//setState((){});
},
with:
onChanged:(v){
changeCallback.call();
},

How to extract TextFormField as reusable widget in flutter

I want to make my flutter project highly manageable, apply clean code and maintain DRY concept strictly. There are a lot of input elements in any flutter project. So I want to make this element as a separate widget so that if I want to change in future then I will change in one place. Here is my approach:
import 'package:flutter/material.dart';
import '../utility/validatation.dart';
class RegistrationPage extends StatefulWidget {
static const String routeName = '/registrationPage';
#override
State<RegistrationPage> createState() => _RegistrationPageState();
}
class _RegistrationPageState extends State<RegistrationPage> {
final _formKey = GlobalKey<FormState>();
final TextEditingController nameInput = TextEditingController();
final TextEditingController businessName = TextEditingController();
final TextEditingController productTypeId = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: new Form(
key: _formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
Container(
height: 70,
margin: EdgeInsets.only(bottom: 50),
child: Image(image: AssetImage('assets/logo.png')),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 30.0),
child: TextInput(inputController: nameInput, label: 'আপনার নাম'),
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_register(context);
}
},
child: Text('Next'),
)
]),
),
);
}
void _register(BuildContext context) {}
}
class TextInput extends StatelessWidget {
const TextInput({
Key? key,
required this.inputController,
required this.label,
}) : super(key: key);
final TextEditingController inputController;
final String label;
#override
Widget build(BuildContext context) {
return TextFormField(
controller: inputController,
keyboardType: TextInputType.text,
decoration: const InputDecoration(
border: UnderlineInputBorder(),
prefixIcon: Icon(Icons.phone),
labelText: label,
),
validator: (value) {
return Validation.required(value);
},
);
}
}
But I got this error:
What is wrong in my code? Is there any problem in my approach or should I stop thinking to refactor my code as I do? Please also suggest if there is any smarter way to make code more clean and manageable.
Oh I see so you have this
class TextInput extends StatelessWidget {
const TextInput({
Key? key,
required this.inputController,
required this.label,
}) : super(key: key);
final TextEditingController inputController;
final String label;
#override
Widget build(BuildContext context) {
return TextFormField(
controller: inputController,
keyboardType: TextInputType.text,
// Notice the const here right?
// So the idea is that decoration objects could rebuild to either change one thing or the other, so 'label' here cannot be a constant
//So to solve this InputDecoration should not have const.
decoration: const InputDecoration(
border: UnderlineInputBorder(),
prefixIcon: Icon(Icons.phone),
labelText: label,
),
validator: (value) {
return Validation.required(value);
},
);
}
}
Since you are using a variable in InputDecoration, you should not declare InputDecoration with const keyword.

How do I extract this switch widget

I have a StatefulWidget with a ListView, the ListView has the bunch of switches with text next to them.
Now i want to extract this into a custom switch widget because i have this more than once.
I don't know how to do this, also I need to know inside my parent widget what state each switch has.
Padding(
padding: const EdgeInsets.only(left: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Use custom dhcp server"),
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Switch(
value: _dhcp,
activeColor: Colors.blue,
onChanged: (bool value) {
setState(() {
_dhcp = value;
});
},
),
),
],
),
),
You can create your own stateless widget like this:
class CustomSwitch extends StatelessWidget {
const CustomSwitch({
Key key,
#required this.value,
#required this.onChanged,
}) : super(key: key);
final bool value;
final void Function(bool) onChanged;
#override
Widget build(BuildContext context) {
return Switch(
value: value,
activeColor: Colors.blue,
onChanged: onChanged,
);
}
}
Where you can use it anywhere like this:
class ParentWidget extends StatefulWidget {
#override
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
bool switchValue = false;
#override
Widget build(BuildContext context) {
return ListView(
children: [
CustomSwitch(
value: switchValue,
onChanged: (newValue) {
setState(() {
switchValue = newValue;
});
},
),
],
);
}
}

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