The StateNotifier with the gender does not update as the dropdown choice changes. I have these providers at the beginning of the file:
class GenderController extends StateNotifier<String>{
GenderController(String state) : super(state);
}
final profileProvider = FutureProvider.autoDispose((ref) {
final details = ref.watch(authToken);
var data = API().staffProfile(token: details['token'], staffID: details['ID']);
return data;
});
final gender = StateNotifierProvider.autoDispose((ref) => GenderController(""));
And this is what the build method looks like in a ConsumerWidget:
Widget build(BuildContext context, WidgetRef ref) {
var dropdownValue = ref.watch(gender);
final details = ref.watch(profileProvider);
return details.when(
data: (data){
dropdownValue = data['gender'];
// show the form with the info
return Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: formKey,
child: Column(
children: [
DropdownButton2(
isExpanded: true,
underline: Container(
color: kDarkGrey,
height: 1.0,
),
buttonPadding: const EdgeInsets.symmetric(vertical: 10.0),
hint: const CustomText(
text: "Gender",
fontSize: 16.0,
color: kBlack,
),
items: genders
.map((item) => DropdownMenuItem<String>(
value: item,
child: Text(
item,
style: const TextStyle(
fontSize: 16,
color: kBlack,
),
overflow: TextOverflow.ellipsis,
),
))
.toList(),
value: dropdownValue == "" ? null : dropdownValue.toString(),
onChanged: (value) {
ref.watch(gender.notifier).state = value!;
}
),
],
)
),
);
},
error: (err, _){
debugPrint(_.toString());
return const Center(
child: CustomText(
text: "Error getting profile",
),
);
},
loading: () => Center(
child: CircularProgressIndicator(
color: kPrimary,
),
)
);
}
I have also tried making the data type of the dropdownValue to be final and then assigning the data to it as ref.watch(gender.notifier).state = data['gender']; but that resulted in a "At least listener of the StateNotifier Instance of 'GenderController' threw an exception when the notifier tried to update its state." error. Please help.
In callbacks, you must call ref.read
ref.read(gender.notifier).state = data['gender'];
I figured it out. I initialised the dropdown before calling the details.when like this:
DropdownButton2 dropdown = DropdownButton2(
isExpanded: true,
underline: Container(
color: kDarkGrey,
height: 1.0,
),
buttonPadding: const EdgeInsets.symmetric(vertical: 10.0),
hint: const CustomText(
text: "Gender",
fontSize: 16.0,
color: kBlack,
),
items: genders
.map((item) => DropdownMenuItem<String>(
value: item,
child: Text(
item,
style: const TextStyle(
fontSize: 16,
color: kBlack,
),
overflow: TextOverflow.ellipsis,
),
))
.toList(),
value: dropdownValue == "" ? null : dropdownValue.toString(),
onChanged: (value) {
ref.read(gender.notifier).state = value!;
}
);
And then I updated the StateNotifier after the data was returned in the FutureProvider like so:
final profileProvider = FutureProvider.autoDispose((ref) async {
final details = ref.watch(authToken);
var data = await API().staffProfile(token: details['token'], staffID: details['ID']);
ref.read(gender.notifier).state = data['gender'];
return data;
});
Everything works fine now, and as it should.
Related
I'm trying to display a result item following the user input in the text field, but I receive all the items. There were some methods I tried, but they didn't work and I encountered some errors.
here is my source code
import 'dart:convert';
import 'package:ebook_flutter_app/constant.dart';
import 'package:ebook_flutter_app/model/image.dart';
import 'package:ebook_flutter_app/model/text_value.dart';
import 'package:ebook_flutter_app/screens/show_item.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:persistent_bottom_nav_bar/persistent-tab-view.dart';
import '../widgets/showImage.dart';
class SearchScreen extends StatefulWidget {
const SearchScreen({Key? key}) : super(key: key);
#override
SearchScreenState createState() => SearchScreenState();
}
class SearchScreenState extends State<SearchScreen> {
List textValues = [];
List original = [];
List result = [];
TextEditingController txtQuery = TextEditingController();
List<TextValue> textValueList = [];
List<MyImage> myImageList = [];
List<TextValue> getCatList(List<TextValue> inputList, String query) {
List<TextValue> outputList =
inputList.where((item) => item.title == query).toList();
//textValueList = outputList;
//var myList = outputList;
return outputList;
}
List<MyImage> getImageList(List<MyImage> inputList, String query) {
List<MyImage> outputList =
inputList.where((o) => o.id_num!.toString() == query).toList();
// myImageList = outputList;
return outputList;
}
#override
void initState() {
super.initState();
txtQuery.addListener(() {
if (isNumeric(txtQuery.text) == true) {
loadImage();
searchById(txtQuery.text);
print('I\'m using search option for loading Image.... ');
} else {
loadData();
search(txtQuery.text);
print('I\'m using search option for loading Data....');
}
});
}
void loadData() async {
String jsonStr = await rootBundle.loadString('assets/db/text_value.json');
var json = jsonDecode(jsonStr);
textValues = json;
original = json;
setState(() {});
}
void loadImage() async {
String jsonStr = await rootBundle.loadString('assets/db/image_db.json');
var json = jsonDecode(jsonStr);
textValues = json;
original = json;
print('load Image is running....');
setState(() {});
}
void search(String query) {
if (query.isEmpty) {
textValues = original;
setState(() {});
return;
}
query = query.toLowerCase();
print(query);
//List result = [];
textValues.forEach((element) {
var name = element["name"].toString().toLowerCase();
var description = element["description"].toString().toLowerCase();
if (name.contains(query) || description.contains(query)) {
result.add(element);
// textValueList.add(element);
// print('textValueList is $textValueList');
}
});
textValues = result;
setState(() {});
}
void searchById(String query1) {
if (query1.isEmpty) {
textValues = original;
print('query1 is .....$query1');
setState(() {});
return;
}
print('query1 is $query1');
//List result = [];
textValues.forEach((element) {
var id_num = element["id_num"].toString();
var img_num = element["img_num"].toString();
if (id_num.contains(query1)) {
result.add(element);
// myImageList.add(element);
// print('mYImageList is $myImageList');
print('result is......$result');
}
});
textValues = result;
print('textValues is .....$textValues');
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(2),
body: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.all(10),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
controller: txtQuery,
onChanged: (value) {
setState(() {});
},
textDirection: TextDirection.rtl,
decoration: InputDecoration(
hintText: "جست وجو...",
hintTextDirection: TextDirection.rtl,
hintStyle: TextStyle(
color: Colors.black,
fontSize: 18,
fontFamily: 'iran-sans-ds',
decoration: TextDecoration.none,
fontStyle: FontStyle.italic,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(4.0)),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black)),
prefixIcon: const Icon(Icons.search),
suffixIcon: IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
txtQuery.text = '';
txtQuery.clear();
},
),
),
keyboardType: TextInputType.text,
),
],
),
),
txtQuery.text.isEmpty
? Container()
: Expanded(
child: ListView.builder(
itemCount: textValues.length,
// isNumeric(txtQuery.text) == true
// ? getImageList(myImageList, txtQuery.text).length
// : getCatList(textValueList, txtQuery.text).length,
itemBuilder: (context, index) {
var textVal = textValues[index];
String description = textVal['description'] ??
'we don\'t have description......';
var id_num = textVal['id_num'].toString() ??
'we don\'t have id_num......';
var img_num = textVal['img_num'].toString() ??
'we don\'t have img_num........... ';
print('id_num is ....$id_num'
' img_num is.....$img_num');
return Card(
margin:
const EdgeInsets.fromLTRB(8.0, 4.0, 8.0, 4.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0)),
color: Colors.blue[50],
child: Theme(
data: Theme.of(context)
.copyWith(dividerColor: Colors.transparent),
child: InkWell(
onTap: (() => pushNewScreen(
context,
screen: isNumeric(id_num) == false
? ShowItem(
name: textVal['name'],
description:
textVal['description'],
)
: ShowImage(
title: id_num,
image: Myasset(img_num),
),
withNavBar:
true, // OPTIONAL VALUE. True by default.
pageTransitionAnimation:
PageTransitionAnimation.slideRight,
)),
child: ExpansionTile(
title: Text(
isNumeric(id_num) == false
? textVal['name']
: id_num,
textDirection: TextDirection.rtl,
style: const TextStyle(
fontSize: 20.0, color: Colors.black54),
),
childrenPadding: const EdgeInsets.only(
bottom: 20.0,
right: 20.0,
left: 20.0,
top: 5.0),
children: [
isNumeric(id_num) == false
? Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
const Text(
'بیشتر',
textDirection:
TextDirection.rtl,
textAlign: TextAlign.justify,
style: TextStyle(
color: Colors.blue,
fontWeight:
FontWeight.bold),
),
Text(
'${description.substring(0, 39)} ...',
textDirection:
TextDirection.rtl,
textAlign: TextAlign.justify,
style: TextStyle(
color: Colors.black),
),
])
: Image.asset(
Myasset(img_num),
fit: BoxFit.cover,
width: MediaQuery.of(context)
.size
.width *
0.01,
height: MediaQuery.of(context)
.size
.height *
0.01,
),
],
),
),
),
);
}),
)
]),
);
}
}
Widget _listView(text_value) {
return Expanded(
child: ListView.builder(
itemCount: text_value.length,
itemBuilder: (context, index) {
var textVal = text_value[index];
String description =
textVal['description'] ?? 'we don\'t have description......';
var id_num =
textVal['id_num'].toString() ?? 'we don\'t have id_num......';
var img_num = textVal['img_num'].toString() ??
'we don\'t have img_num........... ';
print('id_num is ....$id_num' ' img_num is.....$img_num');
return Card(
margin: const EdgeInsets.fromLTRB(8.0, 4.0, 8.0, 4.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0)),
color: Colors.blue[50],
child: Theme(
data:
Theme.of(context).copyWith(dividerColor: Colors.transparent),
child: InkWell(
onTap: (() => pushNewScreen(
context,
screen: isNumeric(id_num) == false
? ShowItem(
name: textVal['name'],
description: textVal['description'],
)
: ShowImage(
title: id_num,
image: Myasset(img_num),
),
withNavBar: true, // OPTIONAL VALUE. True by default.
pageTransitionAnimation:
PageTransitionAnimation.slideRight,
)),
child: ExpansionTile(
title: Text(
isNumeric(id_num) == false ? textVal['name'] : id_num,
textDirection: TextDirection.rtl,
style:
const TextStyle(fontSize: 20.0, color: Colors.black54),
),
childrenPadding: const EdgeInsets.only(
bottom: 20.0, right: 20.0, left: 20.0, top: 5.0),
children: [
isNumeric(id_num) == false
? Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
const Text(
'بیشتر',
textDirection: TextDirection.rtl,
textAlign: TextAlign.justify,
style: TextStyle(
color: Colors.blue,
fontWeight: FontWeight.bold),
),
Text(
'${description.substring(0, 39)} ...',
textDirection: TextDirection.rtl,
textAlign: TextAlign.justify,
style: TextStyle(color: Colors.black),
),
])
: Image.asset(
Myasset(img_num),
fit: BoxFit.cover,
width: MediaQuery.of(context).size.width * 0.01,
height: MediaQuery.of(context).size.height * 0.01,
),
],
),
),
),
);
}),
);
}
I didn't use getCatList & myImageListmethods because I encountered to error when I was using this methods.
How can I fix it?
Inside addListener you're calling loadData() every single time the user enter a character. Try this:
void initState() {
super.initState();
loadData();
txtQuery.addListener(() {
search(txtQuery.text);
});
}
The second thing you could try is use for in instead of forEach
void search(String query) {
if (query.isEmpty) {
textValues = [];
setState(() {});
return;
}
setState(() {
textValues = [
for (var item in yourDataSource)
if (item['key'].contains(query)) item
];
});
}
And you can use textValues in your list
body: ListView.builder(
itemCount: textValues.length,
You can use autocomplete textfield for your problem
check this plugin :
https://pub.dev/packages/auto_complete_search
You don't have any function for onChanged (TextFormField's argument), let's try this:
First edit your search() function
void search(String query) {
if (query.isEmpty) {
setState(() {
textValues = original;
});
return;
}
setState(() {
textValues = original.where( (element) =>
element['name'].contains(query.toLowerCase()).toString().toLowerCase() ||
element['description'].contains(query.toLowerCase()).toString().toLowerCase()
).toList();
});
}
Second edit onChanged argument
onChanged: (value) => search(value)
I'm making a contract terms page. Checkbox ListTile is being used.
There is a total agreement and six items.
If the first to fifth items of the Terms and Conditions are selected, the Elevated Button color should be changed to blue and made available to move home (screenshot).
If all items are selected, the Elevated Button color should be changed to blue and made to be able to move home (screenshot).
Other than that, Elevated Button is grey and cannot be moved home (screenshot).
I tried it, but it's so hard to set the index for each item
Is there a solution?
Thank you to those who answered.
class TermsAgreementPage extends StatelessWidget {
TermsAgreementPage({
Key? key,
}) : super(key: key);
CheckBoxState checkBoxState = Get.put(CheckBoxState(''));
final alltermsAndConditions = CheckBoxState('');
final termsAndConditions = [
CheckBoxState('이용 약관 동의(필수)', '니어엑스 서비스 이용 통합 약관입니다.'),
CheckBoxState('개인정보 처리방침 동의(필수)', '개인정보보호 포털 법률에 의거한 제공동의로 필수 사항입니다.'),
CheckBoxState('개인정보 제3자 제공동의(필수)', '개인정보보호 포털 법률에 의거한 제공 동의로 필수 사항입니다.'),
CheckBoxState('위치기반 서비스 이용약관(필수)', '주변 가게들 검색에 사용됩니다.'),
CheckBoxState('전자금융거래 이용약관(필수)', '구매 또는 결제 사항이 있을 경우 제공 동의로 필수 사항입니다.'),
CheckBoxState('니어엑스 혜택 알림 동의(선택)','미선택 시 주변가게 할인 및 만기 다가오는 쿠폰 알림 사용 불가.')
];
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Scaffold(
body: Padding(
padding: const EdgeInsets.only(top: 80.0, bottom: 40),
child: Column(
children: [
Expanded(
flex: 1,
child: SizedBox(
width: size.width,
child: Column(
children: const [
Text(
'이용 약관 동의',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
Text(
'아래의 약관에 동의 하신 후 서비스를 이용해 주시기 바랍니다.',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.grey),
),
],
),
),
),
Expanded(
flex: 6,
child: Obx(
() => ListView(
children: [
buildGroupCheckbox(CheckBoxState('전체동의')),
Divider(color: Colors.grey[300], height: 5, thickness: 3),
...termsAndConditions.map(buildCheckbox).toList(),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: termsAndConditions.every((termsAndConditions) => termsAndConditions.isChecked.value)
? Colors.blue
: Colors.grey[400],
padding: const EdgeInsets.symmetric(
horizontal: 150, vertical: 10),
textStyle: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w400,
color: Colors.white)),
onPressed: () {
if(alltermsAndConditions.isChecked.value) {
Get.toNamed('/home');
}
},
child: const Text('시작하기'),
),
],
),
),
),
],
),
),
);
}
Widget buildGroupCheckbox(CheckBoxState checkBoxState) {
return CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
secondary: TextButton(
onPressed: () {
Get.toNamed('/home');
},
child: const Text(
'전문보기',
style: TextStyle(fontSize: 10.0, color: Colors.blue),
)),
title: Text(
checkBoxState.title,
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
),
onChanged: toggleCheckBox,
value: alltermsAndConditions.isChecked.value,
);
}
void toggleCheckBox(bool? value) {
if (value == null) return;
alltermsAndConditions.isChecked.value = value;
for (var termsAndConditions in termsAndConditions) {
termsAndConditions.isChecked.value = value;
}
}
Widget buildCheckbox(CheckBoxState checkBoxState) {
return CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
title: Text(
checkBoxState.title,
style: const TextStyle(fontSize: 12),
),
subtitle: Text(
checkBoxState.subTitle,
style: const TextStyle(fontSize: 9, color: Colors.grey),
),
onChanged: (value) {
checkBoxState.isChecked.value = value!;
alltermsAndConditions.isChecked.value = termsAndConditions.every(
(termsAndConditions) => termsAndConditions.isChecked.value);
},
value: checkBoxState.isChecked.value // checkBoxState 의 isChecked 값임 (false 상태)
);
}
}
class CheckBoxState extends GetxController {
RxBool isChecked = false.obs;
String title;
String subTitle;
CheckBoxState(this.title,[this.subTitle = '']);
}
It's possible to use getRange to get the items from 0 to 4 (The first 5 items). And then, just iterate over every one of them to check if all of them are checked.
Just change the line:
primary: termsAndConditions.every((termsAndConditions) => termsAndConditions.isChecked.value)
? Colors.blue
: Colors.grey[400],
To the following:
primary: termsAndConditions.getRange(0, 4).every(
(termsAndConditions) => termsAndConditions.isChecked.value)
? Colors.blue
: Colors.grey[400],
And also the onPressed property from:
onPressed: () {
if (allTermsAndConditions.isChecked.value) {
Get.toNamed('/home');
}
},
To the following:
onPressed: () {
if (termsAndConditions.getRange(0, 4).every(
(termsAndConditions) =>
termsAndConditions.isChecked.value)) {
Get.toNamed('/home');
}
},
I'm making a page of terms and conditions.
I'm using checkboxListTile.
enter image description here
However, the Elevated Button shall be activated when selecting all items from the first to fifth items of the termsAndConditions.
The last sixth item may or may not be selected.
Otherwise, Elevated Button is disabled and the color should appear gray.
enter image description here
and Elevated Button must be activated when alltermsAndConditions are selected.
enter image description here
It’s too difficult.
Is there a solution?
I'd like to thank the people who answer.
class TermsAgreementPage extends StatelessWidget {
TermsAgreementPage({
Key? key,
}) : super(key: key);
CheckBoxState checkBoxState = Get.put(CheckBoxState(title: '', subTitle: ''));
final alltermsAndConditions = CheckBoxState(title: '전체동의', subTitle: '');
final termsAndConditions = [
CheckBoxState(
title: '이용 약관 동의(필수)',
subTitle: '니어엑스 서비스 이용 통합 약관입니다.'),
CheckBoxState(
title: '개인정보 처리방침 동의(필수)',
subTitle: '개인정보보호 포털 법률에 의거한 제공동의로 필수 사항입니다.'),
CheckBoxState(
title: '개인정보 제3자 제공동의(필수)',
subTitle: '개인정보보호 포털 법률에 의거한 제공 동의로 필수 사항입니다.'),
CheckBoxState(
title: '위치기반 서비스 이용약관(필수)',
subTitle: '주변 가게들 검색에 사용됩니다.'),
CheckBoxState(
title: '전자금융거래 이용약관(필수)',
subTitle: '구매 또는 결제 사항이 있을 경우 제공 동의로 필수 사항입니다.'),
CheckBoxState(
title: '니어엑스 혜택 알림 동의(선택)',
subTitle: '미선택 시 주변가게 할인 및 만기 다가오는 쿠폰 알림 사용 불가.'),
];
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Scaffold(
body: Padding(
padding: const EdgeInsets.only(top: 80.0, bottom: 40),
child: Column(
children: [
Expanded(
flex: 1,
child: Container(
width: size.width,
child: Column(
children: const [
Text(
'이용 약관 동의',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
Text(
'아래의 약관에 동의 하신 후 서비스를 이용해 주시기 바랍니다.',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.grey),
),
],
),
),
),
Expanded(
flex: 6,
child: Obx(
() => ListView(
children: [
buildGroupCheckbox(
CheckBoxState(title: '전체동의', subTitle: '')),
const Divider(color: Colors.grey, height: 2),
...termsAndConditions.map(buildCheckbox).toList()
],
),
),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
padding:
const EdgeInsets.symmetric(horizontal: 50, vertical: 10),
textStyle: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w400,
color: Colors.white)),
onPressed: () {
},
child: const Text('시작하기'),
),
],
),
),
);
}
Widget buildGroupCheckbox(CheckBoxState checkBoxState) {
return CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
secondary: TextButton(
onPressed: () {
Get.toNamed('/home');
},
child: const Text(
'전문보기',
style: TextStyle(fontSize: 10.0, color: Colors.blue),
)),
title: Text(
checkBoxState.title,
style: const TextStyle(fontSize: 12),
),
subtitle: Text(
checkBoxState.subTitle,
style: const TextStyle(fontSize: 9, color: Colors.grey),
),
onChanged: toggleCheckBox,
value: alltermsAndConditions.isChecked.value,
);
}
void toggleCheckBox(bool? value) {
if (value == null) return;
alltermsAndConditions.isChecked.value = value;
for (var termsAndConditions in termsAndConditions) {
termsAndConditions.isChecked.value = value;
}
}
Widget buildCheckbox(CheckBoxState checkBoxState) {
return CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
// 왼쪽에 네모 박스 위치
title: Text(
checkBoxState.title,
style: const TextStyle(fontSize: 12),
),
subtitle: Text(
checkBoxState.subTitle,
style: const TextStyle(fontSize: 9, color: Colors.grey),
),
onChanged: (value) {
checkBoxState.isChecked.value = value!;
alltermsAndConditions.isChecked.value = termsAndConditions.every(
(termsAndConditions) => termsAndConditions.isChecked.value);
},
value: checkBoxState
.isChecked.value
);
}
}
********** controller **********
class CheckBoxState extends GetxController {
RxBool isChecked = false.obs;
final String title; // CheckBoxListTile 의 타이틀 제목
final String subTitle; // CheckBoxListTile 의 서브타이틀 내용
CheckBoxState({
required this.title,
required this.subTitle,
});
}
enter code here
First of all you should change this line :
termsAndConditions.map(buildCheckbox).toList()
to this :
termsAndConditions.asMap().forEach((key, value) {
buildCheckbox(value,key);
});
//or if this does work you add .toList()
termsAndConditions.asMap().forEach((key, value) {
buildCheckbox(value,key);
}).toList();
then you change the buildCheckbox function to include the key/index of the list you are passing
Widget buildGroupCheckbox(CheckBoxState checkBoxState,int index) {
...
}
then you need to add an array of booleans to keep track of the checkboxes
final checkBoxState= [false,false,false,false,false,false];
now you are implementing something like this :
onChanged: (value) {
//new code
if (value){checkBoxState[index]=true;}
else {checkBoxState[index]=false;}
checkBoxState.isChecked.value = value!;
alltermsAndConditions.isChecked.value = termsAndConditions.every(
(termsAndConditions) => termsAndConditions.isChecked.value);
},
Finally you can each individual checkbox statue and can do anything you want :
onPressed: (!sumof5first())?null:() {
//does something
},
}
bool sumof5first(){
int sum = 0;
for (int i=0;i<5;i++){
if (checkBoxState[i]==true){sum++}
}
if (sum>4){return true}
else {return false}
}
I am trying to develop a survey form using Flutter and I have multiple dropdown fields in the form. I want to get the selected values from those dropdowns when I click the save button. But all I am getting is the value I initially set inside initState(). The code I am using is as below. Any help to get this sorted out is much appreciated.
class _EditSurveyState extends State<EditSurvey> {
String contactMethod;
String country;
List contactMethodList = ['phone', 'email', 'mail'];
List countryList = ['us', 'uk', 'germany'];
#override
void initState() {
super.initState();
contactMethod = surveryData['contact'];
country = surveryData['country'];
}
#override
Widget build(BuildContext context) {
return Scaffold(
return Scaffold(
children: [
Expanded(
flex: screenWidth(context) < 1300 ? 10 : 8,
child: SafeArea(
child: Column(
children: [
createDropdownField("Contact", contactMethod, contactMethodList),
createDropdownField("Country", country, countryList),
Row(mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(
onPressed: () async {
print(contactMethod + country);
},
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(horizontal: 50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)
)
),
child: Text(
"UPDATE",
style: TextStyle(
color: Colors.white,
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
)
),
],
),
]
)
)
)
]
)
);
}
Row createDropdownField(String labelText, String _valueChoose, List valueList) {
return Row (
children: [
SizedBox(height: 25,),
Align(
alignment: Alignment.centerLeft,
child: Text(
'$labelText',
),
),
DropdownButtonFormField(
value: _valueChoose,
hint: Text("$labelText"),
icon: Icon(Icons.arrow_drop_down),
isExpanded: true,
onChanged: (newValue){
setState(() {
_valueChoose = newValue;
});
},
items: valueList.map((valueItem){
return DropdownMenuItem(
value: valueItem,
child: Text(valueItem),
);
}).toList(),
),
],
);
}
}
I don't understand why you using intitstate if you want to initialize value to String you can do it while declaring, try removing initstate and
Declare a variable first where you will store new value from dropdown onchange
i.e
class _EditSurveyState extends State<EditSurvey> {
String _currentValue;
DropdownButtonFormField(
onChanged: (val) =>
setState(() => _currentValue = val as String),
value: _currentValue ,
items: YourList.map((item) {
return DropdownMenuItem(
value: item,
child: Text('$item Items'),
);
}).toList(),
),
I have a dropdown button which works fine, but when I try to set a default value it will fail with the following error:
'package:flutter/src/material/dropdown.dart': Failed assertion: line 620 pos 15: 'items == null || items.isEmpty || value == null || items.where((DropdownMenuItem item) => item.value == value).length == 1': is not true.
This is my dropdown button:
Widget changeWorkspace() {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
Padding(
padding: EdgeInsets.all(8.0),
child: DropdownButton<AssignedWorkspace>(
isExpanded: true,
hint: Text("SELECT WORKSPACE"),
value: selectedWorkspace,
onChanged: (dropdownValueSelected) {
setState(() {
selectedWorkspace = dropdownValueSelected;
});
},
items: workspaces != null && workspaces.length > 0
? workspaces.map((AssignedWorkspace workspace) {
return new DropdownMenuItem<AssignedWorkspace>(
value: workspace,
child: new Text(workspace.name,
style: new TextStyle(color: Colors.black)),
);
}).toList()
: null),
),
]);
});
}
I've tried to set the value of selectedWorkspace onInit as follows but it fails.
selectedWorkspace = new AssignedWorkspace(
id: userSettings.currentWorkspaceId,
name: userSettings.currentWorkspaceName);
Is there a way of setting a default value in a dropdown button?
import 'package:flutter/material.dart';
import '../config/app_theme.dart';
class DropdownWidget extends StatefulWidget {
final String title;
final List<String> items;
final ValueChanged<String> itemCallBack;
final String currentItem;
final String hintText;
DropdownWidget({
this.title,
this.items,
this.itemCallBack,
this.currentItem,
this.hintText,
});
#override
State<StatefulWidget> createState() => _DropdownState(currentItem);
}
class _DropdownState extends State<DropdownWidget> {
List<DropdownMenuItem<String>> dropDownItems = [];
String currentItem;
AppTheme appTheme;
_DropdownState(this.currentItem);
#override
void initState() {
super.initState();
for (String item in widget.items) {
dropDownItems.add(DropdownMenuItem(
value: item,
child: Text(
item,
style: TextStyle(
fontSize: 16,
),
),
));
}
}
#override
void didUpdateWidget(DropdownWidget oldWidget) {
if (this.currentItem != widget.currentItem) {
setState(() {
this.currentItem = widget.currentItem;
});
}
super.didUpdateWidget(oldWidget);
}
#override
Widget build(BuildContext context) {
appTheme = AppTheme(Theme.of(context).brightness);
return Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 6),
child: Text(
widget.title,
style: appTheme.activityAddPageTextStyle,
),
),
Container(
padding: EdgeInsets.symmetric(vertical: 3, horizontal: 15),
margin: EdgeInsets.only(top: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
color: Colors.white,
boxShadow: [
BoxShadow(
offset: Offset(0, 2),
blurRadius: 10,
color: Color(0x19000000),
),
],
),
child: DropdownButtonHideUnderline(
child: DropdownButton(
icon: appTheme.activityAddPageDownArrowSVG,
value: currentItem,
isExpanded: true,
items: dropDownItems,
onChanged: (selectedItem) => setState(() {
currentItem = selectedItem;
widget.itemCallBack(currentItem);
}),
hint: Container(
child: Text(widget.hintText, style: appTheme.hintStyle),
),
),
),
),
],
),
);
}
}
This is my dropDownWidget without optimization. It has currentItem. You could use it like:
DropdownWidget(
title: kStatus,
items: state.customerStepInfo.statusList,
currentItem: status,
hintText: kCommonPick,
itemCallBack: (String status) {
this.status = status;
},
)
You need implement "equals" in class AssignedWorkspace. I used equatable package.
Example class AssignedWorkspace
class AssignedWorkspace extends Equatable {
final String id;
final String name;
AssignedWorkspace(this.id, this.name);
#override
List<Object> get props => [id];
}
For me id of one of the element is null, once added id is made non-null issue got fixed.
I changed the value of the dropdown var to 1 initially
var _value = '1';
So when the dropdown button has to display its value it displays the one whose value I have set 1 as in the items list in DropDownButton
DropdownButton(
underline: Container(),
onChanged: (value) {
setState(() {
_value = value;
});
},
value: _value,
items: [
DropdownMenuItem(
value: "1",
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Icon(MaterialCommunityIcons.devices),
SizedBox(width: 10),
Text(
"Consumption",
style: TextStyle(
fontSize: 18.0, fontWeight: FontWeight.w600),
),
],
),
),
DropdownMenuItem(
value: "2",
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Icon(MaterialCommunityIcons.solar_panel),
SizedBox(width: 10),
Text(
"Generation",
style: TextStyle(
fontSize: 18.0, fontWeight: FontWeight.w600),
),
],
),
),
],
),
if you want to see only an initial value you can use hint text named parameter of drop down button and set a text widget. i dont know whether it is a good practice or not.