Using Visibility Widget in Flutter to toggle widgets display - flutter

I have a Column widget I'd only like to make visible when a user taps on the add new button. I am using the Visibility widget Flutter provides and I am using setState() to change the visible property in the Visibility widget class. It doesn't work.
What could I be overlooking? Find my code snippet below:
bool _addNewTax = false;
FlatButton.icon(
color: kBgCircleColour,
padding: EdgeInsets.all(15.0),
icon: Icon(
FontAwesomeIcons.plusSquare,
color: kThemeStyleButtonFillColour,
),
onPressed: () {
setState(() {
_addNewTax = true;
});
Visibility(
visible: _addNewTax,
child: _buildTaxInputFields(),
);
},
label: Text(
'Add new tax',
style: kRegularTextStyle,
),
);
My _buildTaxInputFields function as below:
_buildTaxInputFields() {
return Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(20.0),
child: TextFormField(
initialValue: inputValue,
textAlign: TextAlign.left,
onSaved: () {},
decoration: kTextFieldDecoration.copyWith(hintText: 'tax name e.g. VAT, service charge, etc'),
),
Padding(
padding: const EdgeInsets.all(20.0),
child: TextFormField(
initialValue: inputValue,
textAlign: TextAlign.left,
onSaved: () {},
decoration: kTextFieldDecoration.copyWith(hintText: 'tax percentage e.g. 20, 19.5'),
),
Row(
children: <Widget>[
Expanded(
flex: 1,
child: RadioListTile(
title: Text('I\'ll bear it '),
value: TaxBearer.merchant,
groupValue: _taxBearer,
onChanged: (TaxBearer value) {
setState(() {
_taxBearer = value;
});
},
),
),
Expanded(
flex: 1,
child: RadioListTile(
title: Text('Push to customer'),
value: TaxBearer.customer,
groupValue: _taxBearer,
onChanged: (TaxBearer value) {
setState(() {
_taxBearer = value;
});
}),
),
],
),
],
);
}

Your visibility widget will never be rendered because you're generating it in the onPressed method of a button.
You could use a column to generate it below the button like this:
bool _addNewTax = false;
Column(
children: [
FlatButton.icon(
color: kBgCircleColour,
padding: EdgeInsets.all(15.0),
icon: Icon(
FontAwesomeIcons.plusSquare,
color: kThemeStyleButtonFillColour,
),
onPressed: () {
setState(() {
_addNewTax = true;
});
},
label: Text(
'Add new tax',
style: kRegularTextStyle,
),
),
Visibility(
visible: _addNewTax,
child: _buildTaxInputFields(),
),
],
);

Related

flutter bloc doesn't update the ui?

