Flutter/Dart - Dynamically controlling CheckboxListTile inside Listview.Builder - flutter

I am trying to create a page that lists a number of questions. For each question I will have different answers. At the minute, whenever I select an answer, the same option on the other questions are selected at the same time. How can I avoid this and make each question its own entity? Here is my code:
class QuestionScreen extends StatefulWidget {
#override
_QuestionScreenState createState() => _QuestionScreenState();
}
class _QuestionScreenState extends State<QuestionScreen> {
List<bool> _isChecked;
final Map<String, Map> questions = {"milk comes from what animal":
{"horse": false, "monkey": false, "frog": false, "cow": true},
"what colour is the sea?":
{"red": false, "green": false, "blue": true, "yellow": false}};
#override
void initState() {
super.initState();
_isChecked = List<bool>.filled(questions.values.length, false);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
child: Text("questions page"),
),
Expanded(
child: new ListView.builder(
itemCount: questions.keys.length,
itemBuilder: (BuildContext ctxt, int questionTitleIndex) {
return Padding(
padding: const EdgeInsets.all(24.0),
child: Container(
height: MediaQuery.of(context).size.height * 0.45,
decoration: BoxDecoration(
color: OurTheme().ourCanvasColor,
borderRadius: BorderRadius.circular(25),
),
child: Column(
children: [
Text(
questions.keys.toList()[questionTitleIndex],
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.w800),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
shrinkWrap: true,
itemCount: questions.values
.toList()[questionTitleIndex]
.keys
.length,
itemBuilder:
(BuildContext ctxt, int questionAnswersIndex) {
return Container(
decoration: BoxDecoration(border: Border.all()),
child: CheckboxListTile(
title: Text(
"${questionAnswersIndex + 1}. ${questions.values.toList()[questionTitleIndex].keys.toList()[questionAnswersIndex]}",
style: TextStyle(
color: Colors.white, fontSize: 16),
),
value: _isChecked[questionAnswersIndex],
controlAffinity:
ListTileControlAffinity.platform,
onChanged: (bool value) {
setState(
() {
_isChecked[questionAnswersIndex] =
value;
},
);
},
activeColor: OurTheme().ourCanvasColor,
checkColor: Colors.white,
),
);
},
),
)
],
),
),
);
},
),
)
],
),
);
}
}

I see a few problems here.
First since your need to maintain the answer for each question in your _isChecked. It would make more sense to make it a Map<String, String> instead of a List<bool>.
Inside it, the key will be the question title and the value will be the selected option title.
So, inside your initState, you will initiate it liket this.
Map<String, String> _isChecked = {}; // Initializing with empty map
#override
void initState() {
super.initState();
widget.questions.keys.forEach((key) {
// For each question we first set the answer as "". means nothing selected.
_isChecked[key] = "";
// We then loop over the options of that question to see if any option was already true and set it as the initial answer.
for (MapEntry entry in widget.questions[key]!.entries) {
if (entry.value) _isChecked[key] = entry.key;
}
});
}
After this, you just have to change the places in your code where you were using the _isChecked variable.
Here is the link to the full working code. Just replace all your code with the code in the link.
Result.

Related

The dropdown_search library in flutter does not refresh my user list

