从ListView中删除项目的Flutter操作

10

我尝试做:

编写您的第一个Flutter应用程序,第2部分 flutter app页面5

现在我对这个应用程序有一个问题。 我想通过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),
          );
        },
      ),
    );
  }
}
4个回答

12

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(...)
}
)

1
谢谢你的好建议,真的很棒。 - GILO
2
太好了!这里有一个友好的官方示例,演示了Dismissible的用法:https://flutter.dev/docs/cookbook/gestures/dismissible - AnT

11

那是因为你正在创建一个新的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),
    );
  }
}

1
这个可以运行,但你会看到主页并切换到新路由。 - steph
1
你说得对。我会提供一种解决方案来防止这种情况发生(尽管这个方案不像以前那样简单)。 - Sven
总结一下,如果我想编辑页面的内容,我应该创建一个新页面并将该页面推送到导航器。要显示不在同一页上更新的内容,可以通过Navigator.of(context).push( new MaterialPageRoute(来创建页面。 - steph
是的和不是的,之前你已经创建了第二个页面,但没有更新它的状态的方法。现在我们“准备好”了一个有状态的第二个页面,并告诉第一个页面显示它,而不是即时创建一个。 - Sven
这个_state_的概念可能有点抽象。Flutter网站上的这个页面可能会澄清事情! - Sven

6
有点晚了,但我发现如果您想要操作ListView或GridView,则重要的是将Key分配给List/GridView的每个子小部件。
简而言之,Flutter仅按类型而不是状态比较小部件。因此,当在List/GridView中表示的列表的状态更改时,Flutter不知道应该删除哪些子项,因为它们的类型仍然相同并且检查通过。 Flutter唯一注意到的问题是项目数,这就是为什么它仅删除List/GridView中的最后一个小部件。
因此,如果您想在Flutter中操纵列表,请为每个子级的顶级小部件分配一个Key。 这篇文章中提供了更详细的说明。
这可以通过添加实现。
   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') 
            ),

    );
  }),

);

1

步骤1:通过查找特定对象(元素)的索引

List_Name.indexOf(List_Name[index]);

ex:_userTransactions.indexOf(_userTransactions[index]) 

步骤2:删除特定索引处的对象。
LIST_Name.removeAt(index);

ex: _userTransactions.removeAt(index);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接