I have a listview builder with a list in the home page and when an item of the list is pressed a second page will open with details of that list. The details are basically another list of cards on each card I have a counter starts with zero value, when user press on each card the counter value will increase.
Now the issue I face is when user go back to home page and then again click on a the same item to go to the second page, they will find the counter values as there were (i.e not zeros), although I did not include any mechanism to store these values (i.e no sharedpreferance or so).
These values of the counter will not go back to zeros, unless the app is closed and open again.
Is there a way to force the app to reset these values to zeros as they were originally once the user go back to home page.
This is the code of the listview.builder of the second page:
child: ListView.builder(
itemCount: widget.cities.attractions.length,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
setState(() {
widget.cities.attractions[index].localrank++;
});
},
child: Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(widget.cities.attractions[index].name),
Center(
child: Text(
'${widget.cities.attractions[index].localrank}')),
],
),
)),
);
},
),
You have to main a list of size equal to no of cards you have on your screen. This can be bool list (List) and initialise the List like
int noOfCards; // This variable will have the no of cards on your screen
List<bool> isClicked = new List.filled(noOfCards, false);
Now make the value to true when any item is clicked
For Example :- if item on some index is clicked then
if(!isClicked[index]){
isClicked[index] = true;
// your code to increment the value
}
// navigate to your second page
I got it resolved by others. The solution is basically to use the WillPopScope widget to set all "localranks" in widget.cities.attractions to zero when the back button is pressed. For that, we have to define the method in the Widget class and then to use forEach to access each attraction and set the localrank to zero from there.
This is the code:
Future<bool> moveToLastScreen() async {
setState(() {
widget.cities.attractions.forEach((attraction) => attraction.localrank = 0);
});
Navigator.of(context).pop();
return true;
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () => moveToLastScreen(),
child: Scaffold(...),
);
}
Related
I have a simple list of ListTile s that I nested in Slidable widget using the flutter_slidable package. However there is one issue that when I use the delete button, the item stays on the screen, even though it is successfully removed from the list (If i make a hot reload, it will actually get removed from screen). I tried to add a setState method inside the onPressed function but I cannot add it because the SlidableAction widget is a stateless widget. How can I make the item disappear when I tap on this button?
Here is a small video demonstration. I delete two lists with the delete button. They stay on the screen. I go home screen and come back to see they got deleted.
https://streamable.com/td7blf
Here is my code:
Expanded(
child: ListView.builder(
itemCount: likedNames.length,
itemBuilder: (context, index) {
return Slidable(
key: const ValueKey(0),
endActionPane: ActionPane(
motion: ScrollMotion(),
dismissible: DismissiblePane(onDismissed: () {
likedNames.remove(likedNames[index]);
}),
children: [
SlidableAction(
onPressed: (context) {
likedNames.remove(likedNames[index]); // <- this is the part where I want to do the removing of the item
},
label: 'Delete',
backgroundColor: AppColors.error,
),
],
),
child: ListTile(
onTap: () {},
title: Text(likedNames[index].name),
trailing: Icon(Icons.chevron_right),
),
);
}),
),
onPressed: (context) {
setState() => likedNames.remove(likedNames[index])
},
I've used this solution:
onPressed: (BuildContext context) async {
await Provider.of<MyList>(context, listen: false)
.remove(MyList[index]);
}
MyList is a separate class:
class MyList extends ChangeNotifier
Whit this methods:
final List<String> _myList = [];
List<String> get items => _myList;
.
.
.
Future<bool> remove(String item) async {
_myList.remove(item);
notifyListeners();
return true;
}
I am very new to flutter and was just curious to know how can we create a new card widget everytime a button (lets say FAB) is clicked.
Suppose this is the card widget :
return Card(
child: Column(
children: [
Text('name'),
Text('standard'),
Text('Roll No'),
],
),
);
I want the cards to build with the same content everytime the FAB is clicked. Can someone help me with this ?
First declare a List of widget type
List<Widget> _cardList = [];
Then create your widget which you want to add on button click, e.g.
Widget _card() {
return Card(
child: Column(
children: [
Text('name'),
Text('standard'),
Text('Roll No'),
],
),
);
}
add your widget in List on button click
FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
setState(() {
_cardList.add(_card());
});
},
),
Now use a ListView.builder for create a list of widget
ListView.builder(
shrinkWrap: true,
itemCount: _cardList.length,
itemBuilder: (context, index) {
return _cardList[index];
},
)
I have a Flutter web application that displays multiple user profiles on a card within a Row. The cards can each flip over to reveal more information via this library:
https://pub.dev/packages/flip_card
The application uses WebSockets and receives a JSON list of user details which maps to a User dart class, and as soon as new list arrives on a socket, we create a widget and add it to a widgetList and wrap it in a setState():
webSocket.onMessage.listen((e) {
final List receivedJsonUserList = json.decode(e.data);
final List<User> userListFromSocket =
receivedJsonUserList.map((item) => User.fromJson(item)).toList();
userListFromSocket.forEach((newUser) {
setState(() {
widgets[newUser.user.id] = UserDetails(user: newUser);
widgetList = widgets.entries.map((entry) => entry.value).toList();
});
});
}
}
});
The widget is drawn like this:
#override
Widget build(BuildContext context) {
return ResponsiveBuilder(
builder: (context, sizingInformation) => Scaffold(
drawer: sizingInformation.deviceScreenType == DeviceScreenType.mobile
? NavigationDrawer()
: null,
backgroundColor: Colors.white,
body: Scrollbar(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: widgetList),
],
),
),
),
),
);
}
The code works 90% of the time, but occasionally the wrong data is on the back of a card. So User 1 will have User 2's data on the back, etc.
Am I doing this correctly? Is there an obvious issue with this implementation? I tried to create a seperate widget for each user and it seems to resolve the issue but re-using widgets surely has to be possible.
I have a group of cards that are connected to my cloud firestore which shows when the icon related to each card is chosen. However, I want a card that tells the user to choose an icon when none of the other cards are showing. This is my code:
class Tips extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
stream:
FirebaseFirestore.instance.collection('moodIcons').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return Text('Loading data... Please Wait');
var currentCards = 0;
return Container(
child: new ListView(
children: snapshot.data.documents.map<Widget>((DocumentSnapshot document) {
if (document.data()['display'] == true) {
//code to display each card when specific icon is chosen
} else {
if (currentCards < 1) {
currentCards++;
return new Card(
child: new Column(
children: <Widget>[
new Container(
width: 400.0,
height: 45.0,
child: new Text(
"Choose an icon to show a tip!",
),
],
));
Currently, the card shows all the time but I want it to disappear when an icon is clicked. When an icon is clicked, the value of display becomes true.
I tried to use a counter so that if the value of display is true then it adds one to the counter then if the counter is more than 0, the card in the else statement wont show. However, this didn't work as I couldn't return the value of the counter from within the if statement.
Any help would be appreciated!
You could conditionally add an item at the beginning of your ListView if no document is set to display: true:
class Tips extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
stream: FirebaseFirestore.instance.collection('moodIcons').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return Text('Loading data... Please Wait');
return Container(
child: new ListView(children: [
if (!snapshot.data.documents
.any((doc) => doc.data()['display'] == true))
Card(child: Text("Choose an icon to show a tip!")),
...snapshot.data.documents
.where((doc) => doc.data()['display'] == true)
.map((doc) => MyWidget(doc))
.toList(),
]),
);
},
),
);
}
}
If you want this to be done by StatelessWidget you need some state management like Bloc or Riverpod, your own etc.
a good answer here
You need to abstract your stream and create an event/action when you click on the icon.
That event is to be handlined via the above mentioned approaches. where a new card will be pushed to the stream.
Or you can make your widget Stateful, create a list of Card Widgets (to provide to your list builder) and use setState to add a element to a Card to the list.
am making a category app where there will be a lot of dropdowns and on clicked it should display a list of subcategories in grid view here is a sample image of what I want to achieve.
https://cdn.dribbble.com/users/1502567/screenshots/7147539/media/dbca2b397050acac918164ddfc5820cd.jpg
That example doesn't seem like much of a dropdown, more like a container for a grid of items? I would use a Table to contain all these items. Flutter.dev has really good documentation on how to use it: https://api.flutter.dev/flutter/widgets/Table-class.html
bool pressed;
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: <Widget>[
RaisedButton(
child: Text("button"),
onPressed: () {
setState(() {
pressed = true;
});
},
)
// show table if pressed is true, otherwise show an empty widget
pressed ? Table() : SizedBox(),
],
),
);
}