when i navigate from NewSales screen to CreateItem screen and add item and press the add button
the item is added to sqflite database then it navigates back to new sales but the state is not updated , it's updated only if i restarted the app
the NewSales screen
class _NewSalesState extends State<NewSales> {
final controller = TextEditingController();
showAlertDialog(BuildContext context,String name) {
// Create button
// Create AlertDialog
AlertDialog alert = AlertDialog(
title: const Text("Alert"),
content: const Text("you want to delete this item?"),
actions: [
TextButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.green)),
child: const Text("CANCEL", style: TextStyle(color: Colors.white)),
onPressed: () {
Navigator.of(context).pop();
},
),
BlocBuilder<SalesCubit, SalesState>(
builder: (context, state) {
final bloc=BlocProvider.of<SalesCubit>(context);
return TextButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.red)),
child: const Text(
"DELETE",
style: TextStyle(color: Colors.white),
),
onPressed: () {
Navigator.of(context).pop();
bloc.deleteItem(name).then((value) {
bloc.getAllItems();
});
},
);
},
)
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
// #override
#override
Widget build(BuildContext context) {
final deviceSize = MediaQuery
.of(context)
.size;
return BlocConsumer<SalesCubit, SalesState>(
listener: (context, state) {},
builder: (context, state) {
final bloc = BlocProvider.of<SalesCubit>(context);
if (state is SalesInitial) {
bloc.getAllItems();
}
return Scaffold(
drawer: const navDrawer(),
appBar: AppBar(
title: const Text("Ticket"),
actions: [
IconButton(
onPressed: () async {
String barcodeScanRes;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
barcodeScanRes = await FlutterBarcodeScanner.scanBarcode(
'#ff6666', 'Cancel', true, ScanMode.BARCODE);
print('barcodeScanRes $barcodeScanRes');
print(bloc.bsResult);
} on PlatformException {
barcodeScanRes = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
bloc.toggleSearch();
controller.text = barcodeScanRes;
bloc.filterItems(barcodeScanRes);
},
icon: const Icon(Icons.scanner),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: IconButton(
onPressed: () {
Navigator.of(context).pushNamed(Routes.addCustomerRoute);
},
icon: Icon(Icons.person_add)),
),
],
),
body: Column(
children: [
// the first button
Container(
width: double.infinity,
height: deviceSize.height * .1,
color: Colors.grey,
child: GestureDetector(
onTap: ()=>displayMessage("an item was charged successfully", context),
child: Padding(
padding: const EdgeInsets.all(10),
child: Container(
color: Colors.green,
child: const Center(
child: Text("charge",style: TextStyle(color: Colors.white),),
),
),
),
),
),
// the second container
SizedBox(
width: deviceSize.width,
height: deviceSize.height * .1,
child: Row(
children: [
DecoratedBox(
decoration:
BoxDecoration(border: Border.all(color: Colors.grey)),
child: SizedBox(
width: deviceSize.width * .8,
child: bloc.isSearch
? TextFormField(
autofocus: true,
controller: controller,
decoration:
const InputDecoration(hintText: "Search"),
onChanged: (value) {
bloc.filterItems(controller.text);
},
)
: DropdownButtonFormField<String>(
// underline: Container(),
// value: "Discounts",
hint: const Padding(
padding: EdgeInsets.only(left: 10),
child: Text('Please choose type'),
),
items: <String>['Discounts', 'All Items']
.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (_) {},
),
),
),
DecoratedBox(
decoration:
BoxDecoration(border: Border.all(color: Colors.grey)),
child: SizedBox(
width: deviceSize.width * .2,
child: IconButton(
onPressed: () {
bloc.toggleSearch();
if (!bloc.isSearch) {
bloc.filterItems('');
}
},
icon: Icon(
!bloc.isSearch ? Icons.search : Icons.close)),
),
)
],
),
),
// the third container
if (state is IsLoading || state is SalesInitial)
const Center(
child: CircularProgressIndicator(
color: Colors.green,
backgroundColor: Colors.green,
),
),
if (state is Loaded || state is IsSearch || state is SearchDone)
bloc.items.isEmpty
? const Center(
child: Text("no items added yet"),
)
: Expanded(
child: bloc.filteredItems.isEmpty
? const Center(
child: Text(
"no items found with this name",
style: TextStyle(color: Colors.green),
),
)
: ListView.builder(
itemCount: bloc.filteredItems.length,
itemBuilder: (context, i) {
final item = bloc.filteredItems[i];
return Card(
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.green,
radius: 20,
child: Text(item.price),
),
title: Text(item.name),
subtitle: Text(item.barcode),
trailing: Column(
children: [
IconButton(
onPressed: () {
showAlertDialog(context,item.name);
// bloc.deleteItem(item.name).then((value) {
// bloc.getAllItems();
// });
},
icon: const Icon(
Icons.delete,
color: Colors.red,
))
],
)),
);
}))
],
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: ()async {
Navigator.of(context).pushNamed(Routes.createItemRoute);
},
),
);
},
);
}
}
the CreateItem screen
class _CreateItemState extends State<CreateItem> {
final nameController = TextEditingController();
final priceController = TextEditingController();
final costController = TextEditingController();
final skuController = TextEditingController();
final barcodeController = TextEditingController();
final inStockController = TextEditingController();
bool each = true; // bool variable for check box
bool isColor = true;
bool switchValue = false;
File? file;
String base64File = "";
String path = "";
final _formKey = GlobalKey<FormState>();
#override
void dispose() {
nameController.dispose();
priceController.dispose();
costController.dispose();
skuController.dispose();
barcodeController.dispose();
inStockController.dispose();
super.dispose();
}
Future pickImage(ImageSource source) async {
File? image1;
XFile imageFile;
final imagePicker = ImagePicker();
final image = await imagePicker.pickImage(source: source);
imageFile = image!;
image1 = File(imageFile.path);
List<int> fileUnit8 = image1.readAsBytesSync();
setState(() {
base64File = base64.encode(fileUnit8);
});
}
#override
Widget build(BuildContext context) {
final deviceSize = MediaQuery.of(context).size;
return BlocProvider(
create: (BuildContext context) => CreateItemCubit(),
child: Builder(builder: (context){
final bloc=BlocProvider.of<CreateItemCubit>(context);
return Scaffold(
appBar: AppBar(
leading: IconButton(
onPressed: () => Navigator.of(context).pushNamedAndRemoveUntil(Routes.newSalesRoute, (route) => false),
icon: const Icon(Icons.arrow_back)),
title: const Text("Create Item"),
actions: [TextButton(onPressed: () {}, child: const Text("SAVE"))],
),
body: Padding(
padding: const EdgeInsets.all(10),
child: Form(
key: _formKey,
child: ListView(
children: [
TextFormField(
controller: nameController,
cursorColor: ColorManager.black,
decoration: const InputDecoration(hintText: "Name"),
validator: (value) {
if (value!.isEmpty || value.length < 5) {
return "name can't be less than 5 chars";
}
return null;
},
),
gapH24,
Text(
"Category",
style: TextStyle(color: ColorManager.grey),
),
SizedBox(
width: deviceSize.width,
child: DropdownButtonFormField<String>(
// value: "Categories",
hint: const Padding(
padding: EdgeInsets.only(left: 10),
child: Text('No Category'),
),
items: <String>['No Category', 'Create Category']
.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (_) {},
),
),
gapH24,
const Text(
"Sold by",
style: TextStyle(color: Colors.black),
),
gapH24,
Row(
children: [
Checkbox(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5.0))), // Rounded Checkbox
value: each,
onChanged: (inputValue) {
setState(() {
each = !each;
});
},
),
gapW4,
const Text(
"Each",
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
gapH24,
Row(
children: [
Checkbox(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5.0))), // Rounded Checkbox
value: !each,
onChanged: (inputValue) {
setState(() {
each = !each;
});
},
),
gapW4,
const Text(
"Weight",
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
TextFormField(
controller: priceController,
decoration: InputDecoration(
hintText: "Price",
hintStyle: TextStyle(color: ColorManager.grey)),
validator: (value) {
if (value!.isEmpty) {
return "price can,t be empty";
}
return null;
},
),
gapH4,
Text(
"leave the field blank to indicate price upon sale",
style: TextStyle(color: ColorManager.grey),
),
gapH20,
Text(
"Cost",
style: TextStyle(color: ColorManager.grey),
),
TextFormField(
controller: costController,
decoration: InputDecoration(
hintText: "cost",
hintStyle: TextStyle(color: ColorManager.black)),
validator: (value) {
if (value!.isEmpty) {
return "cost can't be empty";
}
return null;
},
),
gapH20,
Text(
"SKU",
style: TextStyle(color: ColorManager.grey),
),
TextFormField(
controller: skuController,
decoration: InputDecoration(
hintText: "Sku",
hintStyle: TextStyle(color: ColorManager.black)),
validator: (value) {
if (value!.isEmpty) {
return "Sku can't be empty";
}
return null;
},
),
gapH30,
TextFormField(
controller: barcodeController,
decoration: InputDecoration(
hintText: "Barcode",
hintStyle: TextStyle(color: ColorManager.grey)),
validator: (value) {
if (value!.isEmpty) {
return "Barcode can't be empty";
}
return null;
},
),
gapH30,
// Divider(thickness: 1,color: ColorManager.black,),
Text(
"Inventory",
style: TextStyle(
color: ColorManager.green,
fontSize: 15,
fontWeight: FontWeight.bold),
),
// ListTile(
// title: Text("TrackStock",style: TextStyle(color: ColorManager.green,fontSize: 15,fontWeight: FontWeight.bold)),
// trailing: Switch(value: switchValue, onChanged: (bool value) {
// setState(() {
// switchValue=!switchValue;
// });
// },),
// ),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("TrackStock",
style: TextStyle(
color: ColorManager.grey,
fontSize: 15,
fontWeight: FontWeight.bold)),
Switch(
value: switchValue,
onChanged: (bool value) {
setState(() {
switchValue = !switchValue;
});
},
),
],
),
gapH10,
if (switchValue)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"in stock",
style:
TextStyle(color: ColorManager.grey, fontSize: 15),
),
TextFormField(
keyboardType: TextInputType.number,
controller: inStockController,
decoration: const InputDecoration(hintText: "0"),
validator: (value) {
if (value!.isEmpty) {
return "value can't be empty";
}
return null;
},
)
],
),
gapH20,
Text(
"Representation in POS",
style: TextStyle(
color: ColorManager.green,
fontSize: 15,
fontWeight: FontWeight.bold),
),
gapH15,
Row(
children: [
Checkbox(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5.0))), // Rounded Checkbox
value: isColor,
onChanged: (inputValue) {
setState(() {
if (!isColor) {
isColor = !isColor;
}
});
},
),
gapW4,
const Text(
"Color and Shape",
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
gapH10,
Row(
children: [
Checkbox(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5.0))), // Rounded Checkbox
value: !isColor,
onChanged: (inputValue) {
setState(() {
if (isColor) {
isColor = !isColor;
}
});
},
),
gapW4,
const Text(
"Image",
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
isColor
? GridView.builder(
physics:
const NeverScrollableScrollPhysics(), // to disable GridView's scrolling
shrinkWrap: true, // You won't see infinite size error
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 5.0,
mainAxisSpacing: 5.0,
),
itemCount: containers().length,
itemBuilder: (BuildContext context, int index) {
return containers()[index];
},
)
: Padding(
padding: const EdgeInsets.all(10),
child: Row(
children: [
Expanded(
flex: 1,
child: base64File == ""
? const Icon(Icons.person)
: Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(5)),
child: Image.memory(
base64.decode(base64File)))),
gapW4,
Expanded(
flex: 2,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
TextButton(
onPressed: () =>
pickImage(ImageSource.camera),
child: Row(
children: [
Icon(
Icons.camera_alt,
color: ColorManager.black,
),
gapW8,
Text(
"Take a photo",
style: TextStyle(
color: ColorManager.black,
),
)
],
),
),
const Divider(
thickness: 1,
color: Colors.grey,
),
TextButton(
onPressed: () =>
pickImage(ImageSource.gallery),
child: Row(
children: [
Icon(Icons.camera_alt,
color: ColorManager.black),
gapW8,
Text(
"Choose from gallery",
style: TextStyle(
color: ColorManager.black,
),
)
],
),
)
],
),
)
],
),
)
],
),
),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.save,color: Colors.white,),
onPressed: () async {
bool isValid = _formKey.currentState!.validate();
if (isValid) {
FocusScope.of(context).unfocus();
ItemModel? item=await bloc.checkItem(nameController.text);
if(item!=null){
displayMessage("this item was inserted before", context);
}else{
int? res=await bloc.saveItem(ItemModel(
nameController.text,
priceController.text,
costController.text,
skuController.text,
barcodeController.text));
if(res!=null){
displayMessage("an item was inserted successfully", context);
Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context)=>const NewSales()), (route) => false);
}
}
}
},
),
);
}
),
);
}
}
this is the SaleCubit
class SalesCubit extends Cubit<SalesState> {
SalesCubit() : super(SalesInitial());
bool isSearch=false;
ItemDBHelper db=ItemDBHelper();
List<ItemModel> items=[];
List<ItemModel> filteredItems=[];
String bsResult='Unknown'; //barcode search result
void toggleSearch(){
isSearch=!isSearch;
emit(IsSearch());
}
void setBarcodeResult(String value){
bsResult=value;
emit(SearchDone());
}
void filterItems(String query){
// emit(Searching());
query.isEmpty?
filteredItems=items:
filteredItems=items.where((item) => item.name.toLowerCase().contains(query.toLowerCase())).toList();
emit(SearchDone());
}
Future<List<ItemModel>?> getAllItems()async{
try{
emit(IsLoading());
items=await db.getAllItems();
filteredItems=items;
print(items);
return items;
}finally{
emit(Loaded());
}
}
Future<void> deleteItem(String name)async{
await db.deleteItem(name);
emit(SearchDone());
}
}
this is the SalesState
part of 'sales_cubit.dart';
#immutable
abstract class SalesState {}
class SalesInitial extends SalesState {}
class IsSearch extends SalesState {}
class IsLoading extends SalesState {}
class Loaded extends SalesState {}
class Searching extends SalesState {}
class SearchDone extends SalesState {}

