How can I add a validator function to a list of RadioButtons in order to have them validated (like TextFormFields with _formKey.currentState.validate()) after the User submits the Form?
You can copy paste run full code below
You can use package https://pub.dev/packages/flutter_form_builder
It support bulid-in validators such as FormBuilderValidators.required() you can directly use
you can also use custom validator function https://pub.dev/packages/flutter_form_builder#custom-validator-function
FormBuilderRadio(
decoration:
InputDecoration(labelText: 'My chosen language'),
attribute: "best_language",
leadingInput: true,
onChanged: _onChanged,
validators: [FormBuilderValidators.required()],
options:
["Dart", "Kotlin", "Java", "Swift", "Objective-C"]
.map((lang) => FormBuilderFieldOption(
value: lang,
child: Text('$lang'),
))
.toList(growable: false),
),
working demo
full code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter FormBuilder Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
inputDecorationTheme: InputDecorationTheme(
labelStyle: TextStyle(color: Colors.purple),
),
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
MyHomePageState createState() {
return MyHomePageState();
}
}
class MyHomePageState extends State<MyHomePage> {
var data;
bool autoValidate = true;
bool readOnly = false;
bool showSegmentedControl = true;
final GlobalKey<FormBuilderState> _fbKey = GlobalKey<FormBuilderState>();
final GlobalKey<FormFieldState> _specifyTextFieldKey =
GlobalKey<FormFieldState>();
ValueChanged _onChanged = (val) => print(val);
var genderOptions = ['Male', 'Female', 'Other'];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("FormBuilder Example"),
),
body: Padding(
padding: EdgeInsets.all(10),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
FormBuilder(
// context,
key: _fbKey,
autovalidate: true,
initialValue: {
'movie_rating': 5,
},
readOnly: false,
child: Column(
children: <Widget>[
FormBuilderRadio(
decoration:
InputDecoration(labelText: 'My chosen language'),
attribute: "best_language",
leadingInput: true,
onChanged: _onChanged,
validators: [FormBuilderValidators.required()],
options:
["Dart", "Kotlin", "Java", "Swift", "Objective-C"]
.map((lang) => FormBuilderFieldOption(
value: lang,
child: Text('$lang'),
))
.toList(growable: false),
),
],
),
),
Row(
children: <Widget>[
Expanded(
child: MaterialButton(
color: Theme.of(context).accentColor,
child: Text(
"Submit",
style: TextStyle(color: Colors.white),
),
onPressed: () {
if (_fbKey.currentState.saveAndValidate()) {
print(_fbKey.currentState.value);
} else {
print(_fbKey.currentState.value);
print("validation failed");
}
},
),
),
SizedBox(
width: 20,
),
Expanded(
child: MaterialButton(
color: Theme.of(context).accentColor,
child: Text(
"Reset",
style: TextStyle(color: Colors.white),
),
onPressed: () {
_fbKey.currentState.reset();
},
),
),
],
),
],
),
),
),
);
}
}
Related
I have a simple app that I am trying to hide or different widgets when a user clicks on yes or no, but when I select Yes or No all the two widgets are shown instead of only showing one when yes is clicked and No when no is clicked and hide Yes or vice versa,
This is what I have done so far
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
// Hide the debug banner
debugShowCheckedModeBanner: false,
title: 'Show Hide Widgets',
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// The inital group value
bool visibilityTag = false;
bool visibilityObs = false;
void _changed(bool visibility, String field) {
setState(() {
if (field == "no"){
visibilityTag = visibility;
}
if (field == "yes"){
visibilityObs = visibility;
}
});
}
String _selectedGender = 'None';
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text(
'Show Hide Widgets',
),
),
body: Padding(
padding: EdgeInsets.all(25),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Please pick one:'),
ListTile(
leading: Radio(
value: 'Yes',
groupValue: _selectedGender,
onChanged: (value) {
setState(() {
_selectedGender = value as String;
visibilityObs ? null : _changed(true, "yes");
});
},
),
title: Text('Yes'),
),
ListTile(
leading: Radio(
value: 'no',
groupValue: _selectedGender,
onChanged: (value) {
setState(() {
_selectedItem = value as String;
visibilityTag ? null : _changed(true, "no");
});
},
),
title: Text('No'),
),
SizedBox(height: 25),
visibilityObs ? new Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
new Expanded(
flex: 11,
child: new TextField(
maxLines: 1,
style: Theme.of(context).textTheme.headline6,
decoration: new InputDecoration(
labelText: "Yes",
isDense: true
),
),
),
new Expanded(
flex: 1,
child: new IconButton(
color: Colors.grey[400],
icon: const Icon(Icons.cancel, size: 22.0,),
onPressed: () {
_changed(false, "yes");
},
),
),
],
) : new Container(),
visibilityTag ? new Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
new Expanded(
flex: 11,
child: new TextField(
maxLines: 1,
style: Theme.of(context).textTheme.title,
decoration: new InputDecoration(
labelText: "yesno",
isDense: true
),
),
),
new Expanded(
flex: 1,
child: new IconButton(
color: Colors.grey[400],
icon: const Icon(Icons.cancel, size: 22.0,),
onPressed: () {
_changed(false, "no");
},
),
),
],
) : new Container(),
],
)),
);
}
How can I only show the a widget when yes is clicked, and no when no is clicked and Hide the previous yes selected?
From the example in docs:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const Center(
child: MyStatefulWidget(),
),
),
);
}
}
enum SingingCharacter { lafayette, jefferson }
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
SingingCharacter? _character = SingingCharacter.lafayette;
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
ListTile(
title: const Text('Lafayette'),
leading: Radio<SingingCharacter>(
value: SingingCharacter.lafayette,
groupValue: _character,
onChanged: (SingingCharacter? value) {
setState(() {
_character = value;
});
},
),
),
ListTile(
title: const Text('Thomas Jefferson'),
leading: Radio<SingingCharacter>(
value: SingingCharacter.jefferson,
groupValue: _character,
onChanged: (SingingCharacter? value) {
setState(() {
_character = value;
});
},
),
),
// -------------------------
_character == SingingCharacter.jefferson
? const ListTile(
title: Text('Something goes here!'),
)
: Container(),
// ----------------------
],
);
}
}
I know there was a really similar case and got solved, I modified my code to 99% liked to that but somehow my list is undefined.
The list that is undefined is at the line where ' ...(list as List).map((answer) { '.
import 'package:flutter/material.dart';
import 'package:kzstats/common/AppBar.dart';
import 'package:kzstats/common/Drawer.dart';
import '../toggleButton.dart';
class Settings extends StatelessWidget {
final String currentPage = 'Settings';
static const _modes = [
{
'mode': ['KZTimer', 'SimpleKZ', 'Vanilla']
},
{
'tickrate': [128, 102, 64]
},
];
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: HomepageAppBar(currentPage),
drawer: HomepageDrawer(),
body: Padding(
padding: EdgeInsets.all(8),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
buildHeader(
title: 'Mode',
child: ToggleButton(_modes[0]['mode']),
),
SizedBox(height: 32),
buildHeader(
title: 'Tick rate',
child: ToggleButton(_modes[1]['tickrate']),
),
],
),
),
),
),
);
}
}
Widget buildHeader({#required String title, #required Widget child}) => Column(
children: [
Text(
title,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
child,
],
);
class ToggleButton extends StatefulWidget {
final List<String> list;
ToggleButton(this.list);
#override
State createState() => new _ToggleButtonState();
}
class _ToggleButtonState extends State<ToggleButton> {
List<bool> _selections = [true, false, false];
#override
Widget build(BuildContext context) {
return new Container(
color: Colors.blue.shade200,
child: ToggleButtons(
isSelected: _selections,
fillColor: Colors.lightBlue,
color: Colors.black,
selectedColor: Colors.white,
renderBorder: false,
children: <Widget>[
...(list as List<String>).map((answer) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 12),
child: Text(
answer,
style: TextStyle(fontSize: 18),
),
);
}).toList(),
],
onPressed: (int index) {
setState(() {
for (int i = 0; i < _selections.length; i++) {
if (index == i) {
_selections[i] = true;
} else {
_selections[i] = false;
}
}
});
},
),
);
}
}
In case someone needs the full code, it's available at https://github.com/davidp918/KZStats
I'm new to Flutter and stackoverflow so if anything please just comment, thanks!
We can access a variable of StatefulWidget from the state class using "widget" (for example: widget.list)
Please refer below code sample for the reference.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Settings());
}
}
class Settings extends StatelessWidget {
final String currentPage = 'Settings';
static const modes = [
{
'mode': ['KZTimer', 'SimpleKZ', 'Vanilla']
},
{
'tickrate': [128, 102, 64]
},
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Container(
child: Padding(
padding: EdgeInsets.all(8),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
buildHeader(
title: 'Mode',
child: ToggleButton(modes[0]['mode']),
),
SizedBox(height: 32),
buildHeader(
title: 'Tick rate',
child: ToggleButton(modes[1]['tickrate']),
),
SizedBox(height: 32),
buildHeader(
title: 'Mode',
child: ToggleButton(modes[0]['mode']),
),
],
),
),
),
),
),
);
}
}
Widget buildHeader({#required String title, #required Widget child}) {
return Column(
children: [
Text(
title,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
SizedBox(height: 16),
child,
],
);
}
class ToggleButton extends StatefulWidget {
final List list;
ToggleButton(this.list);
#override
State createState() => new _ToggleButtonState();
}
class _ToggleButtonState extends State<ToggleButton> {
List<bool> _selections = [false, false, false];
#override
Widget build(BuildContext context) {
return Container(
color: Colors.blue.shade200,
child: ToggleButtons(
isSelected: _selections,
fillColor: Colors.lightBlue,
color: Colors.black,
selectedColor: Colors.white,
renderBorder: false,
children: [
...(widget.list as List)?.map((answer) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 12),
child: Text(
answer.toString() ?? '',
style: TextStyle(fontSize: 18),
),
);
})?.toList(),
],
onPressed: (int index) {
setState(() {
for (int i = 0; i < _selections.length; i++) {
if (index == i) {
_selections[i] = true;
} else {
_selections[i] = false;
}
}
});
},
),
);
}
}
I would like to prevent the keyboard from showing once the time is entered, how could I do it?
Updated
I tried using FocusScope.of(context).unfocus(); and it works for the first try, but it doesn't for the second one. It's kinda weird. Look at this.
The first works, the second doesn't work, but the third works, also noticed that the keyboard show up even before than the TimePicker is showed. (Sorry for my bad english)
this is the code;
TextEditingController _startTime = TextEditingController();
Widget _createTimePicker(String text, TextEditingController controller) {
return Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
text,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
),
SizedBox(
height: 10,
),
TextFormField(
validator: (String value) {
if (value.isEmpty) {
return 'Es necesario especificar una hora.';
}
},
controller: controller,
decoration: InputDecoration(
border: InputBorder.none,
fillColor: Color(0xfff3f3f4),
filled: true),
onTap: () {
Navigator.of(context).push(
showPicker(
context: context,
value: _time,
onChange: onTimeChanged,
is24HrFormat: true,
),
);
FocusScope.of(context).unfocus();
},
)
],
),
);
}
TimeOfDay _time = TimeOfDay.now().replacing(minute: 30);
void onTimeChanged(TimeOfDay newTime) {
setState(() {
_time = newTime;
_startTime.text = _time.format(context);
});
}
You can copy paste run 2 full code below
Solution 1: Quick fix for current code
You can use Future.delayed and FocusManager.instance.primaryFocus.unfocus
onTap: () async {
Navigator.of(context).push(
showPicker(
context: context,
value: _time,
onChange: onTimeChanged,
is24HrFormat: true,
),
);
await Future.delayed(Duration(milliseconds: 50), () {
FocusManager.instance.primaryFocus.unfocus();
});
})
Solution 2: Assume you do not need keyboard to show up all the time
You can use GestureDetector wrap TextFormField and set enable to false
GestureDetector(
onTap: () async {
Navigator.of(context).push(
showPicker(
context: context,
value: _time,
onChange: onTimeChanged,
is24HrFormat: true,
),
);
},
child: TextFormField(
enabled: false,
working demo 1
working demo 2
full code 1
import 'package:day_night_time_picker/lib/daynight_timepicker.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
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 _startTime = TextEditingController();
final _formKey = GlobalKey<FormState>();
Widget _createTimePicker(String text, TextEditingController controller) {
return Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
text,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
),
SizedBox(
height: 10,
),
TextFormField(
//enabled: false,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
controller: controller,
decoration: InputDecoration(
errorStyle: TextStyle(color: Colors.red),
border: InputBorder.none,
fillColor: Color(0xfff3f3f4),
filled: true),
onTap: () async {
Navigator.of(context).push(
showPicker(
context: context,
value: _time,
onChange: onTimeChanged,
is24HrFormat: true,
),
);
await Future.delayed(Duration(milliseconds: 200), () {
FocusManager.instance.primaryFocus.unfocus();
});
})
],
),
);
}
TimeOfDay _time = TimeOfDay.now().replacing(minute: 30);
void onTimeChanged(TimeOfDay newTime) {
setState(() {
_time = newTime;
_startTime.text = _time.format(context);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_createTimePicker("", _startTime),
ElevatedButton(
onPressed: () {
if (_formKey.currentState.validate()) {}
},
child: Text('Submit'),
)
],
),
),
),
);
}
}
full code 2
import 'package:day_night_time_picker/lib/daynight_timepicker.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
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 _startTime = TextEditingController();
final _formKey = GlobalKey<FormState>();
Widget _createTimePicker(String text, TextEditingController controller) {
return Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
text,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
),
SizedBox(
height: 10,
),
GestureDetector(
onTap: () async {
Navigator.of(context).push(
showPicker(
context: context,
value: _time,
onChange: onTimeChanged,
is24HrFormat: true,
),
);
},
child: TextFormField(
enabled: false,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
controller: controller,
decoration: InputDecoration(
errorStyle: TextStyle(color: Colors.red),
border: InputBorder.none,
fillColor: Color(0xfff3f3f4),
filled: true),
),
)
],
),
);
}
TimeOfDay _time = TimeOfDay.now().replacing(minute: 30);
void onTimeChanged(TimeOfDay newTime) {
setState(() {
_time = newTime;
_startTime.text = _time.format(context);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_createTimePicker("", _startTime),
ElevatedButton(
onPressed: () {
if (_formKey.currentState.validate()) {}
},
child: Text('Submit'),
)
],
),
),
),
);
}
}
on the callback function of the time selection dismiss the keyboard
FocusScope.of(context).unfocus();
if this field is only editable through the time picker, you can make the text field read-only by setting its attribute
readOnly: true
if the field can be edited by keyboard, then you can await for the dialog result and then call
FocusScope.of(context).requestFocus(new FocusNode());
After set time to TextInput or on confirm time select, call this FocusScope.of(context).unfocus() function.
// update your function
void onTimeChanged(TimeOfDay newTime) {
FocusScope.of(context).unfocus();
...
}
and,
TextFormField(
readOnly: true,
...
)
I am making a simple TODO app and I wanted to pass data from 2nd Screen of my app to 1st screen. My first Screen is initially blank and there is nothing to display and it has a floating button to add a task. When it is clicked it takes to the Second page where user inputs the task and author and Clicks on "Submit" Button and takes us to 1st page where it gets Displayed. I want to pass data as List .I am trying everything for last 24 hours I implemented using ModalRoute and also created one instance of ToDo class so that it doesn't give NULL error but nothing is working out. I am attaching code So that you can understand my problem.
This is my FirstScreen()
import 'package:flutter/material.dart';
import 'todo.dart';
import 'todocard.dart';
class ToDos extends StatefulWidget{
#override
_ToDosState createState() => _ToDosState();
}
class _ToDosState extends State<ToDos> {
#override
Widget build(BuildContext context) {
List<ToDo> todos =[
];
final routeArgs = ModalRoute.of(context).settings.arguments as Map ;
todos.add(ToDo(author: routeArgs['task'],task: routeArgs['author']));
return Container(
child: Scaffold(
appBar: AppBar(
title: Text("TODO LIST"),
centerTitle: true,
),
body: Column(
children:todos.map((e) => ToDoCard(
todo: e,
)).toList(),
//ToDoCard is just a Card widget
),
floatingActionButton: FloatingActionButton(
elevation: 0.0,
child: Text("+"),
onPressed: ()
{
Navigator.pushNamed(context, '/add_task');
},
),
),
);
}
}
My SecondScreen is :
import 'package:flutter/material.dart';
class AddTask extends StatefulWidget {
#override
_AddTaskState createState() => _AddTaskState();
}
class _AddTaskState extends State<AddTask> {
#override
Widget build(BuildContext context) {
String author,task;
return Container(
child: Scaffold(
appBar: AppBar(
title: Text("ADD TASK"),
centerTitle: true,
),
body: Column(
children: <Widget>[
Text("Enter Your Task"),
TextField(
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'task'
),
onChanged: (text){
task = text;
},
),
TextField(
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'author'
),
onChanged: (text){
author = text;
},
),
Row(
children: <Widget>[
RaisedButton(
onPressed: () {
Navigator.pop(context, {
'author': author,
'task': task,
});
},
child: Text("Submit"),
),
SizedBox(width: 10.0,),
RaisedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("Cancel"),
),
],
)
],
),
));
}
}
The main.dart is as Follows:
import 'package:flutter/material.dart';
import 'todo.dart';
import 'add_task.dart';
import 'display_todo.dart';
void main() {
runApp(MaterialApp(
title: 'Passing Data',
initialRoute: '/',
routes: {
'/': (context) => ToDos(),
'/add_task': (context) => AddTask(),
},
));
}
The ToDoCard for displaying the info as Card:
import 'todo.dart';
import 'package:flutter/material.dart';
class ToDoCard extends StatelessWidget {
final ToDo todo;
ToDoCard({this.todo});
#override
Widget build(BuildContext context) {
return Card(
color: Colors.cyan,
margin: EdgeInsets.fromLTRB(20, 20, 20, 0),
child: Padding(
padding: EdgeInsets.fromLTRB(13, 10, 13, 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
todo.author,
style: TextStyle(
color: Colors.black,
fontSize: 20.0,
),
),
SizedBox(height: 10.0,),
Text(
todo.task,
style: TextStyle(
color: Colors.black,
fontSize: 20.0,
),
),
SizedBox(height: 10.0,),
// RaisedButton.icon(onPressed: delete, icon: Icon(Icons.delete), label:
Text("Delete quote"), color: Colors.red,),
],
),
),
);
}
}
ToDo class:
class ToDo{
final String task;
final String author;
ToDo({this.task,this.author});
}
You can pass the result back on the Navigator.pop() and retrieve it by awaiting the pushNamed call.
Retrieve value in Page 1:
onPressed: () async
{
dynamic result = await Navigator.pushNamed(context, '/add_task');
if(result != null) {
setState(() {todos.add(result);});
}
},
Pass value from page 2 in the submit button
onPressed: () {
Navigator.pop(context, ToDo(task: task, author: author));
},
I'm trying to get the user input to change the title using a text form in show dialog but it seems the state is rebuilding whenever the keyboard shows/closes, my code is working before, but when I did flutter upgrade to v1.17 it's not working anymore. I've been stuck here for a couple of days now and I don't know what's wrong with my code or what error might be causing it, I can only see "getSelectedText on inactive InputConnection" and "mSecurityInputMethodService is null" in the debug console, please help.
Here's a sample of my code:
import 'package:flutter/material.dart';
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
final TextEditingController titleController = new TextEditingController();
final GlobalKey<FormState> _keyDialogForm = new GlobalKey<FormState>();
#override
void initState() {
super.initState();
titleController.text = 'Hello';
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: Center(
child: Column(
children: <Widget>[
Text(titleController.text),
SizedBox(
height: 50,
),
FlatButton(
color: Colors.redAccent,
onPressed: () {
showTitleDialog();
},
child: Text(
'Show Dialog',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
),
))
],
),
));
}
Future showTitleDialog() {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Form(
key: _keyDialogForm,
child: Column(
children: <Widget>[
TextFormField(
decoration: const InputDecoration(
icon: Icon(Icons.ac_unit),
),
maxLength: 8,
textAlign: TextAlign.center,
onSaved: (val) {
titleController.text = val;
},
autovalidate: true,
validator: (value) {
if (value.isEmpty) {
return 'Enter Title Name';
}
return null;
},
)
],
),
),
actions: <Widget>[
FlatButton(
onPressed: () {
if (_keyDialogForm.currentState.validate()) {
_keyDialogForm.currentState.save();
Navigator.pop(context);
}
},
child: Text('Save'),
color: Colors.blue,
),
FlatButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Cancel')),
],
);
});
}
}
You can copy paste run full code below
You can call setState in onSaved
code snippet
onSaved: (val) {
titleController.text = val;
setState(() {});
},
working demo
full code
import 'package:flutter/material.dart';
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
final TextEditingController titleController = new TextEditingController();
final GlobalKey<FormState> _keyDialogForm = new GlobalKey<FormState>();
#override
void initState() {
super.initState();
titleController.text = 'Hello';
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: Center(
child: Column(
children: <Widget>[
Text(titleController.text),
SizedBox(
height: 50,
),
FlatButton(
color: Colors.redAccent,
onPressed: () {
showTitleDialog();
},
child: Text(
'Show Dialog',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
),
))
],
),
));
}
Future showTitleDialog() {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Form(
key: _keyDialogForm,
child: Column(
children: <Widget>[
TextFormField(
decoration: const InputDecoration(
icon: Icon(Icons.ac_unit),
),
maxLength: 8,
textAlign: TextAlign.center,
onSaved: (val) {
titleController.text = val;
setState(() {});
},
autovalidate: true,
validator: (value) {
if (value.isEmpty) {
return 'Enter Title Name';
}
return null;
},
)
],
),
),
actions: <Widget>[
FlatButton(
onPressed: () {
if (_keyDialogForm.currentState.validate()) {
_keyDialogForm.currentState.save();
Navigator.pop(context);
}
},
child: Text('Save'),
color: Colors.blue,
),
FlatButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Cancel')),
],
);
});
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Test(),
);
}
}