how can I use flutter slidable let only one item move??not working key - flutter

I use flutter_slidable: ^0.6.0.
I hope working only one item slidable in my listview.
if one item slide by user, all the others(whatever open or close) r closed.
some docs say use key.but mine is not working.
return ListView.builder(
physics: ClampingScrollPhysics(),
itemCount: snapshot.data!.size,
shrinkWrap: true,
itemBuilder: (BuildContext context, count) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Slidable(
key: Key(snapshot.data!.docs[count].id),
controller: slidableController,
actionPane: SlidableDrawerActionPane(),
actionExtentRatio: 0.25,
actions: <Widget>[
widget.pin == 0
? IconSlideAction(
caption: 'pin 제거',
color: Colors.black45,
icon: Icons.push_pin_rounded,
onTap: () {
pinEraseRoom(widget.roomId);
},
)
: IconSlideAction(
caption: 'Pin',
color: Colors.black45,
icon: Icons.push_pin_outlined,
onTap: () {
pinRoom(widget.roomId);
},
),
],
secondaryActions: <Widget>[
IconSlideAction(
caption: 'Delete',
color: Colors.red,
icon: Icons.delete,
onTap: () {
deleteRoom(widget.roomId);
},
),
],
mine
https://i.stack.imgur.com/eFW7J.jpg

resolved
/// parent
return StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection('users')
.doc(other)
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Container();
}
return ConversationList(
controller: slidableController,
//////// child widget in coversationList widget
itemBuilder: (BuildContext context, count) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Slidable(
key: Key(snapshot.data!.docs[count].id),
controller: widget.controller,
actionPane: SlidableDrawerActionPane(),
actionExtentRatio: 0.25,
actions: <Widget>[
widget.pin == 0
//
More clarity can be found here

Related

Flutter - How to search in List and display in ListView?

I'm coding a search system for the Flutter application I've developed. I'm having a problem with the back-end. First I pull the data from Firebase Firestore. Then I convert it to Model structure.
The code of the search system:
StreamBuilder(
stream: db.collection("DebrisPeoples").snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(),
);
} else {
final List<DebrisPeopleModel> data = snapshot.data!.docs
.map((e) => DebrisPeopleModel.fromDocument(e))
.toList(); // To Model code
return Column(
children: [
const SizedBox(height: 10),
SizedBox(
width: MediaQuery.of(context).size.width * 0.95,
child: TextFormField(
decoration: InputDecoration(
prefixIcon: const Icon(Icons.search),
contentPadding: const EdgeInsets.only(),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
),
),
onChanged: (value) {
final find = data.where(
(element) => data.contains(element.nameSurname));
print(find); // PROBLEM - NOT WORKING
},
),
),
SizedBox(
width: double.infinity,
height: MediaQuery.of(context).size.height * 0.8,
child: ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: data.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
leading: Icon(
data[index].personSize == 1
? Icons.person
: Icons.people,
),
title: Text(data[index].nameSurname.toString()),
subtitle: Text(
"${data[index].city} / ${data[index].district}",
),
trailing: IconButton(
icon: const Icon(Icons.info),
onPressed: () {
Get.to(const UnderRublePeopleDetailPage(),
arguments: data[index]);
print(data[index].nameSurname);
},
),
),
);
},
),
),
],
);
}
},
),
I'm having the problem in the query part. My goal is, for example, if there is a record in the form of ABC, I want it to appear in the results even if the user searches for A or AB.
Then I want the results to be displayed in the list. I will be grateful for your help :)
To change search results:
final find = data.where((element) => element
.nameSurname!
.toLowerCase()
.contains(value.toLowerCase()));
print(find);
setState(() {
data = find.toList();
print(data);
});
I tried to make such a search system. However, the results in the ListView do not change as I enter the TextFormField.
Your onChanged code should be as following.
onChanged: (value) {
final find = data.where(
(element) => element.nameSurname.toLowerCase().contains(value.toLowerCase()));
print(find);
}
Make sure you are managing the state to reflect the changes on UI.
Edited
final controller = TextEditingController();//Keep this as a field
StreamBuilder(
stream: db.collection("DebrisPeoples").snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(),
);
} else {
final searchText = controller.text.trim().toLowerCase();
final List<DebrisPeopleModel> data = snapshot.data!.docs
.map((e) => DebrisPeopleModel.fromDocument(e))
.where((e) => searchText.isEmpty || e.nameSurname!
.toLowerCase().contains(searchText))
.toList(); // To Model code
return Column(
children: [
const SizedBox(height: 10),
SizedBox(
width: MediaQuery
.of(context)
.size
.width * 0.95,
child: TextFormField(
controller: controller,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.search),
contentPadding: const EdgeInsets.only(),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
),
),
onChanged: (value) {
setState((){ });
},
),
),
SizedBox(
width: double.infinity,
height: MediaQuery
.of(context)
.size
.height * 0.8,
child: ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: data.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
leading: Icon(
data[index].personSize == 1
? Icons.person
: Icons.people,
),
title: Text(data[index].nameSurname.toString()),
subtitle: Text(
"${data[index].city} / ${data[index].district}",
),
trailing: IconButton(
icon: const Icon(Icons.info),
onPressed: () {
Get.to(const UnderRublePeopleDetailPage(),
arguments: data[index]);
print(data[index].nameSurname);
},
),
),
);
},
),
),
],
);
}
},
)