Dynamic DropDownFormField returns null on Submit Button

I have created a page where user can input their exam result which are subject and grade.
On the page, user need to click the add subject button to add another subject depending on how many subject they have. User also can remove subject by clicking the remove subject button.
The problem is only TextFormField value submitted. How to get the data for DropDownFormField if the form created dynamically?
I have tried the read the data using the onChanged function of the drop down and yes the data was stored to the variable gradeController but it returns null when I submit the data.
This is the model
class SubjectGrade {
final String? name;
final String? grade;
SubjectGrade(this.name, this.grade);
#override
String toString() {
return 'SPM: name = $name, grade = $grade';
}
}
This is the form
import 'package:flutter/material.dart';
import 'package:testing_app/models/education_model.dart';
class EducationBackgroundForm extends StatefulWidget {
const EducationBackgroundForm({Key? key}) : super(key: key);
#override
_EducationBackgroundFormState createState() =>
_EducationBackgroundFormState();
}
class _EducationBackgroundFormState extends State<EducationBackgroundForm> {
final nameTECs = <TextEditingController>[];
final gradeTECs = <String?>[];
final forms = <Form>[];
Form createForm() {
TextEditingController nameController = TextEditingController();
String? gradeController;
nameTECs.add(nameController);
gradeTECs.add(gradeController);
final grades = ['A+', 'A', 'A-', 'B+', 'B', 'C+', 'C', 'D', 'E', 'G'];
DropdownMenuItem<String> buildMenuItem(String item) => DropdownMenuItem(
value: item,
child: Text(item),
);
return Form(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
children: <Widget>[
Expanded(
flex: 2,
child: Column(
children: <Widget>[
TextFormField(
controller: nameController,
decoration: InputDecoration(
hintText: 'Subject Name',
labelText: 'Subject ${forms.length + 1}',
border: const OutlineInputBorder(),
contentPadding:
const EdgeInsets.only(left: 8, right: 8),
),
keyboardType: TextInputType.text,
textInputAction: TextInputAction.done,
),
],
),
),
const SizedBox(width: 5),
Expanded(
flex: 1,
child: Column(
children: <Widget>[
DropdownButtonHideUnderline(
child: DropdownButtonFormField(
value: gradeController,
isExpanded: true,
iconSize: 36,
icon: const Icon(Icons.arrow_drop_down,
color: Colors.black),
items: grades.map(buildMenuItem).toList(),
onChanged: (String? value) {
setState(() {
gradeController = value;
});
},
hint: const Text("Grade"),
decoration: const InputDecoration(
border: OutlineInputBorder(),
contentPadding: EdgeInsets.only(left: 8),
),
),
)
],
)),
],
),
const SizedBox(height: 10)
],
),
);
}
#override
void initState() {
super.initState();
forms.add(createForm());
}
saveSPMResultToFirebase() {
List<SubjectGrade> entries = [];
for (int i = 0; i < forms.length; i++) {
final name = nameTECs[i].text;
final grade = gradeTECs[i].toString();
entries.add(SubjectGrade(name, grade));
}
debugPrint(entries.toString());
//Navigator.pop(context, entries);
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text("SPM Result", style: TextStyle(fontSize: 18)),
const SizedBox(height: 20),
Expanded(
child: ListView.builder(
itemCount: forms.length,
itemBuilder: (BuildContext context, int index) {
return forms[index];
},
),
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: SizedBox(
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.all(14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8))),
onPressed: () => setState(() {
forms.add(createForm());
}),
child: const Text(
'Add Subject',
style: TextStyle(
fontSize: 16,
),
),
),
),
),
const SizedBox(width: 5),
Expanded(
child: SizedBox(
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.all(14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8))),
onPressed: () => setState(() {
if (forms.length > 1) {
forms.removeAt(forms.length - 1);
}
}),
child: const Text(
'Remove Subject',
style: TextStyle(
fontSize: 16,
),
),
),
),
),
],
),
const SizedBox(height: 10),
SizedBox(
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.all(14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8))),
onPressed: () {
saveSPMResultToFirebase();
},
child: const Text(
'Update',
style: TextStyle(
fontSize: 16,
),
),
),
),
],
);
}
}
When I submit the form, the debug output I get from the submit function for grade is null.
Output:
[SPM: name = English, grade = null]
You should add value of DropDownButton in onChanged method not at top of create form.
change your code to this:
First Delete gradeTECs.add(gradeController); line from top of createForm().
Form createForm() {
TextEditingController nameController = TextEditingController();
String? gradeController;
nameTECs.add(nameController);
// gradeTECs.add(gradeController); <==== DELETE THIS LINE.
Add gradeTECs.add(gradeontroller); in onChanged method of DropdownButtonFormField
DropdownButtonHideUnderline(
child: DropdownButtonFormField(
value: gradeController,
isExpanded: true,
iconSize: 36,
icon: const Icon(Icons.arrow_drop_down,
color: Colors.black),
items: grades.map(buildMenuItem).toList(),
onChanged: (String? value) {
setState(() {
gradeController = value;
gradeTECs.add(gradeController); <=== ADD HERE ==>
});
},
hint: const Text("Grade"),
decoration: const InputDecoration(
border: OutlineInputBorder(),
contentPadding: EdgeInsets.only(left: 8),
),
),
),

How to validate formfield

Am a newbie though. I followed a tutorial for my code below which is a register form. How can i validate each input field since it's just one widget which is reused for the entire input field.
............................................................................................................................................................
Code
import 'package:flutter/material.dart';
import 'package:flutterlogindesign/utils/color.dart';
import 'package:flutterlogindesign/widgets/btn_widget.dart';
import 'package:flutterlogindesign/widgets/herder_container.dart';
class RegPage extends StatefulWidget {
#override
_RegPageState createState() => _RegPageState();
}
class _RegPageState extends State<RegPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.only(bottom: 30),
child: Column(
children: <Widget>[
HeaderContainer("Register"),
Expanded(
flex: 1,
child: Container(
margin: EdgeInsets.only(left: 20, right: 20, top: 30),
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
_textInput(hint: "Fullname", icon: Icons.person),
_textInput(hint: "Email", icon: Icons.email),
_textInput(hint: "Phone Number", icon: Icons.call),
_textInput(hint: "Password", icon: Icons.vpn_key),
Expanded(
child: Center(
child: ButtonWidget(
btnText: "REGISTER",
onClick: () {
Navigator.pop(context);
},
),
),
),
RichText(
text: TextSpan(children: [
TextSpan(
text: "Already a member ? ",
style: TextStyle(color: Colors.black)),
TextSpan(
text: "Login",
style: TextStyle(color: orangeColors)),
]),
)
],
),
),
)
],
),
),
);
}
Widget _textInput({controller, hint, icon}) {
return SingleChildScrollView(
child: Container(
margin: EdgeInsets.only(top: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20)),
color: Colors.white,
),
padding: EdgeInsets.only(left: 10),
child: TextFormField(
controller: controller,
decoration: InputDecoration(
border: InputBorder.none,
hintText: hint,
prefixIcon: Icon(icon),
),
),
),
);
}
}
Use Form Widget like the following
final _formKey = GlobalKey<FormState>();
bool autoValidate = false;
String phone;
child:Form( key: _formKey,
autovalidateMode: autoValidate ? AutovalidateMode.always : AutovalidateMode.disabled,
child: Column(
children: [
AppTextFormField(
initialValue: phone,
onChanged: (value) {
phone = value;
},
validator: (value) {
if (value.isEmpty) {
return 'Enter Phone number';
}
},
),
PrimaryButton(
text: 'Go',
onTap: () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
} else {
autoValidate = true;
}
},
),
],
),
),

