In my last question about StateNotifierProvider not updating state using HookWidget my issue as calculating two TextFormField has been solved, now instead of using HookWidget i try to implement that with ConsumerWidget. i migrated my last code to:
class CalculateTextFormField extends ConsumerWidget {
#override
Widget build(BuildContext context,ScopedReader watch) {
final cashCounterProvider = watch(cashProvider);
final TextEditingController _count = TextEditingController();
final TextEditingController _cash = TextEditingController();
return Scaffold(
body: Form(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('${cashCounterProvider.count + cashCounterProvider.cash}'),
TextFormField(
controller: _count,
keyboardType: TextInputType.number,
onChanged: (value) =>
context.read(cashProvider.notifier).setCount(int.tryParse(value) ?? 0),
),
TextFormField(
controller: _cash,
keyboardType: TextInputType.number,
onChanged: (value) =>
context.read(cashProvider.notifier).setCash(int.tryParse(value) ?? 0),
)
],
),
),
);
}
}
final cashProvider = StateNotifierProvider<CashCounter, CashCounterData>((ref) => CashCounter());
class CashCounter extends StateNotifier<CashCounterData> {
CashCounter() : super(_initialData);
static const _initialData = CashCounterData(0, 0);
void setCount(int value){
state = CashCounterData(value, state.cash);
}
void setCash(value){
state = CashCounterData(state.count, value);
}
int get count => state.count;
int get cash => state.cash;
}
class CashCounterData {
final int count;
final int cash;
const CashCounterData(this.count, this.cash);
}
here i defined cashCounterProvider and when i try to enter value into TextFormField i can't enter multiple value and this line as
Text('${cashCounterProvider.count + cashCounterProvider.cash}'),
does work once. i try to implemeting a simple calculator on multiple entering value into inputs
Controllers in Flutter need to be declared outside the build method unless using hooks. Whenever you type into one of your TextFields, the widget is being rebuilt, which causes your controller to be reassigned.
If using hooks, the following is fine:
class CalculatableTextFormField extends HookWidget {
#override
Widget build(BuildContext context) {
final cashCounterProvider = useProvider(cashProvider);
final TextEditingController _count = useTextEditingController();
final TextEditingController _cash = useTextEditingController();
...
Otherwise, declare controllers outside the build method:
class CalculatableTextFormField extends ConsumerWidget {
final TextEditingController _count = TextEditingController();
final TextEditingController _cash = TextEditingController();
#override
Widget build(BuildContext context, ScopedReader watch) {
final cashCounterProvider = watch(cashProvider);
...
One of my favorite benefits of hooks is not having to handle disposal logic on your own. Here is an example of handling the lifecycle of a TextEditingController. After reading that, take a look at the implementation for useTextEditingController. That should help make sense of how things are working.
Related
I created a widget for a textfield that accepts password and I made use of stateful widget. Now I want to get the value of what is written in the text field in two different files but I can't make the texteditingcontroller requiredenter image description here
Login Page should be something like this:
declare the controller in the login page then you can pass the controller to other Widget including Passwordfield, the Login page now is the owner of the controller it initialize it and dispose it.
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
late TextEditingController _passwordController;
#override
void initState() {
_passwordController = TextEditingController();
}
#override
void dispose() {
_passwordController.dispose();
}
Widget build(BuildContext context) {
return Column(
children: <Widget>[
// Emailfield(),
Passwordfield(
controller: _passwordController,
),
],
);
}
}
in the Passwordfield edit the constructor to use the controller in this Widget:
class Passwordfield extends StatefulWidget {
final TextEditingController controller;
Passwordfield({Key? key, required this.controller,}) : super(key: key);
#override
_PasswordfieldState createState() => _PasswordfieldState();
}
class _PasswordfieldState extends State<Passwordfield> {
ValueChanged<String> onChanged = (value) {};
String hintText = "password";
bool hidepassword = true;
Widget build(BuildContext context) {
return TextField(
controller: widget.controller,
onChanged: onChanged,
obscureText: hidepassword,
// ...
);
}
}
You can make it like this:
First you have to create controller :
var _controller = TextEditingController();
Second add this controller to your textfield
TextField(
autofocus: true,
controller: _controller, // add controller here
decoration: InputDecoration(
hintText: 'Test',
focusColor: Colors.white,
),
),
and finally in your button check if controller is empty or not
CustomButton(
onTap: () async {
if (_controller.text.trim().isEmpty) {
showCustomSnackBar(
'Password field is empty',
context);
}
}
)
just it
Consider the following code
class HomeScreen extends StatelessWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return GetBuilder<HomeController>(
init: HomeController(),
builder: (controller) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
phoneWidget(),
TextFormField(
controller: controller.formController,
),
ElevatedButton(onPressed: ()=> controller.clearForm(), child: const Text('Clear'))
],
),
),
);
});
}
Widget phoneWidget() {
final HomeController _controller = Get.find();
return Container(
decoration: BoxDecoration(
color: Colors.amber,
borderRadius: BorderRadius.circular(20)
),
child: IntlPhoneField( // the widget in question
controller:_controller.secondController ,
showCountryFlag: false,
iconPosition: IconPosition.trailing,
autoValidate: false,
initialValue: _controller.initialPhoneNumber,
initialCountryCode: _controller.initialCountryCode,
),
);
}
}
In the above i have a widget. which I'm setting initial values at init on controller.
class HomeController extends GetxController{
final _intialCountryCode = ''.obs;
String get initialCountryCode => _intialCountryCode.value;
final _initialPhoneNumber = ''.obs;
String get initialPhoneNumber => _initialPhoneNumber.value;
final formController = TextEditingController();
final secondController = TextEditingController();
#override
void onInit() {
super.onInit();
_intialCountryCode('IN');
_initialPhoneNumber('123457788');
formController.text = "TEST";
secondController.text = '1234944627';
}
clearForm(){
formController.clear();
secondController.clear();
secondController.text = '123349526';
}
}
I'm using Getx for my application. I'm assigning values for this widget on init. What I'm expecting is to the widget shows the initial value when the screen is loaded. How ever the changes are not reflected on the widget, instead if I hot-reload, the changes are updated on the widget. I have tried wrapping the widget with Obx. but the results are same. The changes are made only after hot reload. What causes this? Why does the widget only updates after hot reload? How can i resolve this properly
class HomeController extends GetxController{
final _intialCountryCode = ''.obs;
String get initialCountryCode => _intialCountryCode.value;
final _initialPhoneNumber = ''.obs;
String get initialPhoneNumber => _initialPhoneNumber.value;
final formController = TextEditingController().obs;
final secondController = TextEditingController().obs;
#override
void onInit() {
super.onInit();
_intialCountryCode('IN');
_initialPhoneNumber('123457788');
formController.text = "TEST";
secondController.text = '1234944627';
}
clearForm(){
formController.clear();
secondController.clear();
secondController.text = '1234944627';
}
}
GetX<HomeController>(
init: HomeController(),
builder: (controller) {
try adding this changes and see if it works
I am trying to use Riverpod state management. I have two TextFormField and I want to set the value of a Text by taking the sum of the values entered in each of the fields using a StateNotifierProvider.
In the following code, CashCounterData is a data model to be used by the StateNotifier, CashCounter. The notifier has two methods, setCount and setCash that are called in the onChanged method of each TextFormField.
final cashProvider = StateNotifierProvider<CashCounter, CashCounterData>((ref) => CashCounter());
class CashCounter extends StateNotifier<CashCounterData> {
CashCounter() : super(_initialData);
static const _initialData = CashCounterData(0, 0);
void setCount(int value){
state = CashCounterData(value, state.cash);
}
void setCash(value){
state = CashCounterData(state.count, value);
}
int get count => state.count;
int get cash => state.cash;
}
class CashCounterData {
final int count;
final int cash;
const CashCounterData(this.count, this.cash);
}
Next, I implemented the UI and am trying to tie in the StateNotifierProvider defined above. However, when I enter values into each TextFormField, the Text widget is always displaying 0.
class CalculatableTextFormField extends HookWidget {
#override
Widget build(BuildContext context) {
final cashCounterProvider = useProvider(cashProvider.notifier);
final TextEditingController _count = TextEditingController();
final TextEditingController _cash = TextEditingController();
return Scaffold(
body: Form(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'${cashCounterProvider.count + cashCounterProvider.cash}'
),
TextFormField(
controller: _count,
keyboardType: TextInputType.number,
onChanged: (value)=>cashCounterProvider.setCount(int.parse(value)),
),
TextFormField(
controller: _cash,
keyboardType: TextInputType.number,
onChanged: (value)=>cashCounterProvider.setCash(int.parse(value)),
)
],
),
),
);
}
}
What am I missing to get my desired behavior?
You are watching the notifier, not the state. The state is what gets changed, and therefore notifies listeners.
It should work if you just change:
final cashCounterProvider = useProvider(cashProvider.notifier);
to:
final cashCounterProvider = useProvider(cashProvider);
Then, in your change handlers:
onChanged: (value) => context.read(cashProvider.notifier).setCash(int.tryParse(value) ?? 0),
When using a provider in a handler like this, prefer context.read as demonstrated above to avoid unnecessary rebuilds.
You also need to use hooks if you are putting your TextEditingControllers in the build method.
final TextEditingController _count = useTextEditingController();
final TextEditingController _cash = useTextEditingController();
All together, your solution is the following:
class CalculatableTextFormField extends HookWidget {
#override
Widget build(BuildContext context) {
final cashCounterProvider = useProvider(cashProvider);
final TextEditingController _count = useTextEditingController();
final TextEditingController _cash = useTextEditingController();
return Scaffold(
body: Form(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('${cashCounterProvider.count + cashCounterProvider.cash}'),
TextFormField(
controller: _count,
keyboardType: TextInputType.number,
onChanged: (value) =>
context.read(cashProvider.notifier).setCount(int.tryParse(value) ?? 0),
),
TextFormField(
controller: _cash,
keyboardType: TextInputType.number,
onChanged: (value) =>
context.read(cashProvider.notifier).setCash(int.tryParse(value) ?? 0),
)
],
),
),
);
}
}
I tried to get TextFormField value. But result is null
main page,
children:[
UrlTextField(),
UsernameTextField(),
UrlButton()
]
UrlTextField(), same like UsernameTextField()
class UrlTextField extends StatelessWidget {
final myController = TextEditingController();
#override
Widget build(BuildContext context) {
return AppTextField(
decoration:
InputDecoration(prefixText: "https://", labelText: "Enter your URL"),
myController: myController,
textInputType: TextInputType.url,
);}}
AppTextField() It's a common class, I used this class everywhere
class AppTextField extends StatelessWidget {
final InputDecoration decoration;
var myController = TextEditingController();
final TextInputType textInputType;
AppTextField({
this.decoration,
this.myController,
this.textInputType
});
#override
Widget build(BuildContext context) {
return TextFormField(
controller: myController,
keyboardType: textInputType,
textAlign: TextAlign.left,
decoration: decoration
);}}
I need to get Url and Username value when click button or any other area,
class UrlButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return AppButton(
onPressed: () {
String url = UrlTextField().myController.text;
String username = UsernameTextField().myController.text;
print('url is $text');
});}}
AppButton() This class also common
class AppButton extends StatelessWidget {
final VoidCallback onPressed;
AppButton({
this.buttonTextStyle
});
#override
Widget build(BuildContext context) {
return RaisedButton(
child: Text(...),
onPressed: onPressed);}}
You are trying to retrieve text from a controller which has just been instantiated in the onPressed of the button so there can't be any text so far! To solve this problem you need some way of State Management to access and change an existing widget, in your case the UrlTextField widget. I will give you an example of how you could solve this quickly:
Main page:
class MainPage extends StatefulWidget {
...
#override
createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
UrlTextField _urlTextField = UrlTextField();
...
children:[
_urlTextField,
UsernameTextField(),
UrlButton(_urlTextField)
]
Now we instantiated a UrlTextField which can be referenced to and can be passed to another widget like your UrlButton:
class UrlButton extends StatelessWidget {
final UrlTextField urlTextField;
UrlButton(this.urlTextField);
#override
Widget build(BuildContext context) {
return AppButton(
onPressed: () {
String url = this.urlTextField.myController.text;
String username = UsernameTextField().myController.text;
print('url is $text');
}
);
}
}
On this way you instantiated one UrlTextField and used it in your main page where a user can fill in some input and passed it down to UrlButton where you can access its controller and therefore its text.
I would recommend you to look more into the topic State Management since there are a lot of ways to handle such a case. I can recommend you to take a look on Provider which is very easy to use and convenient to access certain data.
what is the value of text in print('url is $text'); isn't it supposed to be like this print('url is $url');
I think this what you are trying to do.... But there's many loop holes brother..
One thing to remember.. You need separate controllers for each TextField you can't declare one as myController and assign it to all. They'll all have the same value.
class StackHelp extends StatefulWidget {
#override
_StackHelp createState() => _StackHelp();
}
class _StackHelp extends State<StackHelp> {
final TextEditingController myController = TextEditingController();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: new Scaffold(
body: Column(children: <Widget>[
UrlTextField(myController),
// UsernameTextField(),
UrlButton(myController)
])),
);
}
}
class UrlTextField extends StatelessWidget {
final TextEditingController myController;
UrlTextField(this.myController);
#override
Widget build(BuildContext context) {
return AppTextField(
decoration:
InputDecoration(prefixText: "https://", labelText: "Enter your URL"),
myController: myController,
textInputType: TextInputType.url,
);
}
}
class AppTextField extends StatelessWidget {
final InputDecoration decoration;
final TextEditingController myController;
final TextInputType textInputType;
AppTextField({this.decoration, this.myController, this.textInputType});
#override
Widget build(BuildContext context) {
return TextFormField(
controller: myController,
keyboardType: textInputType,
textAlign: TextAlign.left,
decoration: decoration);
}
}
class UrlButton extends StatelessWidget {
final TextEditingController myController;
UrlButton(this.myController);
#override
Widget build(BuildContext context) {
void onPressed() {
String url = this.myController.text;
// String username = UsernameTextField().myController.text;
print('url is $url');
}
return AppButton(onPressed);
}
}
class AppButton extends StatelessWidget {
final VoidCallback onPressed;
AppButton(this.onPressed);
#override
Widget build(BuildContext context) {
return RaisedButton(child: Text('Test'), onPressed: onPressed);
}
}
Given a stateful widget, is somehow possible to call a method defined in the State class (the one which extends State<NameOfTheWidget>). Actually, I just want to rebuild the _State class, like calling setState() but from outside of the class. I know how to it from children to parents but not viceversa.
class Foo extends StatefulWidget{
State createState() => new _State();
//...bar() ??
}
class _State extends State<Foo>{
#override
Widget build(BuildContext context) {...}
void bar(){...}
}
EDIT: some real code
First, we hace the equivalent to the inner widget; it's a a customized text field. The point is that I want enable and disable it according to the boolean _activo variable.
import 'package:flutter/material.dart';
import 'package:bukit/widgets/ensure.dart';
class EntradaDatos extends StatelessWidget{
final String _titulo;
final String _hint;
TextEditingController _tec;
FocusNode _fn = new FocusNode();
final String Function(String s) _validador;
final TextInputType _tit;
bool _activo;
/*
* CONSTRUCTOR
*/
EntradaDatos(this._titulo, this._hint, this._validador, this._tit, this._activo){
_tec = new TextEditingController();
}
#override
Widget build(BuildContext context){
print('Construyendo');
return new EnsureVisibleWhenFocused(
focusNode: _fn,
child: new TextFormField(
enabled: _activo,
keyboardType: _tit,
validator: _validador,
autovalidate: true,
focusNode: _fn,
controller: _tec,
decoration: InputDecoration(
labelText: _titulo,
hintText: _hint
),
)
);
}
String getContenido(){
return _tec.text;
}
}
Then I have a concrete implementation of the previous text field, which just extends it:
import 'package:flutter/material.dart';
import 'package:bukit/widgets/entrada_datos.dart';
class EntradaMail extends EntradaDatos{
static String _hint = "nombre#dominio.es";
static String _validador(String s){
if(s.isEmpty){
return 'El campo es obligatorio';
}else{
if(!s.contains('#') || !s.contains('.') || s.contains(' ')){
return 'Introduce una dirección válida';
}else{
String nombre = s.substring(0, s.indexOf('#'));
String servidor = s.substring(s.indexOf('#')+1, s.lastIndexOf('.'));
String dominio = s.substring(s.lastIndexOf('.')+1);
if(nombre.length < 2 || servidor.length < 2 || dominio.length < 2){
return 'Introduce una dirección válida';
}
}
}
}
EntradaMail(String titulo, bool activo) : super(titulo, _hint, _validador, TextInputType.emailAddress, activo);
}
Finally, the equivalent of my outter widget. It's just a checkbox followed by the prevoius EntradaEmail widget. As far as I know, once the checkbox is pressed and the onChange call is made, the setState call should rebuild everything, but I've contrasted with debug messaged that the build method of the first inner widget is never called. My point is enabling and disabling the text field according to the checkbox.
class CampoEnvio extends StatefulWidget{
EntradaMail _mail;
EntradaMovil _movil;
String _tituloMail;
String _tituloMovil;
bool _usaMail = false;
bool _usaMovil = false;
CampoEnvio(this._tituloMail, this._tituloMovil){
_mail = new EntradaMail(_tituloMail, _usaMail);
_movil = new EntradaMovil(_tituloMovil, _usaMovil);
}
State createState() => _State(_mail, _movil, _usaMail, _usaMovil, _tituloMail, _tituloMovil);
}
class _State extends State<CampoEnvio>{
bool _usaMail;
bool _usaMovil;
String _tituloMail;
String _tituloMovil;
EntradaMail _mail;
EntradaMovil _movil;
_State(this._mail, this._movil, this._usaMail, this._usaMovil, this._tituloMail, this._tituloMovil);
#override
Widget build(BuildContext context){
return new Column(
children: <Widget>[
new ListTile(
leading: new SizedBox(
width: 70.0,
child: new Row(
children: <Widget>[
new Checkbox(
value: _usaMail,
activeColor: Colors.black,
onChanged: (value) {
setState(() {
_usaMail = value;
});
},
),
],
),
),
title: _mail,
),
//...
new Divider()
],
);
}
}
Yes, in theory it is possible using a GlobalKey, but not recommended!
class OuterWidget extends StatefulWidget {
#override
State<StatefulWidget> createState() => OuterWidgetState();
}
class OuterWidgetState extends State<OuterWidget> {
final _innerKey = GlobalKey<InnerWidgetState>();
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
InnerWidget(key: _innerKey),
RaisedButton(
child: Text('call foo'),
onPressed: () {
_innerKey.currentState.foo();
},
)
],
);
}
}
class InnerWidget extends StatefulWidget {
InnerWidget({Key key}) : super(key: key);
#override
State<StatefulWidget> createState() => InnerWidgetState();
}
class InnerWidgetState extends State<InnerWidget> {
String _value = 'not foo';
#override
Widget build(BuildContext context) {
return Text(_value);
}
void foo() {
setState(() {
_value = 'totally foo';
});
}
}
Better approach: Instead, what it would be a good idea to pull the state up:
class OuterWidget extends StatefulWidget {
#override
State<StatefulWidget> createState() => OuterWidgetState();
}
class OuterWidgetState extends State<OuterWidget> {
String _innerValue;
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
InnerWidget(value: _innerValue),
RaisedButton(
child: Text('call foo'),
onPressed: () {
setState(() {
_innerValue = 'totally foo';
});
},
)
],
);
}
}
class InnerWidget extends StatefulWidget {
InnerWidget({Key key, this.value}) : super(key: key);
final String value;
#override
State<StatefulWidget> createState() => InnerWidgetState();
}
class InnerWidgetState extends State<InnerWidget> {
#override
Widget build(BuildContext context) {
return Text(widget.value);
}
}
If you can, make the inner widget stateless:
class InnerWidget extends StatelessWidget {
InnerWidget({Key key, this.value}) : super(key: key);
final String value;
#override
Widget build(BuildContext context) {
return Text(value);
}
}
If your child is interactive (taps, checkbox...), you can define callbacks with VoidCallback or ValueChanged<T> (or your own typedef) to process the events in the parent widget.
Ok, now that you added the sample code, I will try to explain why your widget does not work, and I will try to explain what other improvements can be made.
First of all, you can improve the readability of your code by using named constructors for all of your widgets, like in my other answers (You can auto-generate them with Android Studio: Define some final fields, then press the lightbulb button to generate the constructor).
The next problem is that widgets which create a TextEditingController must always be stateful widgets! Otherwise the input made by the user will disappear after every build!
Usually you would pass in the TextEditingController from a parent widget (the widget that handles processes data when you submit it)
Also, it is discouraged to extend widgets. Instead, use composition, e.g.:
class EntradaMail extends StatelessWidget {
final String titulo;
// ...
Widget build(BuildContext context) {
return EntradaDatos(
titulo: titulo,
//...
)
}
}
Widget properties should always be public and final (never start with a _).
You are doing some strange things in CampoEnvio.
First of all, you are for some reason passing in all the properties of the widget to the State in createState. That has some consequences which you probably don't intend.
In general it is extremely rare that your State class has constructor parameters, and usually you would not pass properties from the stateful widget to the state.
The problem is that createState is only called once, it is not called again when you call initState in a parent widget. The state is kept until the widget is disposed.
That means your state constructor is only called once as well, and the fields in _State (of CampoEnvio) will stay the same all the time. Even when the parent is rebuilt and calls the constructor of CampoEnvio again, the old values in _State will not be replaced.
It's also very stange that you are creating widgets (EntradaMail and EntradaMovil) in the StatefulWidget.
The class that extends StatefulWidget should not do that! It is basically just a "bag" of properties.
Here is the complete fixed sample code, following the conventions explained above:
class EntradaDatos extends StatefulWidget {
EntradaDatos({Key key, this.titulo, this.hint, this.validador, this.tit, this.activo}) : super(key: key);
final String titulo;
final String hint;
final String Function(String s) validador;
final TextInputType tit;
final bool activo;
#override
State<StatefulWidget> createState() => _EntradaDatosState();
}
class _EntradaDatosState extends State<EntradaDatos> {
// FocusNode and TextEditingController must be the same for the whole lifetime of the widget
// => put into State
TextEditingController _tec;
FocusNode _fn;
#override
void initState() {
super.initState();
_tec = new TextEditingController();
_fn = new FocusNode();
}
#override
Widget build(BuildContext context) {
print('Construyendo');
return new EnsureVisibleWhenFocused(
focusNode: _fn,
child: new TextFormField(
enabled: widget.activo,
keyboardType: widget.tit,
validator: widget.validador,
autovalidate: true,
focusNode: _fn,
controller: _tec,
decoration: InputDecoration(labelText: widget.titulo, hintText: widget.hint),
));
}
String getContenido() {
return _tec.text;
}
}
class EntradaMail extends StatelessWidget {
static String _hint = "nombre#dominio.es";
static String _validador(String s) {
if (s.isEmpty) {
return 'El campo es obligatorio';
} else {
if (!s.contains('#') || !s.contains('.') || s.contains(' ')) {
return 'Introduce una dirección válida';
} else {
String nombre = s.substring(0, s.indexOf('#'));
String servidor = s.substring(s.indexOf('#') + 1, s.lastIndexOf('.'));
String dominio = s.substring(s.lastIndexOf('.') + 1);
if (nombre.length < 2 || servidor.length < 2 || dominio.length < 2) {
return 'Introduce una dirección válida';
}
}
}
}
EntradaMail({Key key, this.titulo, this.activo}) : super(key: key);
final String titulo;
final bool activo;
#override
Widget build(BuildContext context) {
// use composition instead of inheritance
return EntradaDatos(
titulo: titulo,
activo: activo,
validador: _validador,
hint: _hint,
tit: TextInputType.emailAddress,
);
}
}
class CampoEnvio extends StatefulWidget {
const CampoEnvio({Key key, this.tituloMail, this.tituloMovil}) : super(key: key);
final String tituloMail;
final String tituloMovil;
#override
State<StatefulWidget> createState() => new _CampoEnvioState();
}
class _CampoEnvioState extends State<CampoEnvio> {
// I guess these variables are modified here using setState
bool _usaMail;
bool _usaMovil;
#override
Widget build(BuildContext context) {
// just rebuild the widgets whenever build is called!
final mail = new EntradaMail(
titulo: widget.tituloMail,
activo: _usaMail,
);
final movil = new EntradaMovil(
titulo: widget.tituloMovil,
activo: _usaMovil,
);
return new Column(
children: <Widget>[
new ListTile(
leading: new SizedBox(
width: 70.0,
child: new Row(
children: <Widget>[
new Checkbox(
value: _usaMail,
activeColor: Colors.black,
onChanged: (value) {
setState(() {
_usaMail = value;
});
},
),
],
),
),
title: mail,
),
//...
new Divider()
],
);
}
}
It always helps to look at the official samples in the Flutter repositories!