How to replace ListView with GroupedListView while using StreamBuilder with FireBase data in Flutter?

This is my ListView. It works perfectly:
StreamBuilder(
stream: FirebaseFirestore.instance.collection('products').snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
if (streamSnapshot.hasData) {
return ListView.builder(
itemCount: streamSnapshot.data!.docs.length,
itemBuilder: (context, index) {
final DocumentSnapshot documentSnapshot = streamSnapshot.data!
.docs[index];
return Card(
margin: const EdgeInsets.all(5),
child: ListTile(
title: Text(documentSnapshot['name'] + " (" +
documentSnapshot['quantity'].toStringAsFixed(0) + ")"),
),
);
},
);
}
return const Center(
child: CircularProgressIndicator(),
);
}
),
I use a StreamBuilder to obtain data from FireBase. I turn that stream into snapshots, which are used inside the builder part of the ListView.
How do I replace my ListView with a GroupedListView?
This is the equivalent GroupedListView:
StreamBuilder(
stream: FirebaseFirestore.instance.collection('products').snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
if (!streamSnapshot.hasData) return const Text("Loading...");
return GroupedListView<dynamic, String>(
elements: streamSnapshot.data!.docs,
groupBy: (element) => element['category'],
groupSeparatorBuilder: (String value) => Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
value,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
),
useStickyGroupSeparators: true,
floatingHeader: true,
order: GroupedListOrder.ASC,
itemComparator: (item1, item2) => item1['name'].compareTo(item2['name']),
itemBuilder: (context, dynamic element) {
return Card(
margin: const EdgeInsets.all(5),
child: ListTile(
title: Text(element['name']),
),
);
},
);
}
),

Future not setting default value on load

