从列表视图中颤动删除项目

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);

【讨论】:

以上是关于从列表视图中颤动删除项目的主要内容,如果未能解决你的问题,请参考以下文章

当列表视图的项目在颤动的视口中可见时,如何更改项目颜色并执行一些操作?

如何在颤动中将格式从列表视图更改为网格视图?

在垂直列表视图中颤动动态高度水平列表视图

自定义滚动谷歌颤动中的水平列表视图

颤动水平列表视图/页面视图,选定元素动画到全宽

如何遍历地图列表并在颤动列表视图中显示文本小部件?