【问题标题】:SetState does not rebuild ListView.builderSetState 不重建 ListView.builder
【发布时间】:2021-08-10 08:03:30
【问题描述】:

我正在尝试创建评论页面。 cmets 列表使用 ListView.builder 列出。并且当用户输入评论时,它将再次重建列表以包含新添加的评论。但不知何故,列表没有重建,我在终端收到这条消息:

更改撰写区域内的内容可能会导致 输入法行为怪异,因此不鼓励。看 https://github.com/flutter/flutter/issues/78827 了解更多详情

新添加的评论仅在我关闭评论页面并重新打开时显示。请帮助我,因为我不确定是什么问题以及如何解决它。

评论页面:

import 'package:flutter/material.dart';

import '../model/model_comment.dart';

class CommentsPage extends StatefulWidget {
  @override
  _CommentsPageState createState() => _CommentsPageState();
}

class _CommentsPageState extends State<CommentsPage> {
  ValueNotifier<int> _counter = ValueNotifier<int>(0);
  TextEditingController _controllerComment = TextEditingController();
  bool _hasComment = false;

  @override
  void dispose() {
    _controllerComment.dispose();
    super.dispose();
  }

  _commentOnSend() {
    setState(() {
      var value = CommentModel(
        avatarUrl: "https://randomuser.me/api/portraits/women/34.jpg",
        name: "Laurent Oslo",
        dateTime: "30 Dec 20 08:00",
        comment: _controllerComment.text,
      );
      CommentModel.dummyData.insert(0, value);
    });
    _controllerComment.clear();
    FocusScope.of(context).unfocus();
  }

