You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter:如何完整显示ListView.builder列表后添加TextForm组件

解决方案

问题出在你用Expanded包裹ListView.builder,这会强制列表占用Column的剩余空间,导致父级ExpansionTile还没完全展示就被截断。要实现先显示完整父级列表再展示表单,直接把列表和表单都放到CustomScrollView的slivers中即可:

  • 用SliverList替代原来的Expanded+ListView.builder,让列表根据内容自适应高度
  • 用SliverToBoxAdapter包裹下方的表单组件,将其作为滚动体的一部分

修改后的完整代码:

class ContactUsScreen extends StatefulWidget {
  const ContactUsScreen({Key? key}) : super(key: key);

  static const String contactRouteName = "/contactScreen";

  @override
  State<ContactUsScreen> createState() => _ContactUsScreenState();
}

class _ContactUsScreenState extends State<ContactUsScreen> {
  // 补充必要的全局key和控制器定义
  final GlobalKey<FormState> formKey = GlobalKey<FormState>();
  final TextEditingController fullNameController = TextEditingController();
  final TextEditingController contactNumberController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        appBar: AppBar(
          title: const Text("Contact Us"),
          centerTitle: true,
        ),
        body: CustomScrollView(
          slivers: [
            // 父级ExpansionTile列表
            SliverList(
              delegate: SliverChildBuilderDelegate(
                (BuildContext context, int index) {
                  return Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Card(
                      elevation: 5,
                      child: ExpansionTile(
                        key: PageStorageKey<ContactPeople>(salesList[index]),
                        controlAffinity: ListTileControlAffinity.leading,
                        childrenPadding: const EdgeInsets.symmetric(vertical: 10, horizontal: 20),
                        expandedCrossAxisAlignment: CrossAxisAlignment.end,
                        maintainState: true,
                        title: Text(
                          salesList[index].regionDescription,
                          style: const TextStyle(
                            fontWeight: FontWeight.bold,
                            fontSize: 18,
                          ),
                        ),
                        children: salesList[index].people!.map(
                          (peopleRecord) {
                            return ExpansionTile(
                              expandedCrossAxisAlignment: CrossAxisAlignment.start,
                              key: PageStorageKey<ContactUsScreen>(peopleRecord),
                              title: Align(
                                alignment: Alignment.topLeft,
                                child: Row(
                                  children: [
                                    ClipOval(
                                      child: Image.asset(
                                        "assets/images/people/${peopleRecord.avatarImage}",
                                        fit: BoxFit.cover,
                                        width: 60,
                                        height: 60,
                                      ),
                                    ),
                                    const SizedBox(width: 10),
                                    Text(
                                      peopleRecord.name,
                                      style: const TextStyle(fontSize: 15),
                                    ),
                                  ],
                                ),
                              ),
                              children: [
                                Text(
                                  peopleRecord.title,
                                  style: const TextStyle(fontWeight: FontWeight.bold),
                                ),
                                Text(peopleRecord.cellPhoneNumber),
                                Text(peopleRecord.emailAddress),
                                const SizedBox(height: 5),
                              ],
                            );
                          },
                        ).toList(),
                      ),
                    ),
                  );
                },
                childCount: salesList.length,
              ),
            ),
            // 下方的表单区域
            SliverToBoxAdapter(
              child: Padding(
                padding: const EdgeInsets.symmetric(horizontal: 15),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    const SizedBox(height: 15),
                    const Text(
                      "Contact us directly",
                      style: TextStyle(
                        fontWeight: FontWeight.w700,
                        fontSize: 20,
                        color: Colors.black,
                      ),
                    ),
                    Form(
                      key: formKey,
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.center,
                        children: [
                          TextFormField(
                            controller: fullNameController,
                            keyboardType: TextInputType.text,
                            decoration: const InputDecoration(
                              labelText: "Full Name",
                              hintText: "Full Name",
                            ),
                            validator: (inputFieldFullName) {
                              if (inputFieldFullName!.isEmpty) {
                                return "Please enter your Full Name";
                              } else {
                                return null;
                              }
                            },
                          ),
                          TextFormField(
                            controller: contactNumberController,
                            keyboardType: TextInputType.number,
                            decoration: const InputDecoration(
                              labelText: "Contact Number",
                              hintText: "Contact Number",
                            ),
                            validator: (inputFieldContactNumber) {
                              if (inputFieldContactNumber!.isEmpty) {
                                return "Please enter your contact number";
                              } else {
                                return null;
                              }
                            },
                          ),
                          const SizedBox(height: 5),
                          ElevatedButton(
                            onPressed: () {
                              if (formKey.currentState!.validate()) {
                                //Send the email
                              } else {
                                const SnackBar snackBar = SnackBar(
                                  duration: Duration(seconds: 2),
                                  content: Text("Please correct the errors"),
                                );
                                ScaffoldMessenger.of(context).showSnackBar(snackBar);
                              }
                            },
                            child: const Text("Submit"),
                          ),
                          const SizedBox(height: 20),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

关键修改说明

  1. 移除了SliverFillRemaining和外层的Column,直接在CustomScrollView的slivers数组中排列内容
  2. 用SliverList承载所有父级ExpansionTile,它会根据子项总高度自动扩展,不会截断列表
  3. 用SliverToBoxAdapter包裹表单区域,将其转换为Sliver组件,融入滚动体中

这样整个页面是一个连续的滚动区域,先展示所有父级ExpansionTile,滚动到底部就能看到表单部分。

内容的提问来源于stack exchange,提问作者RPN

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 04:45:46