如何在 Flutter 中配置自定义 Listview 以及自定义可编辑子 ListView?

How to configure custom Listview along with custom editable child ListView in Flutter?

我要实现一个功能,我需要为每个包含 TextField 和按钮的小部件配置自定义列表视图。并且子部件包含每个部件的列表视图,其中包含用于进入新课程的 TextField。

功能如下。

  1. 第一步是进入课程的静态字段。
  2. 进入课程后,点击课程栏中的添加按钮,会添加一个任意主题的动态widget(parent listview)的ListView。
  3. 加载主题小部件后,单击右侧的添加按钮需要将另一个动态小部件的子列表视图附加到子主题的父(子列表视图)。
  4. 所以,上图中2、4是父listview,3、5需要是子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(
        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> {
  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(
                          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(
       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> {
  @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(
                decoration: InputDecoration(
                  border: InputBorder.none,
                  hintText: "Enter the sub topic",
                  hintStyle: TextStyle(color: Colors.grey[400]),
                ),
              ),
            ),
          ),
        ),
      ],
    );
  }
}

这是问题所在

Assertion failed: file:///C:/src/flutter/packages/flutter/lib/src/rendering/sliver_multi_box_adaptor.dart:545:12
child.hasSize
is not true

任何 help/suggestion 将不胜感激。 提前致谢。

您只需要对 ListView 进行收缩包装:

        ListView topicList = new ListView.builder(
          shrinkWrap: true,
          itemCount: newTopicList.length,
        //...
        ListView subTopicList = new ListView.builder(
          shrinkWrap: true,
          itemCount: newSubTopicList.length,

A ListView is basically a CustomScrollView with a single SliverList in its CustomScrollView.slivers property.

If the scroll view does not shrink wrap, then the scroll view will expand to the maximum allowed size in the scrollDirection. If the scroll view has unbounded constraints in the scrollDirection, then shrinkWrap must be true.

您可以了解有关 ListView and shrinkWrap in the official documentaion 的更多信息。