Reset MultiSelect in flutter - flutter

I have 2 multiselect, when i select the item in one of them it has to show the selected items in a listview, but when i select the items in the second multiselect the event reset the first one. how can i resolve this problem?
this is the multiselect in witch one the _materialesSeleccionados variable is set, this variable is used by listview to show the information:
this is the body in my scaffold where I show the multiselects and the listview only shows when _materialesSeleccionados is not empty:
return Scaffold(
appBar: AppBar(
title: Center(child: Text("Tareas y Materiales")),
),
body:SingleChildScrollView(
child: Column(
children: [
_selectTareas(),
_selectMateriales(),
_materialesSeleccionados.isEmpty? Column() : Padding(
padding: const EdgeInsets.only(top:20, bottom: 20),
child: listaMateriales(),
),
_buttonFinalizar(context, argumentos['des_id'])
],
),
)
);
MultiSelectBottomSheetField(
initialChildSize: 0.4,
listType: MultiSelectListType.CHIP,
searchable: true,
buttonText: const Text("Seleccione los Materiales"),
title: const Text("Materiales"),
items: _itemsMateriales,
onConfirm: (values) {
setState(() {
_materialesSeleccionados = values;
});
//listaMateriales();
},
chipDisplay: MultiSelectChipDisplay(
chipColor: Colors.white,
textStyle: const TextStyle(color: Colors.black),
),
),
this is the main screen:
this is the screen after to select the item in the first multiselect:
this is the screen of the second multiselect befero to select the items:
when I select the items in the second multiselect these appear in the listview, but the selected items from the first one they desappear, can i to do?

Related

Flutter: Hide or unhide field based on a button without rebuilding the widget

I have a parent widget which has 2 child widgets for input fields. So, I want one field to be hidden in the beginning and by pressing a button to hide or unhide it. I managed to achieve that by wrapping the child widget (input field) in a visibility widget and then adding the button in the parent widget with a function onPressed() to setState() of the variables. However, this approach rebuild the parent widget since the setState happens in the parent level and my problem is that I don't want to lose the values inside the input fields when I press the button. Therefore, I want just to hide or unhide the child widget.
Some parts of code are:
Two input fields inside the LocationSearchDialog Widget
Table(columnWidths: const <int, TableColumnWidth>{
0: FlexColumnWidth(85),
1: FlexColumnWidth(15),
}, children: [
TableRow(
children: [
Padding(
padding:
const EdgeInsets.fromLTRB(15, 0, 15, 10),
child: Visibility(
visible: widget.showFromField,
child: LocationSearchDialog(
labelText: 'From',
addMarker: addStartPointMarker,
currentLocation: widget.currentLocation,
))),
Text('')
//this widget was added just to align the other 2 widgets in the table
],
),
TableRow(
children: [
Padding(
padding:
const EdgeInsets.fromLTRB(15, 0, 15, 0),
child: LocationSearchDialog(
labelText: 'To',
addMarker: addDestinationPointMarker,
currentLocation: widget.currentLocation,
)),
Padding(
padding:
const EdgeInsets.fromLTRB(15, 0, 15, 0),
child: ShowStartField(updateFromFieldVisibility: updateFromFieldVisibility))
],
),
])
The button that needs to update the visibility of the field
Transform.rotate(
angle: 180 * widget.rotateButton / 180,
child: IconButton(
icon: Icon(Icons.arrow_forward_ios),
iconSize: 15,
color: Colors.blue,
tooltip: 'Add pick up point',
onPressed: () {
updateFromFieldVisibility;
},
));
Function to update the visibility
void updateFromFieldVisibility() {
setState(() {
widget.showFromField = !widget.showFromField;
widget.rotateButton = widget.rotateButton == 1.5 ? 0 : 1.5;
});
print(widget.showFromField.toString() + ' ' + widget.rotateButton.toString());
}
All of these widget and functionality exist in one main file which is the stateful parent widget.
Thank you in advance guys!

TextFormField retrieves the focus and reassigns the initial value when the keyboard is closed