  Widget _listView = ListView.builder(
    itemCount: CommentModel.dummyData.length,
    itemBuilder: (context, index) {
      CommentModel _model = CommentModel.dummyData[index];
      return Column(
        children: <Widget>[
          Divider(
            height: 12.0,
          ),
          Container(
            padding: EdgeInsets.fromLTRB(3.0, 3.0, 3.0, 3.0),
            child: ListTile(
              leading: CircleAvatar(
                radius: 24.0,
                backgroundImage: NetworkImage(_model.avatarUrl),
              ),
              title: Text(_model.name),
              subtitle: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(_model.comment),
                  Text(_model.dateTime),
                ],
              ),
            ),
          ),
        ],
      );
    },
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Comments"),
        titleSpacing: 40.0,
      ),
      body: Container(
        child: Column(
          children: [
            Expanded(child: _listView),
            Divider(
              height: 1.0,
            ),
            ListTile(
              leading: Container(
                height: 40.0,
                width: 40.0,
                decoration: BoxDecoration(
                    color: Colors.deepPurple,
                    borderRadius: BorderRadius.all(Radius.circular(50.0))),
                child: CircleAvatar(
                    radius: 50.0,
                    backgroundImage: NetworkImage(
                        "https://randomuser.me/api/portraits/men/83.jpg")),
              ),
              title: TextField(
                  decoration: (InputDecoration(
                    hintText: "Add Comment"
                  )),
                  minLines: 1,
                  maxLines: 5,
                  controller: _controllerComment,
                  onChanged: (val) {
                    setState(() {
                      _counter.value += 1;
                      if (val.isNotEmpty) {
                        _hasComment = true;
                      } else {
                        _hasComment = false;
                      }
                    });
                  }),
              trailing: ValueListenableBuilder(
                valueListenable: _counter,
                builder: (BuildContext context, int value, Widget? child) {
                  return IconButton(
                    onPressed: _hasComment
                        ? () {
                            _commentOnSend();
                          }
                        : null,
                    icon: Icon(Icons.send_sharp,
                        color: _hasComment ? Colors.deepPurple : null),
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

评论模型类:

class CommentModel {
  final String avatarUrl;
  final String name;
  final String dateTime;
  final String comment;

  CommentModel(
      {required this.avatarUrl,
      required this.name,
      required this.dateTime,
      required this.comment});

  static List<CommentModel> dummyData = [
    CommentModel(
      avatarUrl: "https://randomuser.me/api/portraits/women/34.jpg",
      name: "Laurent Oslo",
      dateTime: "30 Dec 20 08:00",
      comment:
          "There is a reason why I implemented it like this. In a comment section, the same comment widget can appear multiple times. So, the keys assigned to each widget needs to be different. Otherwise I won’t be able to refer to a specific widget later on",
    ),
    CommentModel(
      avatarUrl: "https://randomuser.me/api/portraits/women/49.jpg",
      name: "Tracy Wilbur",
      dateTime: "01 Oct 20 17:00",
      comment: "First Comment!",
    ),
    CommentModel(
      avatarUrl: "https://randomuser.me/api/portraits/women/23.jpg",
      name: "Michael Scott",
      dateTime: "30 Sept 20 06:00",
      comment:
          "The idea is simple. Use the prefix with something else to make the key unique. In this case, I’ve used the index value to make them unique. I used the keys in line 25, 51, 56, and 60. See how I’ve done it in these lines.",
    ),
    CommentModel(
      avatarUrl: "https://randomuser.me/api/portraits/men/45.jpg",
      name: "Williams John",
      dateTime: "17 Sept 20 02:00",
      comment: "Join!",
    ),
    CommentModel(
      avatarUrl: "https://randomuser.me/api/portraits/women/77.jpg",
      name: "Claire Rach",
      dateTime: "15 Aug 20 19:00",
      comment:
          "I want the comment section to be hidden away. A user can view comments by tapping to expand a widget. Meaning, the comment section should be collapsible. It will toggle between expanded and collapsed mode when being tapped.",
    ),
    CommentModel(
      avatarUrl: "https://randomuser.me/api/portraits/men/81.jpg",
      name: "Joe Panama",
      dateTime: "05 Jul 20 03:00",
      comment:
          "A comment will have 3 data values which are commenting user details, time of comment posting and the actual text of the comment. I’ve created a “CommentModel” class to create this model.",
    ),
    CommentModel(
      avatarUrl: "https://randomuser.me/api/portraits/men/83.jpg",
      name: "Mark Hamill",
      dateTime: "09 Jun 20 15:00",
      comment:
          "Because comments are part of a post, “PostModel” needs to have a list of comment data. So I’ve modified “PostModel” to have a list of “CommentModel” objects. Refer to the code changes to see what I’ve done.",
    ),
    CommentModel(
      avatarUrl: "https://randomuser.me/api/portraits/men/85.jpg",
      name: "Williams Dafoe",
      dateTime: "25 May 20 20:00",
      comment:
          "Notice lines 18 to 29. I’ve used the “ExpansionTile” widget to create a collapsible list of comments. Each comment is a “_SingleComment” widget implemented in lines 34 to 67.",
    ),
    CommentModel(
      avatarUrl: "https://randomuser.me/api/portraits/men/98.jpg",
      name: "Phillips Mach",
      dateTime: "01 Apr 20 17:00",
      comment:
          "New to app development and flutter in general(high schooler). Can I use this template? Do I have to give credit or can I just use it? At the very least, can I see the source code so I can learn from it?",
    ),
    CommentModel(
      avatarUrl: "https://randomuser.me/api/portraits/men/12.jpg",
      name: "Joe Snowden",
      dateTime: "04 Mar 20 16:00",
      comment: "PM ME!",
    ),
  ];
}

【问题讨论】:

    标签: flutter dart flutter-layout


    【解决方案1】:

    这是因为您将 Listview.builder 放入状态变量中,这是预期的行为,因为状态变量不会在重建中重新初始化。

    如果你想重构,你可以创建一个新函数来返回它:

    ListView getList(){
      return ListView.builder(
        itemCount: CommentModel.dummyData.length,
        itemBuilder: (context, index) {
          CommentModel _model = CommentModel.dummyData[index];
          return Column(
            children: <Widget>[
              Divider(
                height: 12.0,
              ),
              Container(
                padding: EdgeInsets.fromLTRB(3.0, 3.0, 3.0, 3.0),
                child: ListTile(
                  leading: CircleAvatar(
                    radius: 24.0,
                    backgroundImage: NetworkImage(_model.avatarUrl),
                  ),
                  title: Text(_model.name),
                  subtitle: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(_model.comment),
                      Text(_model.dateTime),
                    ],
                  ),
                ),
              ),
            ],
          );
        },
      );
    }
    

    【讨论】:

    • 虽然该方法对作者来说是正确且简单的,但从函数返回小部件并不是最佳实践。更好地将 ListView 重构为自己的 CommentList 无状态小部件。
    • @AbdurRafaySaleem 是的。感谢您的通知。
    猜你喜欢
    • 2021-08-12
    • 2020-07-23
    • 2021-09-19
    • 1970-01-01
    • 1970-01-01
    • 2019-10-30
    • 2020-07-13
    • 2021-03-02
    • 2019-08-30
    相关资源
    最近更新 更多