在 ListView 中,向下滚动时,在颤动中丢失数据

Posted

技术标签:

【中文标题】在 ListView 中,向下滚动时,在颤动中丢失数据【英文标题】:In ListView, on scrolling down, loosing data in flutter 【发布时间】:2020-12-30 12:44:02 【问题描述】:

我有一种情况,我需要在滚动时维护自定义小部件的列表视图的状态。

以下是功能。

我有一个列表视图,每个列表视图都包含一个文本字段。在每个 ListView 项中,都有一个与每个项关联的嵌套列表,它是另一个 TextField。

ListView 和嵌套的 ListView 是动态创建的。但是在滚动到最后时,文本字段中的文本(父列表视图小部件和子列表视图小部件)被清除并且不保持状态。

以下是我的代码。

import 'package:flutter/material.dart';

void main() 
  runApp(MaterialApp(debugShowCheckedModeBanner: false, home: NewCourse()));


class NewCourse extends StatefulWidget 
  @override
  _NewCourseState createState() => _NewCourseState();


class _NewCourseState extends State<NewCourse> 
  bool isTagSelected = false;
  bool isTopicCreationEnabled = false;

  List<NewTopic> newTopicList = [];

  addNewTopic() 
    newTopicList.add(new NewTopic());
    setState(() );
  

  enableTopicCreation(String txtTopicName) 
    setState(() 
      if (txtTopicName.length > 0) 
        isTopicCreationEnabled = true;
       else 
        isTopicCreationEnabled = false;
      
    );
  

  @override
  Widget build(BuildContext context) 
    var _createNewTopic;

    if (isTopicCreationEnabled) 
      _createNewTopic = () 
        addNewTopic();
      ;
     else 
      _createNewTopic = null;
    

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.blueGrey,
        title: Text('ALL COURSES'),
        centerTitle: true,
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          Container(
            color: Colors.blueGrey,
            child: Center(
              child: Padding(
                padding: EdgeInsets.all(20),
                child: Text(
                  "NEW COURSE",
                  style: TextStyle(
                    fontSize: 20,
                    fontWeight: FontWeight.bold,
                    fontFamily: 'CodeFont',
                    color: Colors.white,
                  ),
                ),
              ),
            ),
          ),
          Container(
            padding: EdgeInsets.all(5),
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.circular(10),
              boxShadow: [
                BoxShadow(
                  color: Colors.grey[400],
                  blurRadius: 20.0,
                  offset: Offset(0, 10),
                ),
              ],
            ),
            child: Column(
              children: <Widget>[
                Row(
                  mainAxisAlignment: MainAxisAlignment.start,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    Expanded(
                      flex: 9,
                      child: Container(
                        padding: EdgeInsets.all(8),
                        margin: EdgeInsets.all(8),
                        child: TextField(
                          onChanged: (text) 
                            enableTopicCreation(text);
                          ,
                          decoration: InputDecoration(
                            border: InputBorder.none,
                            hintText: "Course Name",
                            hintStyle: TextStyle(color: Colors.grey[400]),
                          ),
                        ),
                      ),
                    ),
                    Expanded(
                      flex: 3,
                      child: FlatButton(
                        onPressed: _createNewTopic,
                        child: Container(
                          padding: EdgeInsets.all(18),
                          margin: EdgeInsets.all(8),
                          child: Icon(
                            Icons.add_box,
                            color: isTopicCreationEnabled
                                ? Colors.green
                                : Colors.blueGrey,
                          ),
                        ),
                      ),
                    ),
                  ],
                ),
              ],
            ),
          ),
          Container(
            child: Expanded(
              child: getAllTopicsListView(),
            ),
          ),
        ],
      ),
    );
  

  Widget getAllTopicsListView() 
    ListView topicList = new ListView.builder(
        shrinkWrap: true,
        itemCount: newTopicList.length,
        itemBuilder: (context, index) 
          return new ListTile(
            title: new NewTopic(),
          );
        );
    return topicList;
  


class NewTopic extends StatefulWidget 
  @override
  _NewTopicState createState() => _NewTopicState();