We are running a Future which should be setting the initial/default at time of load but we cannot seem to get this to work. The default seems to update only state change
return FutureBuilder<List<Payment>>(
future: DatabaseService.getPayments(widget.user!.id),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: [
const Divider(),
ListView.separated(
padding: EdgeInsets.zero,
physics: const NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return Dismissible(
direction: DismissDirection.endToStart,
key: Key(snapshot.data![index].cardId!),
onDismissed: (direction) {
// Remove the item from the data source.
setState(() {
snapshot.data!.removeAt(index);
});
},
// Show a red background as the item is swiped away.
background: Container(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
color: Colors.red,
alignment: Alignment.centerRight,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(
Icons.delete_forever_outlined,
color: Colors.white,
size: 32,
),
Text(
'Delete',
style: TextStyle(color: Colors.white),
),
],
),
),
confirmDismiss:
(DismissDirection dismissDirection) async {
switch (dismissDirection) {
case DismissDirection.endToStart:
case DismissDirection.startToEnd:
return await _showConfirmationDialog(
context,
'delete',
snapshot.data![index],
widget.user) ==
true;
case DismissDirection.horizontal:
case DismissDirection.vertical:
case DismissDirection.up:
case DismissDirection.down:
case DismissDirection.none:
break;
}
return false;
},
child: ListTile(
onTap: () {
setState(() {
paymentDefault = snapshot.data![index].cardId;
DatabaseService.createDefaultPayment(
context,
snapshot.data![index].cardId,
widget.user!.id);
});
},
leading: CircleAvatar(
backgroundColor:
snapshot.data![index].brand == 'MasterCard'
? Colors.amber[100]
: Colors.blue[100],
radius: 30,
child: loadImage(snapshot.data![index].brand)),
selected:
paymentDefault == snapshot.data![index].cardId,
title: Text('•••• ${snapshot.data![index].last4}'),
subtitle: Text(
'Exp. ${snapshot.data![index].expMonth}/${snapshot.data![0].expYear}'),
trailing:
paymentDefault == snapshot.data![index].cardId
? const Icon(Icons.check, color: Colors.green)
: const SizedBox.shrink(),
));
},
separatorBuilder: (context, index) {
return Divider(
height: 0,
color: Colors.grey[300],
);
}),
],
);
}
Use initialData prop in FutureBuilder
The data that will be used to create the snapshots provided until a non-null future has completed.
return FutureBuilder<List<Payment>>(
initialData: <Your initial Data here> 👈 Here
future: DatabaseService.getPayments(widget.user!.id),
builder: (context, snapshot) {
...
}

Can't make a single select with a Switch or a RadioListTile flutter

i'm trying to make an Extra Food Items List it's for a Delivery App. The module of restaurants requiere these , but my issue is when I use a RadioListTile or a SwitchListTile it is possible to select more than one option, all the data is from an API where the Admin of some restaurant put the products. What i try to do is to make a ListView. separated for the Extra Groups (eg."Presa") and my Extras are the products or toppings that some product can have
Widget build(BuildContext context) {
return SwitchListTile.adaptive(
activeColor: Theme.of(context).accentColor,
// checkColor: Theme.of(context).primaryColor,
value: widget.extra.checked,
onChanged:(value) {
setState(() {
//widget.extra.checked = value;
widget.extra.checked = !widget.extra.checked;
widget.onChanged();
Future.delayed(Duration(milliseconds: 200),(){
Navigator.pop(context);
});
widget.onSelectedExtra;
});
},
title: Text(
widget.extra?.name,
style: Theme.of(context).textTheme.headline3.merge(TextStyle(
color: Theme.of(context).shadowColor,
fontSize: 16
)),
textAlign: TextAlign.start,
),
subtitle: widget.extra.price == 'null' || widget.extra.price == null || widget.extra.price == '0' || widget.extra.price == 0
? Helper.getPrice(0, context, style: Theme.of(context).textTheme.headline4.merge(TextStyle(
color: Theme.of(context).accentColor
)),)
:Helper.getPrice(widget.extra.price, context, style: Theme.of(context).textTheme.headline4.merge(TextStyle(
color: Theme.of(context).accentColor
)),),
);
}
}
In this section i'm calling just one Extra Food so i can return inside a list in the other section
if (_con.product.extraGroups == null)
CircularLoadingWidget(height: 100)
else
ListView.separated(
padding: EdgeInsets.all(0),
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
controller: ScrollController(initialScrollOffset: 600),
itemBuilder: (context, extraGroupIndex) {
var extraGroup = _con.product.extraGroups.elementAt(extraGroupIndex);
return Wrap(
children: <Widget>[
ListTile(
dense: true,
contentPadding: EdgeInsets.symmetric(vertical: 0),
leading: Icon(
Icons.add_circle_outline,
color: Theme.of(context).accentColor
),
title: Text(
extraGroup?.name ?? '',
style: Theme.of(context).textTheme.subtitle1.merge(TextStyle(
color: Theme.of(context).accentColor,
fontWeight: FontWeight.w600,
fontSize: 16
)),
),
),
Container(
decoration: BoxDecoration(
),
child: ListTile(
title:Text('Seleccione su ${extraGroup.name}',
style: Theme.of(context).textTheme.caption.merge(TextStyle(
color: Theme.of(context).shadowColor,
//fontWeight: FontWeight.w600,
fontSize: 13
)),
),
trailing: FlatButton(
color: Theme.of(context).focusColor.withOpacity(0.4),
shape:RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30)
),
onPressed:(){
showModalBottomSheet(
isScrollControlled: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(topLeft: Radius.circular(30),topRight: Radius.circular(30))
),
context: context,
builder: (context){
return Wrap(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
child: ListView.separated(
padding: EdgeInsets.all(0),
itemBuilder: (context, extraIndex) {
return ExtraItemWidget(
extra: _con.product.extras.where((extra) => extra.extraGroupId == extraGroup.id && extra.multiple_selection == true ).elementAt(extraIndex),
onChanged: _con.calculateTotal,
);
},
separatorBuilder: (context, index) {
return SizedBox(height: 0);
},
itemCount: _con.product.extras.where((extra) => extra.extraGroupId == extraGroup.id && extra.multiple_selection == true).length,
primary: false,
shrinkWrap: true,
),
),
),
Padding(
padding: EdgeInsets.all(8.0),
child: ListView.builder(
itemBuilder: (context, extraIndex) {
return ExtraSingleItemWidget(
extra: _con.product.extras.where((extra) => extra.extraGroupId == extraGroup.id && extra.multiple_selection == false).elementAt(extraIndex),
onChanged: _con.calculateTotal,
onSelectedExtra: _con.SelectedExtra,
);
},
itemCount: _con.product.extras.where((extra) => extra.extraGroupId == extraGroup.id && extra.multiple_selection == false).length,
primary: false,
shrinkWrap: true,
),
),
],
);
});
},
child: Wrap(
children: [
Text(
'Seleccionar',
style: Theme.of(context).textTheme.button.merge(TextStyle(
color: Theme.of(context).shadowColor
)),
),
],
),
),
),
),
],
);
},
separatorBuilder: (context, index) {
return Divider(thickness:1.5,height: 20);
},
itemCount: _con.product.extraGroups.length,
primary: false,
shrinkWrap: true,
),
I'm calling like ExtraSingleItemWidget, once the user select an extra it add to the base price but if the user want to select another extra from the modal bottom sheet it select more than one and I just need one at a time.enter image description here
enter image description here