set selected to radio button Flutter

How to set radio button to selected based on String value fetched from API?
I have an async function that does the API call and fetches the user profile. I need to set the selected gender to the radio button.
I tried calling SetState() from an async function but it results in infinite loop call for build().
Below is my code:
#override
Widget build(BuildContext context) {
if(isProfileExists) {
getProfileDetails();
print("selected radio : "+selectedRadio.toString());
if(selectedRadio >= 0) {
setMemberType(selectedRadio);
}
}
double width=MediaQuery.of(context).size.width;
double height=MediaQuery.of(context).size.height;
Future<bool> _onWillPop() async {
Navigator.pushNamedAndRemoveUntil(context, '/home', ModalRoute.withName('/home'));
}
return new WillPopScope(
child: Scaffold(
appBar: AppBar(
title: Text(
_userName+"\'s Profile ",
style: TextStyle(fontSize: 20.0),
),
actions: <Widget>[
Padding(
padding: EdgeInsets.only(right: 20.0),
child: GestureDetector(
onTap: () {},
child: Icon(
Icons.save
),
)
),
],
),
key: _scaffoldKey,
body: Container(
height: height,
width: width,
child: SingleChildScrollView(
padding: EdgeInsets.fromLTRB(0, 20.0, 0, 10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
GestureDetector(
onTap: (){},
child: TextField(
decoration: InputDecoration(labelText: "Bio"),
controller: bioTextController,
),
),
SizedBox(height: 20.0,),
Padding(
padding: EdgeInsets.all(10.0),
child: Text("Basic Information", style: TextStyle(
color: Colors.black54,
fontSize: 14.0
),
),
),
TextField(
decoration: InputDecoration(labelText: "Height"),
controller: heightTextController,
),
TextFormField(
decoration: InputDecoration(labelText: "Weight"),
controller: weightTextController,
),
TextFormField(
decoration: InputDecoration(labelText: "BMI"),
controller: bmiTextController,
),
TextFormField(
decoration: InputDecoration(labelText: "Target Weight"),
controller: targetWeightTextController,
),
TextFormField(
decoration: InputDecoration(labelText: "Date Of Birth"),
controller: dateOfBirthController,
),
TextFormField(
decoration: InputDecoration(labelText: "Age"),
readOnly: true,
controller: ageTextController,
),
SizedBox(height: 15.0),
Text("Gender"),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Radio(
value: 0,
groupValue: radioValue,
onChanged: setMemberType,
),
Text("Female" ),
Radio(
value: 1,
groupValue: radioValue,
onChanged: setMemberType,
),
Text("Male"),
],
),
...
And the set state function is below:
void setMemberType(int i) {
setState(() {
radioValue = i;
if(i == 0)
_gender = "F";
else
_gender = "M";
});
}
You cannnot call setState from your build method.
Remove this:
if(isProfileExists) {
getProfileDetails();
print("selected radio : "+selectedRadio.toString());
if(selectedRadio >= 0) {
setMemberType(selectedRadio);
}
}
Instead, use an initState inside the State of your Stateful Widget:
#override
void initState() {
if(isProfileExists) {
getProfileDetails();
if(selectedRadio >= 0) {
radioValue = selectedRadio;
}
}
}

Flutter : PopUp Form value showing null

Iam new to flutter.
In my project I need to show a popup when i load my page. So i used WidgetsBindingin initstate().
My code is working fine for showing popup on pageload, but in that popup,there is a textfield to enter mobile number after submitting i need to get that phone number but it not showing the value. While compiling it showing null value.
#override
void initState() {
super.initState();
// _fetchSessionAndNavigate();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await showDialog<String>(
context: context,
builder: (BuildContext context) => new AlertDialog(
content: Stack(
overflow: Overflow.visible,
children: <Widget>[
Positioned(
right: -40.0,
top: -40.0,
child: InkResponse(
onTap: () {
Navigator.of(context).pop();
},
child: CircleAvatar(
child: Icon(Icons.close),
backgroundColor: Colors.red,
),
),
),
Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: EdgeInsets.all(8.0),
child: TextFormField(
keyboardType: TextInputType.number,
maxLines: 1,
obscureText: true,
autofocus: false,
decoration: new InputDecoration(
hintText: 'Enter Mobile Number',
icon: new Icon(
Icons.phone_iphone,
color: Colors.grey,
)),
validator: (value) => value.isEmpty
? 'Phone Number can\'t be empty'
: null,
onSaved: (value) => phoneNumber = value.trim();
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
child: Text("Register Your Device"),
onPressed: () {
if (_formKey.currentState.validate()) {
//_formKey.currentState.save();
Print (_phoneNumber); //NEED TO GET ENTERED VALUE HERE
}
},
),
)
],
),
),
],
),
),
);
});
}
You can remove the await keyword from in front of the function and call it like this:
showDialog({the dialog}).then((dialogResult){
setState((){
yourLocalVariable = dialogResult;
});
});