I find myself working with TextFormField hand in hand with DropdownButton. There is a strange process going on inside the view. I put the steps below:
To the TexFormField, I'm assigning an initial value: controller: _nameController..text = datumAdministrative.name.
I enter a new value to the TextFormField.
When the DropdownButton is deployed, the keyboard is closed the whole Widget is redrawn, which causes the TextFormField to recover its initial value and does not keep the new value entered.
Is there any way to avoid, that the TextFormField returns to its initial value when selecting DropdownButton? I would appreciate if you could help me with a post or feedback.
Code Scaffold:
return Scaffold(
resizeToAvoidBottomPadding: false,
resizeToAvoidBottomInset: true,
appBar: AppBar(
backgroundColor: ColorsTheme.primary,
leading: GestureDetector(
onTap: () {
Navigator.pushReplacementNamed(context, 'administrativeListData');
},
child: Container(
padding: EdgeInsets.all(16),
child: FaIcon(FontAwesomeIcons.arrowLeft)),
),
centerTitle: true,
title: Text(datumAdministrative.name +
' ' +
datumAdministrative.lastNameFather),
automaticallyImplyLeading: false),
body: Stack(
children: <Widget>[
ImageBackground(),
_sizeBoxHeight(),
Container(
child: Form(
key: formValidator.formKey,
child: ListView(
children: <Widget>[
_containerImageUser(context, datumAdministrative),
_sizeBoxHeight(),
CustomCardExpansionTile(
title: Constants.administrativeData,
icon: FontAwesomeIcons.addressBook,
widget: Container(
padding: EdgeInsets.all(15),
child: responsiveDataAdministrative(context)),
),
CustomCardExpansionTile(
title: Constants.addressInformationAdministrative,
icon: FontAwesomeIcons.houseUser,
widget: Container(
padding: EdgeInsets.all(15),
child: responsiveAddressInformationAdministrative(
context))),
CustomCardExpansionTile(
title: Constants.userDataSystem,
icon: FontAwesomeIcons.idCard,
widget: Container(
padding: EdgeInsets.all(15),
child: responsiveInformationSystemAdministrative(
context))),
],
),
),
),
],
),
);
Code TextFormField:
TextFormField(
controller: _nameController..text = datumAdministrative.name,
keyboardType: TextInputType.text,
textInputAction: null,
textCapitalization: TextCapitalization.sentences,
onChanged: (value) => formValidator.name = value,
validator: (value) {
if (formValidator.fieldValidText(value)) {
return null;
} else {
return Constants.nameAdministrativeMessage;
}
});
I see that you set the value for the text property of TextEditingController inside the build method. So, it will be invoked whenever the widget rebuilds, causing the value for the field back to the initial one.
The docs actually tells about it:
text property
Setting this will notify all the listeners of this
TextEditingController that they need to update (it calls
notifyListeners). For this reason, this value should only be set
between frames, e.g. in response to user actions, not during the
build, layout, or paint phases.
To fix it, you should remove the cascade notation here:
controller: _nameController..text = datumAdministrative.name,
Do it inside initState() instead:
#override
void initState() {
_nameController.text = datumAdministrative.name;
}

How to display a list of items inside a column using a ListTile in Flutter

staff.ulogin is a list returned from a web service. If there is more than one item returned, I need to display of list of those items (displaying the company name). I can get the first item displaying, but I'm not sure how to display the entire list.
I also need the user to be able to tap an item so I can setup that company for use, so I'll need to know what item they chose. Thanks for any help.
if (staff.ulogin.length > 1) {
Alert(
context: context,
title: 'Choose Company',
content: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
//how to display all the items
ListTile(
title: Text(staff.ulogin[0].company),
onTap () {}, // <--- how to get the index of the item tapped
),
],
),
),
buttons: [
DialogButton(
child: Text('Cancel', style: TextStyle(color: Colors.white, fontSize: 20)),
color: kMainColor,
onPressed: () {
Navigator.of(context).pop();
},
),
],
).show();
} else
I believe that the correct way of display a list o items is using a ListView. For this case you can use a ListView.builder like this:
Container(
height: 300.0, // Change as you wish
width: 300.0, // Change as you wish
child: ListView.builder
(
itemCount: staff.ulogin.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(staff.ulogin[index].company),
onTap () {
someFunction(staff.ulogin[index]);
},
),
}
)
)

How can I limit to only five items in the dropdown but still be able to scroll down?

We have a list with 20+ elements that we have to show in a dropdown. The problem is that when we deploy the dropdown, the list covers all the screen. We want to reduce the number of shown elements to about 5 or 6 and be able to scroll down the rest of the list.
We would also like to know if it's possible that the dropDownMenuItems deploy under the dropDownButton.
We have tryed doing ".toList().sublist(0,5)," but this way didnĀ“t allow us to scroll.
Widget _dropdown() {
return new Container(
width: MediaQuery.of(context).size.width * 35 / 100,
height: MediaQuery.of(context).size.height * 6 / 100,
decoration: BoxDecoration(
backgroundBlendMode: BlendMode.darken,
),
child: Theme(
child: DropdownButtonHideUnderline(
child: ButtonTheme(
alignedDropdown: true,
child: new DropdownButton(
hint: Text(
'HINT TEXT',
),
value: selected_item,
onChanged: (newValue) {
setState(
() {
selected_item = newValue;
},
);
},
isDense: false,
isExpanded: true,
items: data.map((location) {
return new DropdownMenuItem<String>(
child: new Container(
child: Align(
alignment: Alignment.centerLeft,
child: new Text(
location.toUpperCase(),
),
),
),
value: location,
);
}).toList(),
),
),
),
),
);
}
Look into this custom DropDown Button. It is the normal Dropdown Button with the possibility to give height to the widget. If you set height = 5* 48.0 (which is the _menuItemHeight) you should only see 5 elements.

How to fix "BoxConstraints forces an infinite width" and how to delete an item from a list?