Hello everyone and thank you for taking the time to try to help me.
I use this library in flutter on mobile only => pub.dev dropdown_search
When I click on the dropdown, I load with an API call a list of users. I want to call them 20 by 20. The first call is done well, I have my first 20 users displayed and when I get to the bottom of my list I have a new API call that goes to display the next 20. This part also works.
My problem is that the list view in the dropdown doesn't refresh and doesn't show me my next 20 users and yet in the console I have my request that leaves with my 20 new users showing. If I close and reopen the dropdown the list is updated.
I contacted the developer who told me first to try to use this => myKey.currentState.reassemble() with my scrollController but it did not work.
He then explained that to solve my problem he could only see the use of a streambuilder. I tried it but it didn't work.
Would you have some hints to give me or something else please?
Here is a bit of what I did in code. In this example I not use StreamBuilder.
final GlobalObjectKey<DropdownSearchState<ListValueOptionList>> myKey = new GlobalObjectKey<DropdownSearchState<ListValueOptionList>>(50);
bool isListValueModify = false;
List<delegationModele.Delegation> listDelegations = [];
int indexDelegationList;
int pageCounter = 0;
ScrollController _scrollController = ScrollController();
List<ListValueOptionList> listValueOptionListModule;
List<ListValueOptionList> listValueOptionListTypeOfDelegation;
List<ListValueOptionList> listValueOptionsUserDelegation;
List userAlreadyUse = [];
#override
void initState() {
super.initState();
_scrollController.addListener(() {
if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
print("Scroll event");
_loadValueOptionListSpecificPeople();
setState(() {});
myKey.currentState.reassemble();
}
});
}
#override
Widget build(BuildContext context) {
return Padding(
/// Allows resize when kerboard is open if
/// padding == MediaQuery.of(context).viewInsets
padding: padding,
child: Container(
decoration:BoxDecoration(
color: appStyleMode.primaryBackgroundColor,
borderRadius: BorderRadius.vertical(top: Radius.circular(25.0)),
),
height: modalHeight,
child: Padding(
padding: const EdgeInsets.only(left:30, top:30.0, right: 30, bottom: 20),
child: ListView(
shrinkWrap: false,
children: [
getModificationItemListDelegationBuilder(),
],
),
),
),
);
}
SingleChildScrollView getModificationItemListDelegationBuilder(){
final appStyleMode = Provider.of<AppStyleModeNotifier>(context);
return SingleChildScrollView(
child: Column(
children: [
///Add specific people
Container(
width: MediaQuery.of(context).size.width,
margin: EdgeInsets.only(bottom: 10, top: 10),
child: Text(
"${getTranslated(context, "my_delegation_specific_user")} : ",
style: TextStyle(
color: specificPeopleisEmpty ? Colors.red : appStyleMode.blackWhiteColor,
fontSize: 16,
),
textAlign: TextAlign.start,
),
),
Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: delegation.userDelegationRecipients.length ?? 0,
itemBuilder: (context, index){
return specificPeople(listValueOptionsUserDelegation, index);
},
),
),
],
),
);
}
Widget specificPeople(List<ListValueOptionList> listValueOptionList, int index){
final appStyleMode = Provider.of<AppStyleModeNotifier>(context);
delegationModele.UserDelegationRecipients userDelegation = delegation.userDelegationRecipients[index];
String optionUserDescription;
if(listValueOptionList != null){
listValueOptionList.removeWhere((element) => userLoad.id.stringOf == element.idValue);
for(ListValueOptionList valueOption in listValueOptionList){
if(valueOption.idValue == userDelegation.delegationUserId){
optionUserDescription = valueOption.listValueDescription;
}
}
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
width: MediaQuery.of(context).size.width * 0.6,
child: DropdownSearch<ListValueOptionList>(
key: myKey,
dropdownButtonProps: DropdownButtonProps(
color: appStyleMode.blackWhiteColor
),
popupProps: PopupProps.menu(
showSelectedItems: true,
menuProps: MenuProps(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
elevation: 5.0,
backgroundColor: appStyleMode.primaryBackgroundColor,
),
textStyle: TextStyle(
color: appStyleMode.blackWhiteColor
),
scrollbarProps: ScrollbarProps(
thumbVisibility: true,
thumbColor: appStyleMode.categorySelectorSelectedBackgroundColor,
interactive: true
),
listViewProps: ListViewProps(
controller: _scrollController,
shrinkWrap: true,
),
interceptCallBacks: true,
itemBuilder: (context, list, isSelected){
return new Container(
child: ListTile(
selected: isSelected,
title: Text(
list?.listValueDescription,
style: TextStyle(
color: appStyleMode.blackWhiteColor
),
),
),
);
},
loadingBuilder: (context, loadingText){
return Align(
alignment: Alignment.topCenter,
child: CircularProgressIndicator(),
);
},
emptyBuilder: (context, text){
return Align(
alignment: Alignment.topCenter,
child: Text(
"${getTranslated(context, "my_delegation_empty_search_user")}",
style: TextStyle(
color: appStyleMode.blackWhiteColor,
),
),
);
},
showSearchBox: true,
searchDelay: Duration(seconds: 1),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
enabled: true,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
borderSide: BorderSide(
color: appStyleMode.categorySelectorSelectedBackgroundColor,
style: BorderStyle.solid
)
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
borderSide: BorderSide(
style: BorderStyle.solid
)
),
prefixIcon: Icon(
Icons.search_rounded,
size: 20,
),
hintText: "${getTranslated(context, "my_delegation_search_hint_text")}",
hintStyle: TextStyle(
fontSize: 13
),
),
style: TextStyle(
color: appStyleMode.blackWhiteColor,
),
),
),
onChanged: (newValue){
//OnChanges is well used
},
compareFn: (value1, value2) => value1.idValue == value2.idValue,
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none
)
),
//items: listValueOptionsUserDelegation,
asyncItems: (filter) async{
return _loadValueOptionListSpecificPeople();
},
itemAsString: (ListValueOptionList list) => list.listValueDescription,
selectedItem: new ListValueOptionList(idValue: userDelegation.delegationUserId, listValueDescription: optionUserDescription),
dropdownBuilder: (context, selectedItem){
if(optionUserDescription == null || optionUserDescription.isEmpty){
return Container();
}else if(optionUserDescription == ""){
return Text("");
}else{
selectedItem.listValueDescription = optionUserDescription;
}
return textValueDescription(selectedItem.listValueDescription, appStyleMode, TextAlign.start);
},
),
),
],
);
}else{
return Container();
}
}
#override
void dispose() {
_scrollController.dispose();
super.dispose();
}
}
I've had a similar app. Maybe this code will help:
class PostsList extends StatefulWidget {
#override
State<PostsList> createState() => _PostsListState();
}
class _PostsListState extends State<PostsList> {
final _scrollController = ScrollController();
#override
void initState() {
super.initState();
_scrollController.addListener(_onScroll);
}
#override
Widget build(BuildContext context) {
return BlocBuilder<PostBloc, PostState>(
builder: (context, state) {
switch (state.status) {
case PostStatus.failure:
return const Center(child: Text('failed to fetch posts'));
case PostStatus.success:
if (state.posts.isEmpty) {
return const Center(child: Text('no posts'));
}
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
return index >= state.posts.length
? const BottomLoader()
: PostListItem(post: state.posts[index]);
},
itemCount: state.hasReachedMax
? state.posts.length
: state.posts.length + 1,
controller: _scrollController,
);
default:
return const Center(child: CircularProgressIndicator());
}
},
);
}
#override
void dispose() {
_scrollController
..removeListener(_onScroll)
..dispose();
super.dispose();
}
void _onScroll() {
if (_isBottom) context.read<PostBloc>().add(PostFetched());
}
bool get _isBottom {
if (!_scrollController.hasClients) return false;
final maxScroll = _scrollController.position.maxScrollExtent;
final currentScroll = _scrollController.offset;
return currentScroll >= (maxScroll * 0.9);
}
}
I've used bloc for this but it should work as well with future builder and using snapshot. Basically user scrolls down, and if scrolled more than 90% of the list another getter is send to get more items from api. If you want to replicate the whole bloc (which I strongly recommend) take look at this documentation.

