首页 > 解决方案 > 如何在 Flutter 中配置自定义 Listview 以及自定义可编辑子 ListView?

问题描述

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

概述

以下是功能。

  1. 第 1 步是进入课程的静态字段。
  2. 进入课程后,点击课程字段中的添加按钮将添加任意主题的动态小部件(父列表视图)的ListView。
  3. 加载主题小部件后,单击右侧的添加按钮需要将另一个动态小部件的子列表视图附加到子主题的父(子列表视图)。
  4. 所以,在上图中,2, 4 是父列表视图,3, 5 需要是子列表视图

以下是我的代码。

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

任何帮助/建议将不胜感激。提前致谢。

标签: flutterexpandablelistviewflutter-web

解决方案


你只需要收缩 ListViews:

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

ListView 基本上是一个 CustomScrollView,在其 CustomScrollView.slivers 属性中有一个 SliverList。

如果滚动视图不收缩包裹,那么滚动视图将扩展到滚动方向中允许的最大大小。如果滚动视图在scrollDirection 中有无限约束,则shrinkWrap 必须为真。

您可以在官方文档中了解更多关于ListViewshrinkWrap的信息。


推荐阅读