I am a beginner in Flutter and I can not get the result I want because I have a "layout problem". I tried several things for hours...
Technical problem:
When I click on "add player button" to add a new TextField to set more play name, I have an error: "BoxConstraints forces an infinite width". So, the TextField isn't displayed.
Other features that I do not know how to realize:
- I have a "dropdownmenu' to choose difficulty game. Why the default text isn't displayed (Difficulty: normal)?
- How to do this: When the user clicks on the icon "remove" (see suffixIcon on TextField), then the corresponding fieldText is deleted?
- How to do this: When user click to add player the default HintText is incremented (Player 2, Player 3, Player 4, ....)?
- Why my background gradient doesn't fix when user scroll page? (like in HTML/CSS 'background-attachment: fixed') ?
import 'package:flutter/material.dart';
/// Styles
///
class Homepage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyHomePageState();
}
}
class _MyHomePageState extends State<Homepage> {
String dropdownValue = 'Normal';
List<TextField> _playerList = new List(); //_playerList is my List name
int playerListIndex = 0; //Count the number of times "Add" button clicked
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(children: <Widget>[
// To have a gradient background
new Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
decoration: new BoxDecoration(
gradient: new LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
stops: [0.1, 1],
colors: [
Colors.redAccent[200],
Colors.red[300],
],
tileMode:
TileMode.repeated,
),
),
//I need SingleChildScrollView to haven't "Overflow yellow bar on bottom screen"
child: SingleChildScrollView(
child: Container(
padding: EdgeInsets.fromLTRB(20, 0, 20, 0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
// Box to add big image
SizedBox(
height: 450,
),
// DROP DOWN MENU to choose difficulty modes
InputDecorator(
decoration: InputDecoration(
prefixIcon: const Icon(Icons.games),
filled: true,
),
child: DropdownButtonHideUnderline(
child: new DropdownButton<String>(
value: dropdownValue,
hint: Text('Difficulty normal'),
isDense: true,
items: <String>['Normal', 'Easy', 'Hard']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (String newValue) {
setState(() {
dropdownValue = newValue;
});
},
),
),
),
//Field player name 1. At least I need 1 player
new TextField(
maxLength: 20,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.person),
filled: true,
hintText:"Player 1",
counterText: "",
),
),
//Field for other player name 2, 3, 4, ...
new ListView.builder(
padding: EdgeInsets.all(0),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: _playerList.length,
itemBuilder: (context, index) {
TextField widget = _playerList.elementAt(index);
return widget;
}),
//Button to add other field text to set player name. The user can delete a player on click on "clear icon button"
new Align(
alignment: Alignment.centerRight,
child: FlatButton.icon(
color: Colors.black,
icon: Icon(
Icons.add,
color: Colors.white,
size: 18,
),
label: Text('Add player',
style:
TextStyle(fontSize: 12, color: Colors.white)),
onPressed: () {
setState(() {
playerListIndex++;
_playerList.add(
new TextField(
maxLength: 20,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.person),
suffixIcon: new IconButton(
icon: Icon(Icons.clear),
onPressed: () {
_playerList.removeAt(playerListIndex); //Doesn't work
}),
filled: true,
hintText: "Player $playerListIndex", //Doesn't work i would like Player 2, Player 3, ...
counterText: "",
),
),
);
print(playerListIndex);
print(_playerList);
print(_playerList[0]);
});
}),
),
new ButtonTheme(
minWidth: 350,
height: 45,
child: FlatButton(
color: Colors.black,
child: Text('Play',
style:
TextStyle(fontSize: 18, color: Colors.white)),
onPressed: () {
// Perform some action
},
),
),
],
),
),
),
),
),
]),
);
}
}
I think that I understood what's going on. Let's begin:
First. Your TextField is not showing up and you're getting an error because you didn't set a width and a height for it, so, it's trying to get the maximum size as it can get. Solution for this? Wrap it in a Container widget (or SizedBox) and give it a width and a height. That should fix the first problem.
Second. Your DropDownMenu's hint text (Difficulty Normal) is not showing because you're not passing the parameter "hint" in the DropDown widget, See the hint propierty below.
Third. You want to clear the TextField string when click on a suffix, right? To achieve this, pass a GestureDetector or an IconButton as your suffix component, create a TextEditingController and pass it in the TextField, in the controller section, then, on the onPressed/onTap method (depending on which Widget you'll set) set this: _textController.text.clear() method, inside the setState function. This should fix the problem.
Forth. You want the list of player's string to increase as you add more players right? You have many ways to achieve this, one of them is creating an empty list, you can mock to test it, and then, everytime you click the "Add player" button, you'll add a String corresponding to the value inside a for loop passing the for's index as int value in the "Player $index". Again, the for method will receive a setState method, and inside the setState, your logic to add one more Player.
And finally, your gradient does not keep fixed? Hmm, I think you can pass a proprierty in the Scaffold widget called resizeToAvoidBottomInset and set it to false.
If something did not work as you expected, reply it and I'll help you out.