Flutter - How to select an item (tag) automatically in the UI - List<dynamic>

I am trying to preselect a particular tag shown on the right of the image.
However, I am unable to figure out where to set it. The tags are coming from an API (Postgres backend).
Once it is built to a list of overlay as shown on the right again in the screenshot. I just wanted it to preselect, "Morning", "Evening" or "Daytime" based on the time of the day.
To start off with, I am not able to preselect anything in "selectedTags". This can only be done manually by the user when clicked on a tag.
The method is shared below.
showTagPicker(context, allTags) async {
await showModalBottomSheet(
isDismissible: false,
enableDrag: false,
backgroundColor: Colors.transparent,
isScrollControlled: true,
context: context,
builder: (builder) => Center(
child: Container(
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(10),
),
color: Colors.white,
),
margin: EdgeInsets.all(16),
padding: EdgeInsets.all(24),
child: ListView(
shrinkWrap: true,
children: <Widget>[
Text(
"Please pick your tags",
style: TextStyle(fontSize: 16),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TagPicker(
height: MediaQuery.of(context).size.height * .6,
tags: allTags,
onTagSelected: (_selectedTags) {
selectedTags = _selectedTags;
print("----->");
print(selectedTags);
print("<-----");
},
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
RaisedButton(
color: PRIMARY,
textColor: WHITE,
onPressed: () {
Navigator.of(context).pop();
navigateToAnalysis(context);
},
child: Text("Save"),
),
],
)
],
),
),
),
);
}
I tried, adding "print" to debug and see what and where things are being set but I did not get any further, I have also shown the debug screen if it helps.
Any direction here to preselect one/many tags would be helpful.
Please let me know if I must provide additional details to solve this.
Also, I know there are several things in the code which might be wrong, it is inherited code and I am struggling a bit.
Edit: Including TagPicker. It is not a public library but our widget.
class TagPicker extends StatefulWidget {
const TagPicker(
{Key key, this.height, this.tags, this.onTagSelected, this.selectedTags})
: super(key: key);
#override
TagPickerState createState() => TagPickerState();
final double height;
final List tags;
final List selectedTags;
final Function onTagSelected;
}
class TagPickerState extends State<TagPicker> {
List selectedTags = [];
#override
void initState() {
super.initState();
if (widget.selectedTags != null) {
setState(() {
selectedTags = widget.selectedTags;
});
}
}
#override
Widget build(BuildContext context) {
return widget.tags != null
? Container(
constraints: widget.height != null
? BoxConstraints(maxHeight: widget.height, minHeight: 60)
: BoxConstraints(),
child: SingleChildScrollView(
child: Wrap(
spacing: 0.0,
children: List.generate(
widget.tags.length,
(index) {
return Padding(
padding: const EdgeInsets.only(right: 4.0),
child: ChoiceChip(
selectedColor: PRIMARY,
labelStyle: TextStyle(
fontSize: 12,
color: selectedTags.contains(widget.tags[index])
? WHITE
: Colors.black),
label: Text(widget.tags[index]['tag_name']),
selected: selectedTags.contains(widget.tags[index]),
onSelected: (selected) {
setState(() {
selectedTags.contains(widget.tags[index])
? selectedTags.remove(widget.tags[index])
: selectedTags.add(widget.tags[index]);
widget.onTagSelected(selectedTags);
});
},
),
);
},
),
),
),
)
: Container();
}
}
Pass selectedTags as an argument to TagPicker and modify TagPicker to render an initial set of selected tags. As before onTagSelected callback will provide an updated set.

