Flutter setState not updating a particular widget - flutter

I am using multi_select_flutter to show multi chips in my flutter project. However, am trying to add more items to the chip with data loaded from my api while the user is searching but it doesn't update the state of the multi chip widget. Here is my full code.
Even when I hot reload, it does not update itself. The only way I can manually enforce a refresh update is to re-ad the widget and reload consecutively. Any information as to why this strange behavior happens?
class MultiChoiceComponent extends StatefulWidget {
const MultiChoiceComponent({Key key}) : super(key: key);
#override
_MultiChoiceComponentState createState() => _MultiChoiceComponentState();
}
class _MultiChoiceComponentState extends State<MultiChoiceComponent> {
ApiMethods apiMethods;
TextEditingController searchController = TextEditingController();
List<MultiSelectItem<dynamic>> labels = [];
final TextEditingController textEditingController = TextEditingController();
#override
void initState() {
// TODO: implement initState
super.initState();
apiMethods = new ApiMethods(context: context);
}
#override
Widget build(BuildContext context) {
return ListView(
shrinkWrap: true,
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Text(
'Search and select as many symptoms been experienced, it helps with a more accurate diagnosis',
textAlign: TextAlign.start,
style: TextStyle(
fontSize: 14,
height: 1.8,
fontWeight: FontWeight.w400,
fontFamily: 'Euclid',
color: AppColors.grey1,
),
),
),
SizedBox(
height: 20,
),
Container(
padding: EdgeInsets.symmetric(horizontal: 16,),
// height: maxLines * 50.0,
child: TextField(
controller: textEditingController,
cursorColor: Colors.black26,
onChanged: (val){
if(val!=""){
searchSymptoms(val);
}
},
decoration: InputDecoration(
filled: true,
hintStyle: TextStyle(
fontSize: 14,
height: 1.6,
fontWeight: FontWeight.w400,
fontFamily: 'Euclid',
color: AppColors.grey1,
),
hintText: "Search for a symptom/illness",
fillColor: Colors.white,
contentPadding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 14),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12.0),
borderSide: BorderSide(color: AppColors.textColor.withOpacity(0.5)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12.0),
borderSide: BorderSide(color: AppColors.textColor.withOpacity(0.5)),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12.0),
borderSide: BorderSide(color: AppColors.textColor.withOpacity(0.5)),
)),
),
),
MultiSelectChipField(
scroll: false,
decoration: BoxDecoration(
border: Border.all(color: Colors.transparent, width: 1.8),
),
showHeader: false,
selectedTextStyle: TextStyle(
fontSize: 12.5,
height: 1.8,
fontWeight: FontWeight.w400,
fontFamily: 'Euclid',
color: Colors.white,
),
chipShape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(12),
),
),
selectedChipColor: AppColors.customGreen,
chipColor: Colors.white,
textStyle: TextStyle(
fontSize: 12.5,
height: 1.8,
fontWeight: FontWeight.w400,
fontFamily: 'Euclid',
color: AppColors.textColor,
),
items: labels,
onTap: (value) {
///add symptoms to list
print(value);
selectedSymptoms = value as List<Conditions>;
},
),
],
);
}
Future<void> searchSymptoms(String searchQuery) async {
List<dynamic> _data = await apiMethods.searchConditions(symptomCheckerAge, searchQuery);
conditions = Conditions.fromJsonList(_data);
labels = conditions.map((e) => MultiSelectItem(e, e.label)).toList();
setState(() {});
}
}

I tried this given Chip Widget with custom data and it is working properly.. Meaning the items that I am adding on pressed are being added to the new list. Take a look and try out the few changes and see if that works for you
class _MyHomePageState extends State<MyHomePage> {
static List<Animal> _animals = [
Animal(id: 1, name: "Lion"),
Animal(id: 2, name: "Flamingo"),
Animal(id: 3, name: "Hippo"),
Animal(id: 4, name: "Horse"),
Animal(id: 5, name: "Tiger"),
Animal(id: 6, name: "Penguin"),
Animal(id: 7, name: "Spider"),
Animal(id: 8, name: "Snake"),
Animal(id: 9, name: "Bear"),
Animal(id: 10, name: "Beaver"),
Animal(id: 11, name: "Cat"),
Animal(id: 12, name: "Fish"),
Animal(id: 13, name: "Rabbit"),
];
dynamic _items = _animals
.map((animal) => MultiSelectItem<Animal>(animal, animal.name))
.toList();
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.white,
appBar: new AppBar(
title: new Text('Hello'),
),
body: ListView(
scrollDirection: Axis.vertical,
physics: NeverScrollableScrollPhysics(),
children: [
Container(
child: MultiSelectChipField(
scroll: false,
decoration: BoxDecoration(
border: Border.all(color: Colors.transparent, width: 1.8),
),
showHeader: false,
selectedTextStyle: TextStyle(
fontSize: 12.5,
height: 1.8,
fontWeight: FontWeight.w400,
color: Colors.blue,
),
chipShape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(12),
),
),
selectedChipColor: Colors.green,
chipColor: Colors.yellow,
textStyle: TextStyle(
fontSize: 12.5,
height: 1.8,
fontWeight: FontWeight.w400,
color: Colors.black,
),
items: _items,
onTap: (value) {
///add symptoms to list
print(value);
},
),
),
ElevatedButton(
onPressed: () {
_items.add(MultiSelectItem<Animal>(
Animal(id: 27, name: 'name'), 'new'));
setState(() {});
},
child: Text('Add'))
],
),
);
}
}
class Animal {
final int id;
final String name;
Animal({
required this.id,
required this.name,
});
}

Try this
Future<void> searchSymptoms(String searchQuery) async {
List<dynamic> _data = await apiMethods.searchConditions(symptomCheckerAge, searchQuery);
conditions = Conditions.fromJsonList(_data);
labels = conditions.map((e) => MultiSelectItem(e, e.label)).toList();
setState(() {});
}

I was also struggling with hot reload of the MultiSelectChipField and the solution was to update the globalkey during reload.
Pseudo code:
late var _multiSelectKey;
late List<MultiSelectItem<SomeClass?>>? _items;
#override
void initState() {
_items = null;
}
void reloadChip()
{
_multiSelectKey = GlobalKey<FormFieldState>();
_items = getItemsFromSomeWhere();
}
In scaffold the chip has these params
MultiSelectChipField<SomeClass?>(
initialValue: getSelected(),
key: _multiSelectKey,
title: const Text("Title"),
items: _items!,
onTap: (items) async { },
},
),
When function reloadChip() is called and a setState(() {}); is triggered the MultiSelectChipField will update.

Related

