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), ], ), ), ], ), ), ), ], ), ), ); } }
关键修改说明
- 移除了
SliverFillRemaining和外层的Column,直接在CustomScrollView的slivers数组中排列内容 - 用
SliverList承载所有父级ExpansionTile,它会根据子项总高度自动扩展,不会截断列表 - 用
SliverToBoxAdapter包裹表单区域,将其转换为Sliver组件,融入滚动体中
这样整个页面是一个连续的滚动区域,先展示所有父级ExpansionTile,滚动到底部就能看到表单部分。
内容的提问来源于stack exchange,提问作者RPN
相关产品推荐
相关产品推荐