Why flutter dynamic switch does not changing?

I am trying to change switch, which are created dynamically by using future builder. it does not changing. Following is my script
bool disabledLanguage = false;
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (context, index){
var postId = snapshot.data[index].id;
var postTranslate = snapshot.data[index].translate;
disabledLanguage = (postId == getLanguageId) ? true : false;
return Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: _singleSwitch(postId, postTranslate, disabledLanguage),
);
}
)
and below is function in which I added switch functionality
_singleSwitch(postId, postTranslate, disabledLanguage){
return Row(
children: [
Text(
postTranslate,
style: TextStyle(
color: Color(0xff41453c),
fontSize: 16,
fontWeight: FontWeight.w700
)
),
Switch(
value: disabledLanguage,
onChanged: (value){
setState(() {
disabledLanguage=value;
});
},
activeTrackColor: Colors.lightGreenAccent,
activeColor: Colors.green,
)
],
);
}
When I try to switch any language, it does not changing, i mean if english by default enabled, when I click on it, it should change to disable. It is not happening. Can someone please help me. Thank you
You need to set postId value to getLanguageId instead of setting disabledLanguage.
Copy the below code to DartPad and try. Go through the code once, You'll understand.
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: MyApp(),
));
}
class MyApp extends StatefulWidget {
#override
_State createState() => _State();
}
class _State extends State<MyApp> {
var getLanguageId = 1;
#override
Widget build(BuildContext context) {
var data = [
{
'id': 1,
'translate': 'Eng',
},
{
'id': 2,
'translate': 'Kan',
}
];
return Scaffold(
appBar: AppBar(
title: Text('Flutter Tutorial - googleflutter.com'),
),
body: getListView(data)
);
}
getListView(data) {
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: data.length,
itemBuilder: (context, index) {
var postId = data[index]['id'];
var postTranslate = data[index]['translate'];
var isdisabled = (postId == getLanguageId) ? true : false;
return Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: _singleSwitch(postId, postTranslate, isdisabled),
);
});
}
_singleSwitch(postId, postTranslate, isdisabled) {
print('#Im here for' + postTranslate);
return Row(
children: [
Text(postTranslate,
style: TextStyle(
color: Color(0xff41453c),
fontSize: 16,
fontWeight: FontWeight.w700)),
Switch(
value: isdisabled,
onChanged: (value) {
setState(() {
getLanguageId = postId;
});
},
activeTrackColor: Colors.lightGreenAccent,
activeColor: Colors.green,
)
],
);
}
}
The problem is here:
setState(() {
disabledLanguage=value;
});
disabledLanguage is a function argument. You are updating a function argument and not an instance variable!
Declare an instance variable disabledLanguage,
and remove disabledLanguage from parameter list so that the value is persisted after the function call.
It should look something like this:
class MyWidgetState extends State<MyWidget> {
...
...
bool disabledLanguage = true; //<-- Declared as instance variable
_singleSwitch(postId, postTranslate){ //<-- Removed from parameter list
return Row(
children: [
Text(
postTranslate,
style: TextStyle(
color: Color(0xff41453c),
fontSize: 16,
fontWeight: FontWeight.w700
)
),
Switch(
value: disabledLanguage,
onChanged: (value){
setState(() {
disabledLanguage=value;
});
},
activeTrackColor: Colors.lightGreenAccent,
activeColor: Colors.green,
)
],
);
}
}