class _NewTopicState extends State<NewTopic> 
  TextEditingController _topicController;

  @override
  void initState() 
    super.initState();
    _topicController = new TextEditingController();
  

  @override
  void dispose() 
    super.dispose();
    _topicController.dispose();
  
  bool isSubTopicCreationEnabled = false;

  List<NewSubTopic> newSubTopicList = [];

  addNewSubTopic() 
    setState(() 
      newSubTopicList.add(new NewSubTopic());
    );
  

  enableSubTopicCreation(String txtTopicName) 
    setState(
      () 
        if (txtTopicName.length > 0) 
          isSubTopicCreationEnabled = true;
         else 
          isSubTopicCreationEnabled = false;
        
      ,
    );
  

  @override
  Widget build(BuildContext context) 
    var _createNewSubTopic;

    if (isSubTopicCreationEnabled) 
      _createNewSubTopic = () 
        addNewSubTopic();
      ;
     else 
      _createNewSubTopic = null;
    

    return Column(
      children: [
        Container(
          margin: EdgeInsets.only(top: 20, bottom: 20, left: 10, right: 50),
          padding: EdgeInsets.all(20),
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.circular(10),
            boxShadow: [
              BoxShadow(
                color: Colors.grey[400],
                blurRadius: 20.0,
                offset: Offset(0, 10),
              ),
            ],
          ),
          child: Center(
            child: Column(
              children: <Widget>[
                Row(
                  mainAxisAlignment: MainAxisAlignment.start,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    Expanded(
                      flex: 9,
                      child: Container(
                        child: TextField(
                          controller: _topicController,
                          onChanged: (text) 
                            enableSubTopicCreation(text);
                          ,
                          decoration: InputDecoration(
                            border: InputBorder.none,
                            hintText: "Enter the topic",
                            hintStyle: TextStyle(color: Colors.grey[400]),
                          ),
                        ),
                      ),
                    ),
                    Expanded(
                      flex: 3,
                      child: FlatButton(
                        onPressed: _createNewSubTopic,
                        child: Container(
                          child: Icon(
                            Icons.add_box,
                            color: isSubTopicCreationEnabled
                                ? Colors.green
                                : Colors.blueGrey,
                          ),
                        ),
                      ),
                    ),
                  ],
                ),
                Row(
                  children: <Widget>[
                    Container(
                      child: Expanded(
                        //child: Text("Hi There!"),
                        child: getAllSubTopicsListView(),
                      ),
                    ),
                  ],
                ),
              ],
            ),
          ),
        ),
      ],
    );
  

  Widget getAllSubTopicsListView() 
    ListView subTopicList = new ListView.builder(
       shrinkWrap: true,
       itemCount: newSubTopicList.length,
       itemBuilder: (context, index) 
         return new ListTile(
           title: new NewSubTopic(),
         );
       ,
     );
     return subTopicList;
  


class NewSubTopic extends StatefulWidget 
  @override
  _NewSubTopicState createState() => _NewSubTopicState();


class _NewSubTopicState extends State<NewSubTopic> 
  TextEditingController _subtopicController;

  @override
  void initState() 
    super.initState();
    _subtopicController = new TextEditingController();
  

  @override
  void dispose() 
    super.dispose();
    _subtopicController.dispose();
  
  
  @override
  Widget build(BuildContext context) 
    return Column(
      children: [
        Container(
          margin: EdgeInsets.only(top: 20, bottom: 20, left: 50, right: 10),
          padding: EdgeInsets.all(20),
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.circular(10),
            boxShadow: [
              BoxShadow(
                color: Colors.grey[400],
                blurRadius: 20.0,
                offset: Offset(0, 10),
              ),
            ],
          ),
          child: Center(
            child: Container(
              child: TextField(
                controller: _subtopicController,
                decoration: InputDecoration(
                  border: InputBorder.none,
                  hintText: "Enter the sub topic",
                  hintStyle: TextStyle(color: Colors.grey[400]),
                ),
              ),
            ),
          ),
        ),
      ],
    );
  

我已经使用 TextEditingController 来维护状态,但没有解决问题。

问题: 一旦我向下滚动并滚动回屏幕顶部,就无法保持状态。清除父子项中 TextField 小部件中输入的文本。任何建议将不胜感激,谢谢。

【问题讨论】:

发生这种情况是因为,当 ListView 的元素未显示在屏幕上时,颤振会删除它们。因此,您的文本控制器会被丢弃,留下一个空的 TextField。 我已经使用 TextEditingController 来设置状态,但它不起作用。那么可能的解决方案是什么? 我没有尝试过,但我认为您必须制作一个包含文本字段的自定义有状态小部件,以便您可以将值存储在状态变量中,即使控制器被处置您的 wigdet 仍将具有状态变量中的值。只需确保将关键参数传递给您的自定义小部件,以便颤振可以区分列表中的所有项目。 如果您的列表很小,您可以使用常规的 ListView 小部件,而不是构建器。常规的 afaik 不会执行任何性能改进,并且会保留所有正在处理的数据,即使它不在屏幕上 我可以使用常规的 ListView 小部件,但每次单击添加图标时都会创建动态 ListView。 【参考方案1】:

添加这个

class _HomeHomeMadeListState extends State<HomeHomeMadeList> with AutomaticKeepAliveClientMixin    class _HomeHomeMadeListState extends State<HomeHomeMadeList> 

  @override 
  bool get wantKeepAlive => true;

【讨论】:

【参考方案2】:

我在开发我的一个应用程序时遇到了同样的问题。

我通过将ListView 替换为用SingleChildScrollView 包裹的Column 来解决此问题。

这是因为ListView 仅在屏幕上呈现可见项目。

因此,每当您向上滚动时,子小部件和其中的状态(数据)都会被破坏,并且必须重新运行应用程序才能检索状态。

【讨论】:

以上是关于在 ListView 中,向下滚动时,在颤动中丢失数据的主要内容,如果未能解决你的问题,请参考以下文章

在颤动中向上/向下滚动列表时如何在列表中插入和删除项目?

如何在颤动中设置滚动条颜色?

如何在颤动中使我的文本与 listview.builder 一起滚动

如何使我的 ListView 在颤动中可滚动?

Android中ListView包含CheckBox时滑动丢失选中状态的解决

在listview中颤动Custompaint:忽略两个手指滚动