从列表视图中颤动删除项目
Posted
技术标签:
【中文标题】从列表视图中颤动删除项目【英文标题】:flutter delete item from listview 【发布时间】:2019-08-04 04:41:07 【问题描述】:我试过了
编写您的第一个 Flutter 应用,第 2 部分 flutter app page 5
我现在对此应用程序有疑问。我想从该 List onLongPress 中删除一个条目,如下所示:
onLongPress: ()
setState(()
_saved.remove(pair);
);
,
这将从列表中删除该项目,但不会更新屏幕。返回家中并重新打开此路线后,新项目没有被删除。但是如何在没有用户重新打开页面的情况下触发此页面上的更新。
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return new MaterialApp(
title: 'Startup Name Generator',
home: RandomWords(),
theme: new ThemeData(
primaryColor: Colors.orange,
),
);
class RandomWords extends StatefulWidget
@override
RandomWordsState createState() => RandomWordsState();
class RandomWordsState extends State<RandomWords>
final List<WordPair> _suggestions = <WordPair>[];
final TextStyle _biggerFont = const TextStyle(fontSize: 18);
final Set<WordPair> _saved = new Set<WordPair>();
_buildSuggestions()
return ListView.builder(
padding: const EdgeInsets.all(16),
itemBuilder: (context, i)
if (i.isOdd) return Divider();
final index = i ~/ 2;
if (index >= _suggestions.length)
_suggestions.addAll(generateWordPairs().take(10));
return _buildRow(_suggestions[index]);
,
);
Widget _buildRow(WordPair pair)
final bool alreadySaved = _saved.contains(pair);
return ListTile(
title: Text(pair.asPascalCase, style: _biggerFont),
trailing: new Icon(
alreadySaved ? Icons.favorite : Icons.favorite_border,
color: alreadySaved ? Colors.red : null,
),
onTap: ()
setState(()
if (alreadySaved)
_saved.remove(pair);
else
_saved.add(pair);
);
,
);
@override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text("Startup Name Generator"),
actions: <Widget>[
new IconButton(icon: const Icon(Icons.list), onPressed: _pushSaved),
],
),
body: _buildSuggestions(),
);
void _pushSaved()
Navigator.of(context).push(
new MaterialPageRoute(
builder: (BuildContext context)
final Iterable<ListTile> tiles = _saved.map(
(WordPair pair)
return new ListTile(
//this is the delete operation
onLongPress: ()
setState(()
_saved.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),
);
,
),
);
【问题讨论】:
【参考方案1】:2019-11月最新方式
更多信息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(...)
)
【讨论】:
感谢非常好的建议。 太棒了!下面是一个友好的官方Dismissible
用法示例:flutter.dev/docs/cookbook/gestures/dismissible【参考方案2】:
那是因为您正在创建一个新的 MaterialPageRoute。
试试这个:
onLongPress: ()
_saved.remove(pair);
Navigator.of(context).pop();
_pushSaved();
,
使用此解决方案,您仍然会看到视图发生变化。如果你想防止这种情况发生,你需要一个新的有状态页面,并进行一些重构:
使您的_saved
项目全局化(仅适用于本示例)
删除_pushSaved
方法
更新用于调用_pushSaved
函数的onPressed
函数
添加有状态的DetailPage
而不是_pushSaved
方法
像这样:
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),
);
【讨论】:
这可行,但您会看到主页并切换到新路线。 你是对的。我将提供一个解决方案来防止这种情况(尽管解决方案不会像以前那么简单) 总结一下。如果我想编辑页面的内容,我应该创建一个新页面并将该页面推送到导航器。要在同一页面上显示不更新的内容,我可以创建页面 throws Navigator.of(context).push( new MaterialPageRoute( 是和否,在您已经创建第二个页面但无法更新它的状态之前。现在我们“准备”了一个有状态的第二页,并告诉第一页显示它而不是动态创建一个。 这个state 概念可能看起来有点抽象。 This page on the Flutter site 可以澄清一下!【参考方案3】:聚会有点晚了,但我发现如果您想操作 ListView 或 GridView,最重要的是为 List/GridView 的每个子 Widget 分配一个 Key
简而言之,Flutter 仅按类型而不是状态来比较小部件。因此,当 List/GridView 中表示的 List 的状态发生更改时,Flutter 不知道应该删除哪些子项,因为它们的 Types 仍然相同并签出。 Flutter 发现的唯一问题是项目的数量,这就是为什么它只删除 List/GridView 中的最后一个小部件。
因此,如果您想在 Flutter 中操作列表,请为每个子组件的***小部件分配一个 Key。 this article 有更详细的解释。
这可以通过添加来实现
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')
),
);
),
);
【讨论】:
【参考方案4】:第 1 步:查找特定对象(元素)VIA 的索引
List_Name.indexOf(List_Name[index]);
ex:_userTransactions.indexOf(_userTransactions[index])
第 2 步:删除特定索引处的对象
LIST_Name.removeAt(index);
ex: _userTransactions.removeAt(index);
【讨论】:
以上是关于从列表视图中颤动删除项目的主要内容,如果未能解决你的问题,请参考以下文章