SwitchListTile and Container or RaisedButton within ListView.builder not working properly

I have a Dialog class in which I want to show different designations that could be assigned to an employee.
In the beginning, I tried to use only a RaisedButton to select the desired designations. Within the App, the Button should change Colors. This part is found within a StatefulWidget.
I also tried a modified version, where I created a new StatefulWidget only for the Dialog part but this part did not have any effect, thus I thought to implement a SwitchListTile to do the same thing.
The SwitchListTile gets activated and deactivated although only the true value gets registered. This means that when I deactivate (swipe to left) the code does not go within the following setState:
setState(() { hEnabled[hDesignations[index].designation] = value; });
Also when the hEnabled Map gets changed within the setState method the following code does not re-run to change the color of the container:
color: hEnabled[hDesignations[index].designation] ? Colors.green : Colors.grey,
Part with the Dialog:
Widget buildChooseDesignations(
BuildContext context, List<Designation> hDesignations) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadiusDirectional.circular(8.0),
),
child: _buildDialogChild(context, hDesignations),
);
}
_buildDialogChild(BuildContext context, List<Designation> hDesignations) {
//todo: when editing an employee I need the chosen designations (have to pass a list)
Map<String, bool> hEnabled = new Map<String, bool>();
for (var i = 0; i < hDesignations.length; i++) {
hEnabled[hDesignations[i].designation] = false;
}
return Container(
height: 200.0,
//todo: width not working properly
width: 50,
child: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: hDesignations.length,
itemBuilder: (context, index) {
return Row(
children: <Widget>[
Expanded(
child: Container(
width: 10,
color: hEnabled[hDesignations[index].designation]
? Colors.green
: Colors.grey,
padding: EdgeInsets.only(left: 80),
child: Text(hDesignations[index].designation,
style: TextStyle(fontWeight: FontWeight.bold),),
),
),
Expanded(
child: SwitchListTile(
value: hEnabled[hDesignations[index].designation],
onChanged: (bool value) {
setState(() {
hEnabled[hDesignations[index].designation] =
value;
});
}),
)
],
);
}),
),
SizedBox(
height: 15.0,
),
RaisedButton(
color: Colors.blueGrey,
child: Text(
'set',
style: TextStyle(color: Colors.white),
),
onPressed: () {
//todo: save the 'newly' selected designations in a list on set click
},
)
],
),
);
}
The Dialog is called when I click on the Add + FlatButton and looks like this:
ButtonTheme(
height: 30.0,
// child: Container(),
child: FlatButton(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
color: Colors.blueGrey.shade200,
onPressed: () {
//todo add Dialog
// List<Designation> hList = state.designations;
showDialog(
context: context,
builder: (context) => buildChooseDesignations(
context, state.designations));
// DesignationDialog(
// designations:state.designations));
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0)),
child: Text(
'Add +',
style: TextStyle(color: Colors.black),
),
),
),
Found the problem :)
First I did re-write everything into a new StatefulWidget. This I needed since I want that my widget gets re-build after I click on the SwitchListTile to re-color my Container.
Then I had to move my hEnabled (re-named hChecked) map outside the state. The reason was that the widget would re-build all the everything including the initialization of this map, making the user's input useless.
The same applies to the RaisedButton Widget.
Here is my code:
class DesignationDialog extends StatefulWidget {
final List<Designation> designations;
final Map<String, bool> hChecked;
DesignationDialog({Key key, this.designations, this.hChecked}) : super(key: key);
#override
_DesignationDialogState createState() => _DesignationDialogState();
}
class _DesignationDialogState extends State<DesignationDialog> {
_buildDialogChild(BuildContext context, List<Designation> hDesignations) {
//todo: when editing an employee I need the chosen designations (have to pass a list)
// for (var i = 0; i < hDesignations.length; i++) {
// hChecked[hDesignations[i].designation] = false;
// }
return Container(
height: 200.0,
child: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: hDesignations.length,
itemBuilder: (context, index) {
// return ButtonTheme(
// //todo: fix the width of the buttons is not working
// minWidth: 20,
// child: RaisedButton(
// color: widget.hChecked[hDesignations[index].designation]
// ? Colors.green
// : Colors.grey,
// child: Text(hDesignations[index].designation),
// onPressed: () {
// //todo mark designation and add to an array
// setState(() {
// widget.hChecked[hDesignations[index].designation] =
// !widget
// .hChecked[hDesignations[index].designation];
// });
// },
// ),
// );
// -- With Switch
return Row(
children: <Widget>[
Expanded(
child: Container(
child: Text(hDesignations[index].designation),
width: 10,
color: widget.hChecked[hDesignations[index].designation]
? Colors.green
: Colors.grey,
)),
Expanded(
child: SwitchListTile(
value: widget.hChecked[hDesignations[index].designation],
onChanged: (bool value) {
setState(() {
widget.hChecked[hDesignations[index].designation] =
value;
});
}),
)
],
);
// -- end
}),
),
SizedBox(
height: 15.0,
),
RaisedButton(
color: Colors.blueGrey,
child: Text(
'set',
style: TextStyle(color: Colors.white),
),
onPressed: () {
//todo: save the 'newly' selected designations in a list on set click
},
)
],
),
);
}
#override
Widget build(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadiusDirectional.circular(8.0),
),
child: _buildDialogChild(context, widget.designations),
);
}