How to make Dropdown Button Seprate class andd use in any where in flutter project?

I make Seprate DropDownButton custom StatelessWidget class but its
give me error.
Error=>
There should be exactly one item with [DropdownButton]'s value:
Instance of 'Company'. Either zero or 2 or more [DropdownMenuItem]s
were detected with the same value
'package:flutter/src/material/dropdown.dart': Failed assertion: line
882 pos 15: 'items == null || items.isEmpty || value == null ||
items.where((DropdownMenuItem item) {
return item.value == value;
}).length == 1'
**
Code
**
This is my custom class
import "package:flutter/material.dart";
class MyDropDown<T> extends StatelessWidget {
List<DropdownMenuItem<T>> items;
final T value;
String hintText;
ValueChanged<T?> onChanged;
MyDropDown(
{Key? key,
required this.items,
required this.value,
required this.hintText,
required this.onChanged})
: super(key: key);
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 10.0),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.grey,
width: 0.3,
),
borderRadius: BorderRadius.all(
Radius.circular(
30.0,
),
),
),
child: Row(
children: [
Expanded(
// T
child: DropdownButton<T>(
hint: Text(
hintText,
style: TextStyle(),
),
isExpanded: true,
value: value,
items: items,
onChanged: onChanged,
underline: Container(),
),
)
],
),
);
}
}
=> This is my model class
class Company {
int? cId;
String? cName;
Company({ this.cId, this.cName});
}
=> Where i am use this class
import 'package:flutter/material.dart';
import 'package:sqllite_chart/widget/custom_dropdown_widget.dart';
import 'package:sqllite_chart/widget/custom_textformfield_widget.dart';
class MachineDataAddDialog extends StatefulWidget {
MachineDataAddDialog({Key? key}) : super(key: key);
#override
State<MachineDataAddDialog> createState() => _MachineDataAddDialogState();
}
class _MachineDataAddDialogState extends State<MachineDataAddDialog> {
TextEditingController textEditingControllerMId = TextEditingController();
TextEditingController textEditingControllerMName = TextEditingController();
TextEditingController textEditingControllerMTypeId = TextEditingController();
TextEditingController textEditingControllerMTypeName =
TextEditingController();
TextEditingController textEditingControllerCWGId = TextEditingController();
TextEditingController textEditingControllerCWGName = TextEditingController();
TextEditingController textEditingControllerMDesc = TextEditingController();
List<Company> companies = [
Company(cId: 1, cName: 'ABC'),
Company(cId: 2, cName: 'PQR'),
Company(cId: 3, cName: 'RST'),
Company(cId: 4, cName: 'GFI'),
Company(cId: 5, cName: 'XYZ')
];
List<DropdownMenuItem<Company>> companyListDropDownItems = [];
Company? selectedCompany;
int selectedCompanyId = 1;
String selectedCompanyTitle = 'ABC';
List<DropdownMenuItem<Company>> buildCompanyList(List company) {
List<DropdownMenuItem<Company>> items = [];
for (Company companyList in company) {
items.add(
DropdownMenuItem(
value: companyList,
child: Row(
children: [
Text(
'${companyList.cName}',
style: TextStyle(
color: Color.fromRGBO(49, 87, 110, 1.0),
),
),
],
),
),
);
}
return items;
}
void onChangeActivityListDropDownItem(Company? selected) {
setState(() {
selectedCompany = selected!;
selectedCompanyId = selected.cId!;
selectedCompanyTitle = selected.cName!;
});
}
#override
void initState() {
selectedCompany=Company();
companyListDropDownItems = buildCompanyList(companies);
super.initState();
}
#override
Widget build(BuildContext context) {
return AlertDialog(
title: const Align(
alignment: Alignment.bottomCenter,
child: Text("Enter Machine Details",
style: TextStyle(
shadows: [
Shadow(
color: Color.fromRGBO(49, 87, 110, 1.0),
offset: Offset(0, -5))
],
color: Colors.transparent,
decorationColor: Color.fromRGBO(49, 87, 110, 1.0),
decoration: TextDecoration.underline,
decorationThickness: 1,
decorationStyle: TextDecorationStyle.double,
)),
),
content: SingleChildScrollView(
child: Column(
children: [
CustomTextFormFieldWidget(
controller: textEditingControllerMId,
keyboardType: TextInputType.number,
hintText: "Enter Machine Id",
),
SizedBox(
height: 5
),
CustomTextFormFieldWidget(
controller: textEditingControllerMName,
keyboardType: TextInputType.text,
hintText: "Enter Machine Name",
),
SizedBox(
height: 5
),
CustomTextFormFieldWidget(
controller: textEditingControllerMTypeId,
keyboardType: TextInputType.number,
hintText: "Enter Machine Type ID",
),
SizedBox(
height: 5
),
CustomTextFormFieldWidget(
controller: textEditingControllerMTypeName,
keyboardType: TextInputType.text,
hintText: "Enter Machine Type Name",
),
SizedBox(
height: 5
),
MyDropDown<Company>(
hintText: 'Company',
value: selectedCompany!,
items: companyListDropDownItems,
onChanged:onChangeActivityListDropDownItem,
),
SizedBox(
height: 5
),
CustomTextFormFieldWidget(
controller: textEditingControllerCWGId,
keyboardType: TextInputType.number,
hintText: "Enter CWG Id",
),
SizedBox(
height: 5
),
CustomTextFormFieldWidget(
controller: textEditingControllerCWGName,
keyboardType: TextInputType.text,
hintText: "Enter CWG Name",
),
SizedBox(
height: 5
),
CustomTextFormFieldWidget(
controller: textEditingControllerMDesc,
keyboardType: TextInputType.text,
hintText: "Enter Machine Description",
maxLines: 2,
),
],
),
),
actions: [
Row(
children: [
Expanded(
flex: 4,
child: MaterialButton(
onPressed: () {
Navigator.of(context).pop();
},
color: Color.fromRGBO(49, 87, 110, 1.0),
child: Text("CANCEL", style: TextStyle(color: Colors.white)),
),
),
SizedBox(
width: 10,
),
Expanded(
flex: 4,
child: MaterialButton(
onPressed:() {},
child: Text("ADD", style: TextStyle(color: Colors.white)),
color: Color.fromRGBO(49, 87, 110, 1.0),
),
)
],
),
],
);
}
}
Problem is while creating your companylist to pass to your custom widget, you pass companyList as a value, which you can not do because value expects String?. You need to find a unique string that belongs to that company
DropdownMenuItem(
value: companyList,
child: Row(
children: [
Text(
'${companyList.cName}',
style: TextStyle(
color: Color.fromRGBO(49, 87, 110, 1.0),
),
),
],
),

how can I make scrollable and responsive screen flutter

I have implemented a login screen using flutter. the screen is fully functional and working properly with validations. I want this screen to be scrollable and responsive to other devices. how can I do that? below I have added my full login screen code. appreciate your help on this.
how to scroll the screen
how to implement responsive screen
class LoginScreen extends StatefulWidget {
const LoginScreen({Key? key}) : super(key: key);
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
#override
Widget build(BuildContext context) {
final Size = MediaQuery.of(context).size;
return GestureDetector(
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Color.fromARGB(255, 3, 86, 124), Color(0xff141a3a)],
begin: Alignment.topRight,
end: Alignment.bottomLeft,
)),
child: Scaffold(
backgroundColor: Colors.transparent,
resizeToAvoidBottomInset: false,
body: Padding(
padding: const EdgeInsets.only(top: 40, left: 20, right: 20),
child: Column(
children: [
Expanded(
flex: 2,
child: Column(
children: [
// Spacer(flex: 1),
Image(
image: const AssetImage(
'assets/images/LogoVector.png',
),
height: Size.width / 2.9,
width: Size.width / 2.9,
),
SizedBox(height: 5),
Text(
"LOGIN",
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 30,
color: textWhite,
fontFamily: "Roboto"),
),
],
),
),
// const Spacer(flex: 1),
const Expanded(flex: 3, child: Center(child: LoginForm())),
// Spacer(
// flex: 1,
// ),
],
),
),
),
),
);
}
}
class LoginForm extends StatefulWidget {
const LoginForm({Key? key}) : super(key: key);
#override
_LoginFormState createState() => _LoginFormState();
}
Map<String, String> loginUserData = {
'email': '',
'password': '',
'id': '',
'userName': '',
'token': '',
'userStatus': '',
};
class _LoginFormState extends State<LoginForm> {
TextEditingController emailEditingController = new TextEditingController();
TextEditingController passwordEditingController = new TextEditingController();
final _formKey = GlobalKey<FormState>();
String email = "";
String password = "";
String username = "";
bool isLoading = false;
bool typing = true;
bool _isObscure = true;
// bool newValue = true;
bool checkedValue = true;
//String fcmToken = '';
Future LoginData() async {
setState(() {
isLoading = true;
typing = false;
});
try {
var response = await Dio().post(BASE_API + 'user/login',
data: {"username": email, "password": password});
if (response.data["status"] == "LoginSuccess") {
setState(() {
isLoading = false;
});
Get.snackbar(
"success",
"logged in successfully",
backgroundColor: buttontext.withOpacity(0.5),
colorText: textWhite,
borderWidth: 1,
borderColor: Colors.grey,
);
Get.to(BottomNavigation());
} else {
setState(() {
isLoading = false;
typing = true;
});
Get.snackbar(
"error",
"No User Found",
backgroundColor: buttontext.withOpacity(0.5),
colorText: textWhite,
borderWidth: 1,
borderColor: Colors.grey,
);
}
print("res: $response");
setState(() {
isLoading = false;
typing = true;
});
} catch (e) {
setState(() {
isLoading = false;
typing = true;
});
Get.snackbar("Error", "Something went wrong.Please contact admin",
backgroundColor: buttontext.withOpacity(0.5),
borderWidth: 1,
borderColor: Colors.grey,
colorText: Colors.white,
icon: Icon(
Icons.error_outline_outlined,
color: Colors.red,
size: 30,
));
print(e);
}
}
#override
Widget build(BuildContext context) {
final Size = MediaQuery.of(context).size;
return Form(
key: _formKey,
autovalidateMode: AutovalidateMode.disabled,
child: Column(
children: [
TextFormField(
controller: emailEditingController,
enabled: true,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(
color: textWhite,
),
// borderSide: BorderSide.none
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(color: textWhite),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(color: Colors.red),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(color: Colors.red),
),
isDense: true,
contentPadding: EdgeInsets.fromLTRB(10, 30, 10, 0),
hintText: "Email/ Username",
hintStyle: TextStyle(
color: textWhite, fontFamily: "Roboto", fontSize: 14),
),
style: TextStyle(color: textWhite),
validator: (String? UserName) {
if (UserName != null && UserName.isEmpty) {
return "Email can't be empty";
}
return null;
},
onChanged: (String? text) {
email = text!;
// print(email);
},
onSaved: (value) {
loginUserData['email'] = value!;
},
),
SizedBox(
height: 10,
),
TextFormField(
controller: passwordEditingController,
obscureText: _isObscure,
enabled: true,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(color: textWhite),
// borderSide: BorderSide.none
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(color: textWhite),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Colors.red),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Colors.red),
),
isDense: true,
contentPadding: EdgeInsets.fromLTRB(10, 10, 10, 0),
suffixIcon: IconButton(
icon: Icon(
_isObscure ? Icons.visibility : Icons.visibility_off),
color: textWhite,
onPressed: () {
setState(() {
_isObscure = !_isObscure;
});
}),
hintText: "Password",
hintStyle: TextStyle(
color: textWhite,
fontFamily: "Roboto",
fontSize: 14,
)),
style: TextStyle(color: textWhite),
validator: (String? Password) {
if (Password != null && Password.isEmpty) {
return "Password can't be empty";
}
return null;
},
onChanged: (String? text) {
password = text!;
print(password);
},
onSaved: (value) {
loginUserData['password'] = value!;
},
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
child: CheckboxListTile(
title: const Text(
"Remember Me",
style: TextStyle(
color: textWhite, fontFamily: "Roboto", fontSize: 14),
),
activeColor: buttontext,
// tileColor: buttontext,
value: checkedValue,
onChanged: (newValue) {
FocusManager.instance.primaryFocus?.unfocus();
setState(() {
if (isLoading != true) {
checkedValue = newValue!;
print(newValue);
}
});
},
contentPadding: EdgeInsets.only(left: 0, top: 0),
controlAffinity:
ListTileControlAffinity.leading, // <-- leading Checkbox
),
),
TextButton(
child: Text(
"Forget Password",
style: TextStyle(
color: textWhite, fontFamily: "Roboto", fontSize: 14),
),
onPressed: () {
Get.to(() => Forget_Screen());
},
)
],
),
SizedBox(height: 40),
isLoading
? SpinKitDualRing(
color: textWhite,
size: 40,
)
: GestureDetector(
child: MainButton("Login"),
onTap: () async {
FocusManager.instance.primaryFocus?.unfocus();
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
await LoginData();
// Get.to(BottomNavigation());
}
},
),
SizedBox(height: 15),
Container(
width: 275.0,
height: 40.0,
child: OutlinedButton(
onPressed: () {
Get.to(() => const Signup_Screen());
},
child: const Text(
'Signup',
style: TextStyle(
fontSize: 15, fontFamily: "Roboto", color: textWhite
//fontWeight: FontWeight.w500,
// color: Colors.black,
),
),
style: OutlinedButton.styleFrom(
side: const BorderSide(
width: 1.0,
color: textWhite,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
// side: BorderSide(width: 2, color: Colors.green),
),
),
),
)
],
),
);
}
}
How to scroll the screen? Use SingleChildScrollView wrap your Column, remember to ristrict the content size to avoid error.
How to implement responsive screen? Simplest is use LayoutBuilder widget that provide constrains as a props. This constrains can provide maxHeight, maxWidth,... You can then rely on it to determine how you build your application (responsive).
Example:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: LayoutBuilder(
builder: (context, constrains){
final maxw = constrains.maxWidth;
String text = '';
if(maxw < 420) {text = 'Smaller than 420';}
else {text = 'Bigger than 420';}
return Center(child: Text('Width is: $maxw, $text'));
}
),
);
}

