I'm new to flutter and I want to use list view using flutter list view builder.
I used this data model(user.equipments) to build the list view
in here equipmentName comming from equipment model as show following
Here is the actual issue, I need to buind the equipment name from equipment model instead of buinding this id "1603739590802". I tried few ways, but those were not woking as expected.
here is the current result
here is my code for list view builder
Container(
width: size.width,
child: ListView.builder(
shrinkWrap: true,
itemCount: _user.getSingleUser.equipments.length,
itemBuilder: (context, index) {
return ListTile(
tileColor: Colors.blue[100],
onTap: () {
_addModalBottomSheet(bc,size,_user.getSingleUser.equipments[index].id);
},
leading:Icon(Icons.fiber_manual_record_rounded),
trailing: IconButton(
onPressed: () {
singleUser.equipments.removeAt(index);
_user.setSingleUser(singleUser);
},
icon: Icon(
Icons.close_outlined,
color: Colors.red,
),
iconSize: 20,
),
title: Text(
'${_user.getSingleUser.equipments[index].equipmentName} :
${_user.getSingleUser.equipments[index].yearsOfUsing} years',
),
);
},
),
),
Any suggestion would be appreciated.
Assuming equipmentList is the list of equipments :
equipmentList.firstWhere(
(e) => e.id == _user.getSingleUser.equipments[index].equipmentName).name
The name equipmentName is misleading, it should be equipmentId.
Related
I have had tough luck with the StreamUnreadIndicator() within the getStream API. I am trying to essentially have an indicator on the list tile for whenever a new message comes in. But nothing returns. I tried putting some debug prints to at least get the number of unread messages for the channel, but it is always 0.
Here's my message list view:
Widget _messagesList(List<dynamic>? messages, StreamChatClient client,
int messageCount, bool friendsTab) {
return ListView.separated(
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
itemCount: messageCount,
itemBuilder: (context, index) {
//print("messaging:"+messages![index].channel);
return GestureDetector(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) =>
MessageApi(
sourceType: SourceType.justMet,
receiverUser: friendsTab ? friends[index] : chatRequesters[index],
userName: userName,
channelId: messages![index].channel,
streamToken: streamToken,
client: StreamChatCore.of(context).client,
)
));
},
child: ListTile(
title: friendsTab ? Text(friends[index].firstName) : Text(chatRequesters[index].firstName),
subtitle: _buildLastMessage(messages![index].channel, client),
trailing: Column(
children: [
StreamUnreadIndicator(
cid: "messaging:"+messages[index].channel,
),
_buildLastMessageAt(messages[index].channel, client),
],
),
leading: CircleAvatar(
radius: 30,
backgroundImage: CachedNetworkImageProvider(
friendsTab ? friends[index].photoUrl : chatRequesters[index].photoUrl
),
),
),
);
},
separatorBuilder: (context, index) {
return const Divider();
},
);
}
Version 4.3 of Stream Chat Flutter introduced unreadMessagesSeparatorBuilder:
Try
StreamMessageListView(
unreadMessagesSeparatorBuilder: (context, unreadCount) =>
Text('$unreadCount unread message'),
)
See the changeling for additional details: https://pub.dev/packages/stream_chat_flutter/changelog
They seem to have updated their backend and now it works without me changing anything. I noticed they changed their docs recently too after this question.
I have a code whereby the genders of a pet is listed in two separate cards and when the user taps on one of them, it changes color to indicate that it has been selected and is saved in the database. However, the app is letting the user continue to the next page without choosing any one of the values. I want to do a validation whereby the user will have to choose one of the cards to be able to move forward. How can I do this please?
Here is my code:
Expanded(
child: GridView.count(
crossAxisCount: 2,
primary: false,
scrollDirection: Axis.vertical,
children: List.generate(petGenders.length, (index) {
return GestureDetector(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0)),
color:
selectedIndex == index ? primaryColor : null,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
petGenders[petKeys[index]],
SizedBox(
height: 15.0,
),
Text(
petKeys[index],
style: TextStyle(
color: selectedIndex == index
? Colors.white
: null,
fontSize: 18.0,
fontWeight: FontWeight.w600),
),
],
),
),
),
onTap: () {
setState(() {
widget.pet.gender = petKeys[index];
selectedIndex = index;
});
});
}),
),
),
The model:
Map<String, Image> genders() => {
"Male":
Image(image: AssetImage('Assets/images/male.png'), width: 50),
"Female":
Image(image: AssetImage('Assets/images/female.png'), width: 50)
};
Take one variable
bool isGenderSelected = false;
Then change its value to true on tap of card like
onTap: () {
setState(() {
isGenderSelected = true;
widget.pet.gender = petKeys[index];
selectedIndex = index;
});
});
Now check if it's true then only allow the user to go next page or show some message to the user
Scenario like this, I prefer using nullable selectedValue. In this case, I will create nullable int to hold and switch between selection.
int? selectedIndex;
And using color will be like
color: selectedIndex==index? SelectedColor:null,
you can replace null with inactive color.
For validation part, do null check on selectedIndex .
if(selectedIndex!=null){.....}
I am working on a flutter project and I want to popup to get generated on clicking a particular tile. This is my code
This is my ListTile generator
Future<Widget> getRecordView() {
print("405 name " + name.toString());
print(nameArr);
var items = List<Record>.generate(int.parse(widget.vcont), (index) => Record(
name: nameArr[index],
type: typeArr[index],
address: addressArr[index],
state: stateArr[index],
phone:phoneArr[index],
city: cityArr[index],
id: idArr[index],
));
print("Started");
var listItems = items;
var listview = ListView.builder(
itemCount: int.parse(widget.vcont),
itemBuilder: (context,index){
return listItems[index] ;
}
);
return Future.value(listview);
}
The Popup I need on tap :
Future <bool> details(BuildContext context,String type) {
return Alert(
context: context,
type: AlertType.success,
title: "Submission",
desc: type, //The parameter
buttons: [
DialogButton(
child: Text(
"OKAY",
style: TextStyle(color: Colors.white, fontSize: 20),
),
onPressed: () => Navigator.pop(context),
color: Color.fromRGBO(0, 179, 134, 1.0),
radius: BorderRadius.circular(0.0),
),
],
).show();
}
I tried to wrap Record with GestureDetector and Inkwell, but I only got errors and Android Studio tells me that Record is not expected in that context. I looked up in the internet and couldnt find anything on this matter. Please help.
Record, as far I can see is just a model, and not a widget. Item Builder requires a widget. You should wrap what you are passing to the item builder with an actual widget like a Container(), ListTile(), .. etc. These widgets can be wrapped with Gesture Detector to perform the pop ups you want.
It would look like this
var listview = ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
// Tap on an item in the list and this will get executed.
},
// Return an actual widget, I'm using a ListTile here, but you can
// use any other type of widget here or a create custom widget.
child: ListTile(
// this will display the record names as list tiles.
title: Text(items[index].name),
),
);
},
);
I have an array which i set as a class like this
class FilterArray {
static var FilterArrayData = [];
}
I am simply adding the values in an array. Issue is i am calling this array in a page when array is null. Then on next Page i am adding values in array. Now issue is when i come back in previous page the array is still null. I need to refresh page for this. Which i dont want thats why i use FutureWidget i though from Future widget when array update it will also update in my screen but thats not working. Need to know what can i do for this here i need to update data when array is update so it can show in a Future Widget.
This is my total code
class _SearchPgState extends State<SearchPg> {
Future getData() async {
var result = FilterArray.FilterArrayData;
if (result.length != 0) {
return result;
} else {
return null;
}
}
#override
Widget build(BuildContext context) {
print(FilterArray.FilterArrayData);
return Scaffold(
appBar: AppBar(
title: Container(
height: 50.0,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 3.0),
child: Center(
child: TextField(
onTap: () => Get.to(SearchPgExtra()),
readOnly: true,
decoration: InputDecoration(
hintText: tr('search.search'),
alignLabelWithHint: true,
hintStyle: Theme.of(context).textTheme.subtitle2,
prefixIcon: Icon(Icons.search),
),
),
),
),
),
actions: [
IconButton(
icon: Icon(
FlutterIcons.sort_descending_mco,
color: Theme.of(context).accentColor,
),
onPressed: navigateToSortPage,
),
IconButton(
icon: Icon(
FlutterIcons.filter_fea,
color: Theme.of(context).primaryColor,
),
onPressed: navigateToFilterPage,
),
],
),
body: FutureBuilder(
future: getData(), // async work
builder: (context, projectSnap) {
print(projectSnap.data);
if (projectSnap.hasData) {
return StaggeredGridView.countBuilder(
itemCount: projectSnap.data.length,
crossAxisCount: 4,
staggeredTileBuilder: (int index) => StaggeredTile.fit(2),
mainAxisSpacing: 15.0,
crossAxisSpacing: 15.0,
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: ScrollPhysics(),
padding: EdgeInsets.symmetric(horizontal: 18.0),
itemBuilder: (context, index) {
var product = projectSnap.data[0][index];
return FadeInAnimation(
index,
child: ProductCard2(
product: product,
isHorizontalList: false,
),
);
},
);
} else {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
'assets/images/search.png',
width: MediaQuery.of(context).size.width / 2,
),
SizedBox(height: 15.0),
Text(
'search.title',
style: Theme.of(context).textTheme.headline1,
).tr(),
SizedBox(height: 15.0),
Text(
'search.subtitle',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.subtitle1,
).tr(),
SizedBox(
height: MediaQuery.of(context).size.height / 5,
),
],
),
);
}
},
),
);
}
}
In start array is null then ill add values in array then comeback nothing change then i reload the screen then its working fine.
This is the how i am adding array
RangeSlider(
values: _currentRangeValues,
min: 0,
max: 10000,
divisions: 10,
labels: RangeLabels(
_currentRangeValues.start.round().toString(),
_currentRangeValues.end.round().toString(),
),
onChanged: (RangeValues values) {
setState(() {
_currentRangeValues = values;
//print(_currentRangeValues);
});
var data = searchArray.searchArrayData;
for (int i = 0; i < data.length; i++) {
var current = data[i];
if(current['Price'] >= _currentRangeValues.start && current['Price'] <= _currentRangeValues.end){
print(data);
FilterArray.FilterArrayData.add(data);
}
}
},
),
when data add to FilterArrayData ill go back on Page array on that page not updating so then i change the page and comeback again in SearchPg then i can see data
Don't do your validation with the length of your array. It is like trying to do a validation with something that doesn't existe yet. Instead of that, try using
if(snapshot.hasData)
{ return ... ; }
then, after that, now you can do another validation, for instance, sometimes what you receive is data, but an empty array. There is where I would place the other two options. Remember, inside of the first if.
if(array.isNotEmpty)
{ return ... ; }
and
else
{ return ... ; }
After the first if, then you can now also validate, what will happen if you didn't receive data at all. Simply with an else.
else
{ return ... ; }
In summary: use one first validation with hasData and then, inside of that, decide what to do with the received information. Outside all that, decide what to do if you didn't receive any information at all.
Such cases are faced by new developers often. The best way to deal with it is state management packages like Provider, Bloc, etc. Visit the link and you will find all the relevant packages. Personally, I have used Provider a lot. Bloc is also a good option. A lot of people use it. But I haven't had the chance to use it. Riverpod is an up and coming package. But it still requires a lot of fixing.
I have a widget with a item builder. In the item builder I want to create Drop down for each item.
For the value property of this DropdownMenuItem i use
SaleRef _selectedRef;
The problem is when I declare this SaleRef _selectedRef; inside the itemBuilder the value does not change after selecting a item.
When I declare it outside this widget but in the class it changes the value of every dropdown
What can I do to select separate values on every drop down ?
This is the code for creating the items
ListView.separated(
itemBuilder: (context, index) {
return Row(
children: <Widget>[
Container(
height: 40,
width: MediaQuery.of(context).size.width * 1 / 5,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: new BorderRadius.circular(25.0),
border: Border.all(
color: Colors.grey,
style: BorderStyle.solid,
width: 0.80),
),
child: DropdownButton<SaleRef>(
hint: Text(" Select Ref"),
value: _selectedRef,
onChanged: (SaleRef value) {
setState(() {
_selectedRef = value;
print(_selectedRef.refID);
});
},
items: _filteredSaleRef.map((SaleRef ref) {
return DropdownMenuItem<SaleRef>(
value: ref,
child: Row(
children: <Widget>[
Text(
" " + ref.refName,
style: TextStyle(color: Colors.black),
),
],
),
);
}).toList(),
),
),
],
);
},
separatorBuilder: (context, index) {
return Divider(height: 16);
},
itemCount: filteredShopItem.length,
)
When I declare it like this
class _AssignRefPageState extends State<AssignRefPage>
with {
SaleRef _selectedRef;
This happens after selecting a value
When I declare it like this inside the builder like this
itemBuilder: (context, index) {
SaleRef _selectedRef;
return Row(
This is what I get it's always the hint even after I select a one
The problem is you are using the same variable for every dropdownbutton and when you try to declare it in the ListView itemBuilder and you call setState the whole Widget is built setting it to null again
What you can do is create a List of values(the size should be number of dropdownbuttons you have)
Like this
class _AssignRefPageState extends State<AssignRefPage>
with {
List<SaleRef>_selectedRef = List.generate(numberOfDropDowns, (index) => SaleRef());
Then use the array in your setState
DropdownButton<SaleRef>(
hint: Text(" Select Ref"),
value: _selectedRef,
onChanged: (SaleRef value) {
setState(() {
_selectedRef[index] = value;
print(_selectedRef[index].refID); //<- The index is from itemBuilder
});
}
As #Josteve-Adekanbi said the problem was using the same variable
Creating an array with a size outside the class helped me solve my problem
List<SaleRef> _selectedRef = new List(filteredShopItem.length);
Then I used it like this
DropdownButton<SaleRef>(
hint: Text(" Select Ref"),
value: _selectedRef[index],
onChanged: (SaleRef value) {
setState(() {
_selectedRef[index] = value;
print(_selectedRef[index].refID);
});
}