How to pass data from alertdialog to page in flutter?

I am passing data from my alertdialog to item of listview.
Where I can find information about it?
I tried use TextEditingController, with Navigation Routes. And I used materials from this Tutorial
This is my code:
class MeasurementsScreen extends StatefulWidget {
#override
_MeasurementsScreenState createState() => _MeasurementsScreenState();
}
class _MeasurementsScreenState extends State<MeasurementsScreen> {
List<_ListItem> listItems;
String lastSelectedValue;
var name = ["Рост", "Вес"];
var indication = ["Введите ваш рост", "Введите ваш вес"];
TextEditingController customcintroller;
void navigationPageProgrammTrainingHandler() {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => ProgrammTrainingHandler()),
);
}
#override
void initState() {
super.initState();
initListItems();
}
Future<String> createAlertDialog(BuildContext context, int indexAl){
customcintroller = TextEditingController();
if(indexAl < 2){
return showDialog(context: context, builder: (context){
return AlertDialog(
title: Text(name[indexAl]),
content: TextField(
textDirection: TextDirection.ltr,
controller: customcintroller,
style: TextStyle(
color: Colors.lightGreen[400],
fontSize: 18.5),
decoration: InputDecoration(
contentPadding: EdgeInsets.only(bottom: 4.0),
labelText: indication[indexAl],
alignLabelWithHint: false,
),
keyboardType: TextInputType.phone,
textInputAction: TextInputAction.done,
),
actions: <Widget>[
FlatButton(
child: const Text('ОТМЕНА'),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: const Text('ОК'),
onPressed: () {
Navigator.of(context).pop(customcintroller.text.toString());
},
),
],
);
});
} else if (indexAl > 1){
navigationPageProgrammTrainingHandler();
}
}
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Color(0xff2b2b2b),
appBar: AppBar(
title: Text(
'Замеры',
style: new TextStyle(
color: Colors.white,
),),
leading: IconButton(
icon:Icon(Icons.arrow_back),
color: Colors.white ,
onPressed:() => Navigator.of(context).pop(),
),
),
body: ListView.builder(
physics: BouncingScrollPhysics(),
itemCount: listItems.length,
itemBuilder: (BuildContext ctxt, int index){
return GestureDetector(
child: listItems[index],
onTap: () {
createAlertDialog(context, index).then((onValue){
});
}
);
},
),
);
}
void initListItems() {
listItems = [
new _ListItem(
bgName: 'assets/images/soso_growth.jpg',
name: customcintroller.text.toString().isEmpty == false ? customcintroller.text.toString() : "Рост",
detail: "Нажми, чтобы добавить свой рост"),
new _ListItem(
bgName: 'assets/images/soso_weight.jpg',
name: customcintroller.text.toString().isEmpty == false ? customcintroller.text.toString() : "Вес",
detail: "Нажми, чтобы добавить свой вес"),
new _ListItem(
bgName: 'assets/images/soso_chest.jpg',
name: "Грудь",
detail: "PRO-версия"),
new _ListItem(
bgName: 'assets/images/soso_shoulder.jpg',
name: "Плечи",
detail: "PRO-версия"),
new _ListItem(
bgName: 'assets/images/soso_biceps.jpg',
name: "Бицепс",
detail: "PRO-версия")
];
}
}
class _ListItem extends StatelessWidget {
_ListItem({this.bgName, this.name, this.detail});
// final int index;
final String bgName;
final String name;
final String detail;
#override
Widget build(BuildContext context) {
return Container(
height: 180.0,
margin: const EdgeInsets.symmetric(
vertical: 1.0,
),
child: new Stack(
children: <Widget>[
new Container(
decoration: new BoxDecoration(
image: new DecorationImage(
image: new AssetImage(bgName),
colorFilter: new ColorFilter.mode(
Colors.black.withOpacity(0.45), BlendMode.darken),
fit: BoxFit.cover,
alignment: Alignment.center),
),
child: new SizedBox.expand(
child: Container(
alignment: Alignment.center,
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new Text(
name,
style: new TextStyle(fontSize: 29.0, color: Colors.white),
),
Padding(
padding: const EdgeInsets.only(top: 12.0),
child: new Text(
detail,
style:
new TextStyle(fontSize: 16.0, color: Colors.white),
),
)
],
),
),
),
),
],
),
);
}
}
I expect to get text from alertdialog.
Do you want to update data in your current screen or in another Scaffold/screen? Solution for the first option would be to set your desired data in a setState or pass it to a Model (with ScopedModel) and update the view. E.g. :
FlatButton(
child: const Text('ОТМЕНА'),
onPressed: () {
setState(() {
myData = data;
Navigator.of(context).pop();
}); // not sure if this will work, could you try?
}
),
If it is in a new/other screen, you can pass it in the Navigator like for example:
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NewScreen(data: myData),
),
);
},
Does this solve your problem? Otherwise, please elaborate on what it is you need.
I tried to use Navigator.of(context).pop("OK"); in the AlertDialog but it doesn't work.
use ValueChanged as a param on the showDialog method and it works well.
class Dialogs{
static showAlertDialog(BuildContext context, String title, String message,
{List<String> actions, ValueChanged onChanged}) async {
if (actions == null) {
actions = ["OK"];//default OK button.
}
await showDialog(
context: context,
child: AlertDialog(
title: Text(title ?? 'Message'),
content: Text(message),
actions: actions
.map((e) => new FlatButton(
child: Text(e.trim()),
onPressed: () {
Navigator.of(context).pop(e.trim());
if (onChanged != null) {
onChanged(e.trim());
}
}))
.toList(),
),
);
}
}
the caller looks like
_onProcess(){
String result = "";
await Dialogs.showAlertDialog(context, "Warning", "Warning message", actions: ["OK", "Cancel"],
onChanged: (value) {
result = value;
});
if (result != "OK") {
Dialogs.showSnackBarMessage(context, "Cancel This PTN");
return;
}
....process with the OK logic.
}
If you want to change an item of the list you can do that with setState() before calling Navigator.pop(). It is not possible to pass data through Navigator.pop() though, as you may be able to do with Navigator.push(... MyPage(data)).
You could achieve what you want through State management. You can check for state management tutorials in general. But the two practices that are used most in my opinion are Scoped Model and BLoC pattern. These practices help you pass data through the widget tree back and forward. I would recommend BLoC pattern there are many tutorials about it. Also there is a package which can be very helpful with BLoC pattern: flutter_bloc.