Flutter close a dialog and reload page with filtered list of the condition selected

import 'package:flutter_event_app/constant/color.dart';
import 'package:flutter_event_app/network/models/categories.dart';
import 'package:flutter_event_app/network/models/event_model.dart';
import 'package:flutter_event_app/network/models/time.dart';
import 'package:flutter_event_app/network/services/event_api.dart';
import 'package:flutter_event_app/pages/event_detail_page.dart';
import 'package:flutter_event_app/pages/search/home_search.dart';
import 'package:flutter_event_app/widgets/event_card.dart';
import 'package:flutter_event_app/widgets/no_events.dart';
import 'package:flutter_event_app/widgets/onload.dart';
class SelectedCategory extends StatefulWidget {
// SelectedCategory(Categories categories);
final Categories categories;
final Time time;
SelectedCategory(this.categories, [this.time]);
#override
_SelectedCategoryState createState() => _SelectedCategoryState();
}
class _SelectedCategoryState extends State<SelectedCategory> {
Categories categories;
Time timing;
String timeselect;
// Event event;
void viewEventDetail(Events event) {
Navigator.of(context).push(
PageRouteBuilder(
opaque: false,
barrierDismissible: true,
transitionDuration: Duration(milliseconds: 300),
pageBuilder: (BuildContext context, animation, __) {
return FadeTransition(
opacity: animation,
child: EventDetailPage(event),
);
},
),
);
}
bool isLoading = false;
List<Events> upcomingEvents;
List categorizedupcomingEvents = [];
List categorizedPaidupcomingEvents = [];
List categorizedFreeupcomingEvents = [];
#override
void initState() {
_fetchData();
categories = widget.categories;
timing = widget.time;
// print(timing.id);
super.initState();
}
Future _fetchData() async {
setState(() => isLoading = true);
upcomingEvents = await getEventss();
categorizedupcomingEvents = upcomingEvents
.where((category) => category.category == categories.id)
.toList();
categorizedPaidupcomingEvents = categorizedupcomingEvents
.where((paid) => paid.is_paid == true)
.toList();
categorizedFreeupcomingEvents = categorizedupcomingEvents
.where((free) => free.is_paid == false)
.toList();
setState(() => isLoading = false);
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(
MediaQuery.of(context).size.height / 9.5,
),
child: AppBar(
title: Text(categories.categoryName),
centerTitle: true,
actions: <Widget>[
IconButton(
icon: Icon(
Icons.sort,
),
onPressed: () {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) =>
showFilterByTimeDialog(context);
// )
// );
}),
IconButton(
icon: Icon(
Icons.more_vert,
),
onPressed: () {})
],
bottom: TabBar(
tabs: [
Text('All'),
Text('Paid'),
Text('Free'),
],
),
),
),
body: TabBarView(
children: <Widget>[
// All
isLoading
? OnloadingCards()
: Column(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: categorizedupcomingEvents.isEmpty
? NoItems()
: ListView.builder(
itemCount: categorizedupcomingEvents.length,
shrinkWrap: true,
primary: false,
physics: BouncingScrollPhysics(),
// scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
final event =
categorizedupcomingEvents[index];
return EventCard(event,
onTap: () => viewEventDetail(event));
},
),
),
),
],
),
// Paid
isLoading
? OnloadingCards()
: Column(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: categorizedPaidupcomingEvents.isEmpty
? NoItems()
: ListView.builder(
itemCount:
categorizedPaidupcomingEvents.length,
shrinkWrap: true,
primary: false,
physics: BouncingScrollPhysics(),
// scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
final event =
categorizedPaidupcomingEvents[index];
return EventCard(event,
onTap: () => viewEventDetail(event));
},
),
),
),
],
),
// Free
isLoading
? OnloadingCards()
: Column(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: categorizedFreeupcomingEvents.isEmpty
? NoItems()
: ListView.builder(
itemCount:
categorizedFreeupcomingEvents.length,
shrinkWrap: true,
primary: false,
physics: BouncingScrollPhysics(),
// scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
final event =
categorizedFreeupcomingEvents[index];
return EventCard(event,
onTap: () => viewEventDetail(event));
},
),
),
),
],
),
],
),
));
}
void showFilterByTimeDialog(BuildContext context) {
Dialog fancyDialog = Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
child: SingleChildScrollView(
child: Container(
width: double.infinity,
height: MediaQuery.of(context).size.height * 0.5,
// alignment: Alignment.bottomCenter,
decoration: BoxDecoration(
// color: Colors.greenAccent,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12),
bottomLeft: Radius.circular(12),
bottomRight: Radius.circular(12),
),
),
child: Column(
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height * 0.05,
child: Text(
"Time",
style: TextStyle(
color: Colors.deepPurple,
fontSize: 20,
fontWeight: FontWeight.w600),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
// color: Colors.red,
width: double.infinity,
child: ListView.builder(
shrinkWrap: true,
primary: false,
physics: BouncingScrollPhysics(),
itemCount: times.length,
itemBuilder: (context, int index) {
Time time = times[index];
return RaisedButton(
onPressed: () {
debugPrint('I am Awesome');
},
textColor: Colors.red,
// color: Colors.blueAccent,
disabledColor: Colors.grey,
disabledTextColor: Colors.white,
highlightColor: Colors.orangeAccent,
child: Text(time.name),
);
}),
),
),
),
],
),
),
),
);
showDialog(
context: context, builder: (BuildContext context) => fancyDialog);
}
}
Within the same page I have a dialog box as shown below
On the method showFilterByTimeDialog where I select an item and have to go back to the same page below the dialogue .Am still learning flutter and my issue is I need help when I select an item from the dialogue box,i refresh the same page and display a new filtered lst from the current list displayed on that page with a condition of the item selected from the dialogue box.