flutter delete item from listview
2019-November Latest way
more information https://www.youtube.com/watch?v=iEMgjrfuc58
ListView.builder/GridView.builder(
itemBuilder: (BuildContext context, int index){
return Dismissible(
key: Key(selectServiceLocations[index].toString()),
onDismissed: (direction) {
setState(() {
selectServiceLocations.removeAt(index);
});
},
child: Container(...)
}
)
That's because you're creating a new MaterialPageRoute.
Try this:
onLongPress: () {
_saved.remove(pair);
Navigator.of(context).pop();
_pushSaved();
},
With this solution you'll still see the view change. If you want to prevent that, you'll need a new stateful page, and a bit of refactoring:
- Make your
_saved
items global (only for this example) - Remove the
_pushSaved
method - Update the
onPressed
function that used to call the_pushSaved
function - Add the stateful
DetailPage
instead of the_pushSaved
method
Like so:
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
void main() => runApp(new MyApp());
// create a global saved set
Set<WordPair> savedGlobal = new Set<WordPair>();
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Startup Name Generator',
home: new RandomWords(),
);
}
}
class RandomWords extends StatefulWidget {
@override
RandomWordsState createState() => new RandomWordsState();
}
class RandomWordsState extends State<RandomWords> {
final List<WordPair> _suggestions = <WordPair>[];
final TextStyle _biggerFont = const TextStyle(fontSize: 18.0);
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: const Text('Startup Name Generator'),
actions: <Widget>[
// change the onPressed function
new IconButton(icon: const Icon(Icons.list), onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailPage()
)
);
}),
],
),
body: _buildSuggestions(),
);
}
Widget _buildSuggestions() {
return new ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: (BuildContext _context, int i) {
if (i.isOdd) {
return const Divider();
}
final int index = i ~/ 2;
if (index >= _suggestions.length) {
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
});
}
Widget _buildRow(WordPair pair) {
final bool alreadySaved = savedGlobal.contains(pair);
return new ListTile(
title: new Text(
pair.asPascalCase,
style: _biggerFont,
),
trailing: new Icon(
alreadySaved ? Icons.favorite : Icons.favorite_border,
color: alreadySaved ? Colors.red : null,
),
onTap: () {
setState(() {
if (alreadySaved) {
savedGlobal.remove(pair);
} else {
savedGlobal.add(pair);
}
});
},
);
}
}
// add a new stateful page
class DetailPage extends StatefulWidget {
@override
_DetailPageState createState() => _DetailPageState();
}
class _DetailPageState extends State<DetailPage> {
final TextStyle _biggerFont = const TextStyle(fontSize: 18.0);
@override
Widget build(BuildContext context) {
Iterable<ListTile> tiles = savedGlobal.map((WordPair pair) {
return new ListTile(
onLongPress: () {
setState(() {
savedGlobal.remove(pair);
});
},
title: new Text(
pair.asPascalCase,
style: _biggerFont,
),
);
});
final List<Widget> divided = ListTile.divideTiles(
context: context,
tiles: tiles,
).toList();
return new Scaffold(
appBar: new AppBar(
title: const Text('Saved Suggestions'),
),
body: new ListView(children: divided),
);
}
}
A bit late to the party but what I've found is that if you want to manipulate a ListView or GridView it is paramount that you assign a Key to each child Widget of the List/GridView
In short Flutter compares widgets only by Type and not state. Thus when the state is changed of the List represented in the List/GridView, Flutter doesn't know which children should be removed as their Types are still the same and checks out. The only issue Flutter picks up is the number of items, which is why it only removes the last widget in the List/GridView.
Therefore, if you want to manipulate lists in Flutter, assign a Key to the top level widget of each child. A more detailed explanation is available in this article.
This can be achieved be adding
return GridView.count(
shrinkWrap: true,
crossAxisCount: 2,
crossAxisSpacing: 5.0,
mainAxisSpacing: 5.0,
children: List.generate(urls.length, (index) {
//generating tiles with from list
return GestureDetector(
key: UniqueKey(), //This made all the difference for me
onTap: () => {
setState(() {
currentUrls.removeAt(index);
})
},
child: FadeInImage( // A custom widget I made to display an Image from
image: NetworkImage(urls[index]),
placeholder: AssetImage('assets/error_loading.png')
),
);
}),
);