Can i Select Multiple Accounts in a Banking app and pay them different amounts at the same time in flutter?

import 'package:flutter/material.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'package:keyboard_dismisser/keyboard_dismisser.dart';
import 'package:money_formatter/money_formatter.dart';
import 'package:shukela_app/api/banking_app_api.dart';
import 'package:shukela_app/screens/stokvel_detail.dart';
import 'package:shukela_app/screens/stokvels.dart';
import 'package:shukela_app/utils/constants.dart';
import 'package:shukela_app/utils/global_variables.dart';
import 'package:sizer/sizer.dart';
import 'package:shukela_app/model/bankingapp_model.dart';
import '../utils/user_preferences.dart';
class PayMultipleStokvelScreen extends StatefulWidget {
const PayMultipleStokvelScreen({Key? key}) : super(key: key);
#override
State<PayMultipleStokvelScreen> createState() =>
_PayMultipleStokvelScreenState();
}
TextEditingController txtSearch = TextEditingController();
TextEditingController txtAmount = TextEditingController();
class _PayMultipleStokvelScreenState extends State<PayMultipleStokvelScreen> {
String? selectedType;
bool hasText = false;
String buttonText = "PAY NOW";
bool isLoading = false;
Widget? showHideIcon() {
if (hasText) {
return IconButton(
icon: const Icon(
Icons.clear,
color: AppColors.primaryBlue,
),
onPressed: () {
txtSearch.clear();
setState(() {
hasText = false;
});
},
);
} else {
return null;
}
}
// void _showMultiSelectDialog(BuildContext context) async {
// await showDialog(
// context: context,
// builder: (ctx) {
// return MultiSelectDialog(
// items: _animals.map((e) => MultiSelectItem(e, e)).toList(),
// initialValue: _selectedAnimals,
// onConfirm: (values) {...},
// );
// },
// );
// }
double? balance;
final _formKey = GlobalKey<FormState>();
var selectedValue;
List<StokvelDetail> selectedStokvel = [];
#override
void initState() {
super.initState();
balance = double.parse(UserPreferences.getBalance() ?? '');
}
mf() {
MoneyFormatter mf = MoneyFormatter(amount: balance!);
return mf;
}
StokvelListState currentState = StokvelListState.showAllListState;
#override
Widget build(BuildContext context) => KeyboardDismisser(
gestures: const [GestureType.onTap],
child: SafeArea(
child: Scaffold(
backgroundColor: AppColors.secondaryColor,
appBar: AppBar(
backgroundColor: AppColors.secondaryColor,
elevation: 0,
title: const Text('Pay Multiple Stokvel',
style: screenTitleTextStyle),
leading: IconButton(
icon: const Icon(
Icons.arrow_back_ios,
color: AppColors.primaryBlue,
),
onPressed: () => Navigator.pop(context),
),
),
body: Form(
key: _formKey,
child: Column(
children: [
SizedBox(height: 5.h),
Container(
height: 6.h,
width: 98.w,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: TypeAheadFormField<Stokvel?>(
debounceDuration: const Duration(milliseconds: 500),
hideSuggestionsOnKeyboardHide: false,
suggestionsBoxDecoration: const SuggestionsBoxDecoration(
constraints: BoxConstraints(maxHeight: 450),
color: AppColors.secondaryColor,
borderRadius: BorderRadius.all(Radius.circular(10))),
textFieldConfiguration: TextFieldConfiguration(
style: const TextStyle(
color: AppColors.primaryBlue,
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
controller: txtSearch,
onChanged: (value) {
setState(() {
hasText = true;
});
},
decoration: InputDecoration(
prefixIcon: const Icon(
Icons.search,
color: AppColors.primaryBlue,
),
suffixIcon: showHideIcon(),
hintText: 'Search Stokvel',
border: const OutlineInputBorder(
borderSide:
BorderSide(color: AppColors.primaryBlue),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(4.0),
borderSide:
const BorderSide(color: AppColors.primaryBlue),
),
),
),
validator: (val) {
if (val!.isEmpty) {
return "Please select stokvel";
}
return null;
},
suggestionsCallback: BankingAppApi.getStokvelSuggestions,
itemBuilder: (context, Stokvel? suggestion) {
final stokvel = suggestion!;
//
return ListTile(
trailing: SizedBox(
height: 20.0,
width: 20.0,
child: Container(
color: AppColors.primaryBlue,
),
),
title: Text(
stokvel.stokvelName!,
style: const TextStyle(
fontFamily: Fonts.primaryFont,
fontWeight: FontWeight.bold,
color: AppColors.primaryBlue,
),
),
subtitle: Text(
stokvel.stokvelType!,
style: const TextStyle(
fontFamily: Fonts.primaryFont,
fontWeight: FontWeight.bold,
color: AppColors.primaryBlue),
),
);
},
noItemsFoundBuilder: (context) => const SizedBox(
height: 60,
child: Center(
child: Text(
'No Stokvel Found.',
style: TextStyle(fontSize: 20),
),
),
),
onSuggestionSelected: (Stokvel? suggestion) {
final stokvel = suggestion!;
setState(() {
txtSearch.text = stokvel.stokvelName!;
hasText = true;
});
stokvelID = stokvel.stokvelID;
memberID = stokvel.memberID;
},
),
),
SizedBox(
height: 4.h,
),
SizedBox(height: 3.h),
Container(
height: 6.h,
width: 98.w,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: TextFormField(
controller: txtAmount,
decoration: const InputDecoration(
labelStyle: TextStyle(
color: AppColors.primaryBlue,
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
suffixText: "ZAR",
border: OutlineInputBorder(
borderSide:
BorderSide(color: AppColors.primaryBlue)),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1, color: AppColors.primaryBlue),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1, color: AppColors.primaryBlue),
),
labelText: 'Amount',
contentPadding:
EdgeInsets.only(left: 20, right: 15, bottom: 8),
),
keyboardType: TextInputType.number,
style: const TextStyle(
fontSize: 20.0,
fontFamily: Fonts.primaryFont,
color: AppColors.primaryBlue),
validator: (val) {
if (val!.isEmpty) {
return "Please enter amount";
}
return null;
},
),
),
SizedBox(height: 3.h),
Container(
height: 6.h,
width: 98.w,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: TextField(
readOnly: true,
style: const TextStyle(
fontSize: 18.0, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
decoration: InputDecoration(
hintText: "Wallet Balance : R " + mf().output.nonSymbol,
border: const OutlineInputBorder(
borderSide:
BorderSide(color: AppColors.primaryBlue),
borderRadius: BorderRadius.horizontal()),
focusedBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1, color: AppColors.primaryBlue),
),
enabledBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1, color: AppColors.primaryBlue),
),
),
),
),
SizedBox(height: 3.h),
Container(
height: 50,
width: 400,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: ElevatedButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.white)),
child: isLoading
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
CircularProgressIndicator(
color: AppColors.secondaryColor),
SizedBox(width: 24),
Text(
"Submitting payment...",
style: TextStyle(
color: AppColors.secondaryColor),
)
],
)
: Text(
buttonText,
style: const TextStyle(
fontFamily: Fonts.primaryFont,
fontWeight: FontWeight.bold,
color: AppColors.primaryBlue),
),
onPressed: () {
if (_formKey.currentState!.validate()) {
if (double.parse(txtAmount.text) <= balance!) {
setState(
() {
isLoading = true;
},
);
stokvelTransact.amount =
double.parse(txtAmount.text);
stokvelTransact.memberID = memberID;
stokvelTransact.stokvelID = stokvelID;
stokvelTransact.transactionTypeID = 1;
api
.stokvelDeposit(stokvelTransact,
"StokvelTransaction/StokvelTransact")
.then(
(value) => setState(
() {
Future.delayed(
const Duration(seconds: 3));
isLoading = false;
if (value == "Success") {
ScaffoldMessenger.of(context)
.showSnackBar(snackBar(
content:
'Payment made succesfully',
duration: 5));
} else {
ScaffoldMessenger.of(context)
.showSnackBar(snackBar(
content:
'We have encountered technical problems, Try again later',
duration: 5));
}
},
),
)
.catchError(
(err) {
setState(() {
isLoading = false;
});
ScaffoldMessenger.of(context).showSnackBar(
snackBar(
content: err.toString(), duration: 7));
},
);
} else {
ScaffoldMessenger.of(context).showSnackBar(snackBar(
content: "Insuficient funds!", duration: 7));
}
}
},
),
),
],
),
),
),
),
);
}
The above code is for the single page that I want to achieve this
This is what I want to achieve or any suggestions are accepted, I want to mark the Group of Stokvels I want to pay and pay them different accounts I don't know if it's possible please I'm new in flutter it a project I have to get it done as soon as possible this is the source code for my whole payment page, for now, I'm able to pay a single Stokvel Group but I want to select multiple
I highly suggest starting with formatting of the current code - its hard to read and with that hard to maintain.
The simple idea would be:
use List<StokvelDetail> selectedStokvel = []; you created to add the Stokvel's in every time you select Stokvel in SearchBar
Add Map<int,double> amounts = {}; <StokvelId, amount> to keep track of the amount you want to send to each Stokvel
Based on selectedStokvel build multiple 'amount window' widgets
Before submit verify walletBalance >= amounts.values.reduce((a,b)=> a + b)
For each selectedStokvel call api.stokvelDeposit(stokvelTransact, ...
I would primarly focus on spliting this code to multiple Widget classes, there are many schools but I like to have around 150 lines of code in single file at most.
From what i see your file can be easily split into:
Column(
children: [
SizedBox(),
SearchBar(),
SizedBox(),
AmountInput(), --> StokvelAmountInput() for each selectedStokvel
SizedBox(),
WalletBalance(),
SizedBox(),
SubmitButton(),
]),
```

Drop down widget is not showing in flutter

I am design a screen in which I need to opening a drop down list when user click on textfield till now I am using CupertinoActionSheetAction sheet which is correctly working now I need to replace it with DropdownButton but when I am doing it is not displaying on screen I don't know why.
Here is my code focus on line number 136 (function for CupertinoActionSheetAction is working code) and line 138 where (function for drop not working code) according to me may be it due to build context but I m not sure how to use 'build context' in this context can any one help me out to fix that.
Thanks in advance, And any suggestion for making my code more reusable will be helpful as well :)
import 'dart:ffi';
import 'package:fleet_management/AppContants/Contants.dart';
import 'package:fleet_management/Controllers/NewFuelExpenseController.dart';
import 'package:fleet_management/Models/NewFuelExpenseModel.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class AddFuelView extends StatefulWidget {
const AddFuelView({Key? key}) : super(key: key);
#override
_AddFuelView createState() {
return _AddFuelView();
}
}
// MyTripHistoryView
class _AddFuelView extends State<AddFuelView> {
NewFuelExpenseModel fuelExpense =
NewFuelExpenseController().getNewFuelExpense();
DateTime _selectedDate = DateTime.now();
String dropdownvalue = 'Item 1';
// List of items in our dropdown menu
var items = [
'Item 1',
'Item 2',
'Item 3',
'Item 4',
'Item 5',
];
TextStyle _titleTextStyle = const TextStyle();
TextStyle _valueTextStyle = const TextStyle();
final GlobalKey newFuelExpenseFormField = GlobalKey<FormState>();
final TextEditingController _dateEditingController = TextEditingController();
final TextEditingController _priceTextEditingController =
TextEditingController();
final TextEditingController _gallonsTextEditingController =
TextEditingController();
final TextEditingController _totalTextEditingController =
TextEditingController();
final TextEditingController _odometerTextEditingController =
TextEditingController();
final TextEditingController _fuelTypeTextEditingController =
TextEditingController();
final TextEditingController _vendorTextEditingController =
TextEditingController();
#override
void initState() {
super.initState();
_titleTextStyle =
const TextStyle(fontSize: 16, fontWeight: FontWeight.w600);
_valueTextStyle = const TextStyle(
fontSize: 16, fontWeight: FontWeight.w600, color: Colors.black38);
_dateEditingController.text =
"${_selectedDate.day}-${_selectedDate.month}-${_selectedDate.year}";
}
#override
Widget build(BuildContext context) {
var scaffold = Scaffold(
appBar: AppBar(
title: Text(StringsConstants.newFuelExpense),
backgroundColor: AppColorsConstants.primaryColor,
actions: <Widget>[
FlatButton(
textColor: Colors.white,
onPressed: () {
print("SAVE Button Clicked");
},
child: Text("Save", style: _titleTextStyle),
),
],
),
body: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16),
child: Form(
key: newFuelExpenseFormField,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
showDateRow(context),
addTextFieldInput(
StringsConstants.priceGallon,
"\u{20B9}0.00",
false,
_priceTextEditingController,
TextInputType.datetime,
null,
null),
addTextFieldInput(
StringsConstants.gallons,
"",
false,
_gallonsTextEditingController,
TextInputType.number,
null,
null),
addTextFieldInput(
StringsConstants.total,
"\u{20B9}0.00",
false,
_totalTextEditingController,
TextInputType.number,
null,
null),
addTextFieldInput(
StringsConstants.odometer,
"",
false,
_odometerTextEditingController,
TextInputType.number,
null,
null),
// show action sheet
addTextFieldInput(
StringsConstants.fuelType,
"",
true,
_fuelTypeTextEditingController,
TextInputType.none,
null, () {
// Working. - >>>> HERE <<<<<
showActionSheet(context);
// Not Working >>>> HERE <<<<< Uncomment following function before
// showDropDown();
}),
addTextFieldInput(
StringsConstants.vendor,
"",
false,
_vendorTextEditingController,
TextInputType.text,
null,
null),
const SizedBox(
height: 50,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () {
print("Submit button pressed!");
},
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 24),
primary: AppColorsConstants.primaryColor,
textStyle:
const TextStyle(fontWeight: FontWeight.bold),
),
child: Text(StringsConstants.submit)),
ElevatedButton(
onPressed: () {
print("Cancel button pressed!");
},
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 24),
primary: AppColorsConstants.primaryColor,
textStyle:
const TextStyle(fontWeight: FontWeight.bold),
),
child: Text(StringsConstants.cancel))
],
)
],
),
),
),
),
),
);
return scaffold;
}
void showDropDown() {
DropdownButton<String>(
items: <String>['A', 'B', 'C', 'D'].map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (_) {},
);
}
Future<dynamic> showActionSheet(BuildContext context) {
return showCupertinoModalPopup(
context: context,
builder: (BuildContext context) => CupertinoActionSheet(
title: const Text('Choose Options'),
actions: <Widget>[
CupertinoActionSheetAction(
child: const Text('Oil'),
onPressed: () {
_fuelTypeTextEditingController.text = "Oil";
Navigator.pop(context, 'Oil');
},
),
CupertinoActionSheetAction(
child: const Text('Petrol'),
onPressed: () {
_fuelTypeTextEditingController.text = "Petrol";
Navigator.pop(context, 'Petrol');
},
),
CupertinoActionSheetAction(
child: const Text('diesel'),
onPressed: () {
_fuelTypeTextEditingController.text = "diesel";
Navigator.pop(context, 'diesel');
},
)
],
cancelButton: CupertinoActionSheetAction(
child: const Text('Cancel'),
isDefaultAction: true,
onPressed: () {
Navigator.pop(context, 'Cancel');
},
)),
);
}
Container addTextFieldInput(
String title,
String initialValue,
bool isEditable,
TextEditingController textfieldController,
TextInputType keyboardType,
FormFieldValidator<String>? validator,
GestureTapCallback? tabCallback,
) {
return Container(
padding: const EdgeInsets.fromLTRB(0, 16, 0, 8),
child: SizedBox(
child: TextFormField(
onTap: tabCallback,
keyboardType: keyboardType,
cursorHeight: 16,
cursorWidth: 1.4,
readOnly: isEditable,
controller: textfieldController,
validator: validator,
decoration: InputDecoration(
isDense: true,
floatingLabelStyle:
TextStyle(color: AppColorsConstants.primaryColor),
labelText: title,
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: AppColorsConstants.primaryColor, width: 1.5),
),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black38, width: 1.5),
),
border: const OutlineInputBorder()),
style: const TextStyle(fontSize: 18),
cursorColor: Colors.black38,
showCursor: true,
autofocus: true,
),
),
);
}
Container showDateRow(BuildContext context) {
return Container(
child: TextFormField(
onTap: () => {_selectDate(context)},
cursorHeight: 16,
cursorWidth: 1.4,
readOnly: true,
enableInteractiveSelection: false,
controller: _dateEditingController,
decoration: InputDecoration(
isDense: true,
labelStyle: const TextStyle(color: Colors.orangeAccent),
labelText: StringsConstants.date,
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.orangeAccent, width: 1.5),
),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black38, width: 1.5),
),
border: const OutlineInputBorder()),
style: const TextStyle(fontSize: 18),
cursorColor: Colors.black38,
showCursor: true,
autofocus: true,
),
);
}
Future<void> _selectDate(BuildContext context) async {
final DateTime? picked = await showDatePicker(
context: context,
initialDate: _selectedDate,
firstDate: DateTime(1990),
lastDate: DateTime.now());
if (picked != null && picked != _selectedDate) {
setState(() {
_selectedDate = picked;
this._dateEditingController.text =
"${_selectedDate.day}-${_selectedDate.month}-${_selectedDate.year}";
});
}
}
}
For DropDown in flutter you need
an initail value,
a list to be shown just
String selectedvalue='myvalue';
/// be sure you add the variable value in the 0 index of you list other wise it will thrown error or exception..
List<String> myList=[
'myvalue',/// the value is same as variable value
'othe value', ....
];
DropDownButton(
onChanged: (resultValue){
/// you can get selected value from here
},
items: myList.map((e){
retrun DropdownMenuItem(
value: e,
child: Text(e));
}).toList(),
value: selectedvalue,
),
You can use DropdownButtonFormField in place of TextFormField and can make some style related changes per your requirement.
class MyWidget extends StatelessWidget {
String selectedValue = "USA";
List<DropdownMenuItem<String>> get dropdownItems{
List<DropdownMenuItem<String>> menuItems = [
DropdownMenuItem(child: Text("USA"),value: "USA"),
DropdownMenuItem(child: Text("Canada"),value: "Canada"),
DropdownMenuItem(child: Text("Brazil"),value: "Brazil"),
DropdownMenuItem(child: Text("England"),value: "England"),
];
return menuItems;
}
#override
Widget build(BuildContext context) {
return DropdownButtonFormField(
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue, width: 2),
borderRadius: BorderRadius.circular(20),
),
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue, width: 2),
borderRadius: BorderRadius.circular(20),
),
filled: true,
fillColor: Colors.blueAccent,
),
dropdownColor: Colors.blueAccent,
value: selectedValue,
onChanged: (String? newValue) {
selectedValue = newValue!;
},
items: dropdownItems);
}
}

type '(dynamic) => Null' is not a subtype of 'TextEditingController?'

I've been trying to accept user input in an extracted TextFormField method(Name, contact and email) and then save them to my firestore database using TextEditingController.
Here lies the case after accepting the input, the name and email controller always displays the same input. After saving, even for test purposes, the database always shows that only null values have been returned from the controllers...
Please help me solve this problem...
Snippet of the code:
class Profile extends StatefulWidget {
final String email;
Profile({Key key, this.email}) : super(key: key);
get user => AuthService();
#override
_ProfileState createState() => _ProfileState(email);
}
class _ProfileState extends State<Profile> {
String email;
String contain;
_ProfileState(this.email);
bool showPhone = false;
//Text editing controller
final TextEditingController _emailController = TextEditingController();
final TextEditingController _phoneController = TextEditingController();
final TextEditingController _nameController = TextEditingController();
// initState
#override
void initState() {
super.initState();
_emailController.addListener(() {
final String text = _emailController.text;
_emailController.value = _emailController.value.copyWith(
text: text,
selection:
TextSelection(baseOffset: text.length, extentOffset: text.length),
composing: TextRange.empty,
);
});
_phoneController.addListener(() {
final String contact = _phoneController.text;
_phoneController.value = _phoneController.value.copyWith(
text: contact,
selection: TextSelection(
baseOffset: contact.length,
extentOffset: contact.length,
),
composing: TextRange.empty,
);
});
_nameController.addListener(() {
final String contact = _nameController.text;
_nameController.value = _nameController.value.copyWith(
text: contact,
selection: TextSelection(
baseOffset: contact.length,
extentOffset: contact.length,
),
composing: TextRange.empty,
);
});
}
#override
void dispose() {
// Clean up the controller when the widget is removed from the
// widget tree.
_emailController.dispose();
_phoneController.dispose();
_nameController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
final FirebaseAuth _auth = FirebaseAuth.instance;
bool loading = false;
final _formKey = GlobalKey<FormState>();
final doc = Database();
final ThemeData themeData = Theme.of(context);
String name, email, contact;
return loading
? Loading()
: SafeArea(
child: StreamBuilder<UserData>(
stream: Database(uid: doc.uid).userData,
builder: (context, snapshot) {
if (snapshot.hasData) {
UserData userData = snapshot.data;
return Scaffold(
appBar: AppBar(
backgroundColor: Aqua,
elevation: 1,
title: Text(
'Edit Profile Info',
style: themeData.textTheme.headline1.copyWith(
color: White,
fontSize: 22,
letterSpacing: 0.75,
),
),
),
body: new Container(
height: window.physicalSize.height * 0.85,
padding: const EdgeInsets.only(
left: 16,
right: 16,
top: 25,
),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
new Center(
child: new Stack(
children: [
addVertical(20),
Container(
width: 190,
height: 190,
decoration: BoxDecoration(
border: Border.all(
width: 5,
color: Theme.of(context)
.scaffoldBackgroundColor,
),
boxShadow: [
BoxShadow(
blurRadius: 15,
spreadRadius: 5,
color: Cyan.withOpacity(0.5),
offset: Offset(0, 15),
),
],
shape: BoxShape.circle,
image: DecorationImage(
image: AssetImage(
"assets/images/IMG2.jpg"),
fit: BoxFit.cover,
),
),
),
Positioned(
bottom: 0,
right: 0,
child: Container(
height: 55,
width: 55,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
width: 5,
color: Theme.of(context)
.scaffoldBackgroundColor,
),
color: Cyan,
),
child: IconButton(
icon: Icon(
Icons.edit,
color: Dark_Blue,
),
onPressed:
() {}, // change profile picture
),
),
),
],
),
),
addVertical(30),
Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.only(
top: 25, left: 35, right: 35, bottom: 10),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
buildTextFormField(
"Full Name",
userData.fname ??
"Enter Full Name here...",
false,
false ?? null,
),
addVertical(15),
buildTextFormField(
"Contact",
userData.contact ?? "020 *** ****",
true,
false ?? null,
),
addVertical(15),
buildTextFormField(
"E-mail",
userData.email ?? "emaple123#gmail.com",
false,
true ?? null,
),
],
),
),
),
),
addVertical(35),
Padding(
padding: const EdgeInsets.only(
left: 35, right: 35, top: 25),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
primary: White,
),
onPressed: () {
Navigator.pop(context);
},
child: Text(
"CANCEL",
style: themeData.textTheme.bodyText1
.copyWith(
letterSpacing: 1.25,
fontWeight: FontWeight.bold,
fontSize: 18,
color: Mid_Blue,
),
),
),
ElevatedButton(
onPressed: () async {
if (_formKey.currentState.validate()) {
setState(() => loading = true);
dynamic result =
await _auth.currentUser;
// User person = result.user;
if (result != null) {
await Database(uid: doc.uid)
.updateData(
"$name",
"$email",
"$contact",
);
Toast.show(
"Your details have been successfully updated!",
context,
duration: Toast.LENGTH_LONG,
gravity: Toast.BOTTOM,
);
setState(() => loading = false);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
Homepage()));
} else {
Toast.show(
"Please try again later",
context,
gravity: Toast.BOTTOM,
duration: Toast.LENGTH_LONG,
);
}
}
},
style: ElevatedButton.styleFrom(
elevation: 2,
primary: Colors.green[300],
shape: new RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
), //!elevatedButton background
),
child: Text(
"SAVE",
style: TextStyle(
fontSize: 18,
letterSpacing: 2.2,
color: White,
),
),
),
],
),
),
],
//),
),
),
),
);
} else {
return Loading();
}
},
),
);
}
Widget buildTextFormField(
String labelText,
String placeholder,
bool isPhone,
bool isEmail,
) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 10),
child: TextField(
onSubmitted: isPhone || isEmail
? (value) {
value = _phoneController.toString() != null
? _emailController.toString() != null
: _nameController;
}
: null,
controller: isEmail || isPhone
? (value) {
value = _emailController.toString() != null
? _nameController.toString() != null
: _phoneController;
}
: _emailController,
obscureText: isPhone ? showPhone : false,
decoration: InputDecoration(
suffixIcon: isPhone
? IconButton(
onPressed: () => setState(() => showPhone = !showPhone),
icon: Icon(
Icons.remove_red_eye,
color: Red,
),
)
: null, //!This makes the icon appear only for the password field
contentPadding: EdgeInsets.only(bottom: 3),
labelText: labelText,
labelStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Aqua,
),
floatingLabelBehavior: FloatingLabelBehavior.always,
hintText: placeholder,
hintStyle: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Cyan,
),
),
keyboardType:
isPhone ? TextInputType.phone : TextInputType.emailAddress,
),
);
}
}
This is the function that builds name textField
buildTextFormField(
"Full Name",
userData.fname ?? "Enter Full Name here...",
false, // isPhone
false ?? null, // isEmail
),
From here we can see that isPhone = false; isEmail = false;
This is the function for the and controller in TextFormField:
controller: isEmail || isPhone
? (value) {
value = _emailController.toString() != null
? _nameController.toString() != null
: _phoneController;
}
: _emailController,
Since isEmail and isPhone are both false, it returns _emailController for name Textfield. So name is being updated on email textfield.
The function for onSubmitted() returns null if isEmail & isPhone are both false. Therefore, for name field, it will return null as the value (probably why you are getting null in the database).
I have tried to better organise the code below:
class MyHomePage extends StatefulWidget {
final String email;
MyHomePage({Key key, this.email}) : super(key: key);
get user => AuthService();
#override
_MyHomePageState createState() => _MyHomePageState(email);
}
class _MyHomePageState extends State<MyHomePage> {
String email;
// not used in this class
String contain;
_MyHomePageState(this.email);
bool showPhone = false;
//Text editing controller
final TextEditingController _emailController = TextEditingController();
final TextEditingController _phoneController = TextEditingController();
final TextEditingController _nameController = TextEditingController();
#override
void dispose() {
// Clean up the controller when the widget is removed from the
// widget tree.
_emailController.dispose();
_phoneController.dispose();
_nameController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
final FirebaseAuth _auth = FirebaseAuth.instance;
bool loading = false;
final _formKey = GlobalKey<FormState>();
final doc = Database();
final ThemeData themeData = Theme.of(context);
return loading
? Loading()
: SafeArea(
child: StreamBuilder<UserData>(
stream: Database(uid: doc.uid).userData,
builder: (context, snapshot) {
if (snapshot.hasData) {
UserData userData = snapshot.data;
_emailController.text = userData.email;
_phoneController.text = userData.phone;
_nameController.text = userData.name;
return Scaffold(
appBar: AppBar(
backgroundColor: Aqua,
elevation: 1,
title: Text(
'Edit Profile Info',
style: themeData.textTheme.headline1.copyWith(
color: White,
fontSize: 22,
letterSpacing: 0.75,
),
),
),
body: new Container(
height: window.physicalSize.height * 0.85,
padding: const EdgeInsets.only(
left: 16,
right: 16,
top: 25,
),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
new Center(
child: new Stack(
children: [
addVertical(20),
Container(
width: 190,
height: 190,
decoration: BoxDecoration(
border: Border.all(
width: 5,
color: Theme.of(context)
.scaffoldBackgroundColor,
),
boxShadow: [
BoxShadow(
blurRadius: 15,
spreadRadius: 5,
color: Cyan.withOpacity(0.5),
offset: Offset(0, 15),
),
],
shape: BoxShape.circle,
image: DecorationImage(
image: AssetImage(
"assets/images/IMG2.jpg"),
fit: BoxFit.cover,
),
),
),
Positioned(
bottom: 0,
right: 0,
child: Container(
height: 55,
width: 55,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
width: 5,
color: Theme.of(context)
.scaffoldBackgroundColor,
),
color: Cyan,
),
child: IconButton(
icon: Icon(
Icons.edit,
color: Dark_Blue,
),
onPressed:
() {}, // change profile picture
),
),
),
],
),
),
addVertical(30),
Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.only(
top: 25, left: 35, right: 35, bottom: 10),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
buildTextFormField(
"Full Name",
"Enter Full Name here...",
false,
_nameController,
),
addVertical(15),
buildTextFormField(
"Contact",
"020 *** ****",
true,
_phoneController,
),
addVertical(15),
buildTextFormField(
"E-mail",
"example123#gmail.com",
false,
_nameController,
),
],
),
),
),
),
addVertical(35),
Padding(
padding: const EdgeInsets.only(
left: 35, right: 35, top: 25),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
primary: White,
),
onPressed: () {
Navigator.pop(context);
},
child: Text(
"CANCEL",
style: themeData.textTheme.bodyText1
.copyWith(
letterSpacing: 1.25,
fontWeight: FontWeight.bold,
fontSize: 18,
color: Mid_Blue,
),
),
),
ElevatedButton(
onPressed: () async {
if (_formKey.currentState.validate()) {
setState(() => loading = true);
dynamic result =
await _auth.currentUser;
// User person = result.user;
if (result != null) {
await Database(uid: doc.uid)
.updateData(
_nameController.text,
_emailController.text,
_phoneController.text,
);
Toast.show(
"Your details have been successfully updated!",
context,
duration: Toast.LENGTH_LONG,
gravity: Toast.BOTTOM,
);
setState(() => loading = false);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
Homepage()));
} else {
Toast.show(
"Please try again later",
context,
gravity: Toast.BOTTOM,
duration: Toast.LENGTH_LONG,
);
}
}
},
style: ElevatedButton.styleFrom(
elevation: 2,
primary: Colors.green[300],
shape: new RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
), //!elevatedButton background
),
child: Text(
"SAVE",
style: TextStyle(
fontSize: 18,
letterSpacing: 2.2,
color: White,
),
),
),
],
),
),
],
//),
),
),
),
);
} else {
return Loading();
}
},
),
);
}
Widget buildTextFormField(
String labelText,
String placeholder,
bool isPhone,
TextEditingController controller,
) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 10),
child: TextField(
// onSubmitted: isPhone || isEmail
// ? (value) {
// value = _phoneController.toString() != null
// ? _emailController.toString() != null
// : _nameController;
// }
// : null,
controller: controller,
obscureText: isPhone ? showPhone : false,
decoration: InputDecoration(
suffixIcon: isPhone
? IconButton(
onPressed: () => setState(() => showPhone = !showPhone),
icon: Icon(
Icons.remove_red_eye,
color: Red,
),
)
: null, //!This makes the icon appear only for the password field
contentPadding: EdgeInsets.only(bottom: 3),
labelText: labelText,
labelStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Aqua,
),
floatingLabelBehavior: FloatingLabelBehavior.always,
hintText: placeholder,
hintStyle: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Cyan,
),
),
keyboardType:
isPhone ? TextInputType.phone : TextInputType.emailAddress,
),
);
}
}