Flutter技术求助:如何让ElevatedButton固定在屏幕底部
解决方案
要实现按钮固定在屏幕底部且不遮挡列表的效果,你需要调整页面的布局结构,将列表区域和底部按钮区域分离,让列表占据屏幕中间的可滚动空间,按钮始终固定在底部。以下是修改后的完整代码:
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/material.dart'; import 'package:fyp/assignment/ViewComplete.dart'; import 'ReassignEmployee.dart'; import 'ViewAssigned.dart'; import 'ViewUnassigned.dart'; class assignmentPage extends StatefulWidget { const assignmentPage({Key? key}) : super(key: key); @override State<assignmentPage> createState() => _assignmentPageState(); } class _assignmentPageState extends State<assignmentPage> { TextEditingController searchController = TextEditingController(); String searchText = ''; String id = ''; CollectionReference allNoteCollection = FirebaseFirestore.instance.collection('Assignment'); List<DocumentSnapshot> documents = []; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('View Assignment'), ), body: Column( children: [ // 搜索框固定在顶部 Padding( padding: const EdgeInsets.all(8.0), child: TextField( controller: searchController, onChanged: (value) { setState(() { searchText = value; }); }, decoration: const InputDecoration( hintText: 'Search...', prefixIcon: Icon(Icons.search), ), ), ), // 列表区域占据中间可滚动空间 Expanded( child: StreamBuilder( stream: allNoteCollection.snapshots(), builder: (ctx, streamSnapshot) { if (streamSnapshot.connectionState == ConnectionState.waiting) { return const Center( child: CircularProgressIndicator()); } documents = streamSnapshot.data!.docs; if (searchText.isNotEmpty) { documents = documents.where((element) { final carPlate = element.get('carPlate').toString().toLowerCase(); final custName = element.get('custName').toString().toLowerCase(); final date = element.get('date').toString().toLowerCase(); final employee = element.get('employee').toString().toLowerCase(); final docId = element.get('id').toString().toLowerCase(); final payment = element.get('payment').toString().toLowerCase(); final serviceName = element.get('serviceName').toString().toLowerCase(); final status = element.get('status').toString().toLowerCase(); final timeEnd = element.get('timeEnd').toString().toLowerCase(); final timeStart = element.get('timeStart').toString().toLowerCase(); final lowerSearch = searchText.toLowerCase(); return carPlate.contains(lowerSearch) || custName.contains(lowerSearch) || date.contains(lowerSearch) || employee.contains(lowerSearch) || docId.contains(lowerSearch) || payment.contains(lowerSearch) || serviceName.contains(lowerSearch) || status.contains(lowerSearch) || timeEnd.contains(lowerSearch) || timeStart.contains(lowerSearch); }).toList(); } return ListView.separated( reverse: true, padding: const EdgeInsets.symmetric(horizontal: 8.0), itemCount: documents.length, separatorBuilder: (BuildContext context, int index) { return const Divider(); }, itemBuilder: (BuildContext context, int index) { // 注意:此处赋值会导致id始终是最后一个文档的ID,点击Show unassigned时可能不符合预期 id = documents[index]['id']; return ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 0.0), onTap: () {}, title: Column( children: <Widget>[ Text(documents[index]['carPlate']), Text(documents[index]['custName']), Text(documents[index]['date']), Text(documents[index]['employee']), Text(documents[index]['payment']), Text(documents[index]['serviceName']), Text(documents[index]['status']), Text(documents[index]['timeStart']), Text(documents[index]['timeEnd']), ], ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ InkWell( onTap: (){ Navigator.push(context, MaterialPageRoute(builder: (context) => ReassignEmployee( serviceName: documents[index]['serviceName'], carPlate: documents[index]['carPlate'], custName: documents[index]['custName'], date: documents[index]['date'], timeStart: documents[index]['timeStart'], timeEnd: documents[index]['timeEnd'], payment: documents[index]['payment'], status: documents[index]['status'], employee: documents[index]['employee'], id: documents[index]['id'], ))); },child: const Icon(Icons.edit), ), ], ) ); }, ); }, ), ), // 底部按钮区域固定在屏幕底部 Padding( padding: const EdgeInsets.all(8.0), child: Column( children: [ ElevatedButton( onPressed: () async { Navigator.push(context, MaterialPageRoute(builder: (context) => ViewUnassigned(id: id.toString(),))); }, child: const Text('Show unassigned'), ), const SizedBox(height: 8.0), ElevatedButton( onPressed: () { Navigator.push(context, MaterialPageRoute(builder: (context) => ViewAssigned())); }, child: const Text('Show assigned'), ), ], ), ), ], ), ); } }
关键修改点说明
- 布局结构调整:将原来的
SingleChildScrollView包裹整个内容的结构,改为用Column拆分三个部分:顶部搜索框、中间可滚动列表、底部按钮区。 - Expanded组件:用
Expanded包裹列表区域,让它自动占据屏幕除了顶部搜索框和底部按钮之外的所有空间,保证列表可以独立滚动,不会挤压按钮位置。 - 移除不必要的属性:去掉了列表的
shrinkWrap: true和physics: NeverScrollableScrollPhysics(),让列表自己处理滚动逻辑,提升性能。 - 代码优化:将搜索匹配的逻辑提取变量,提升可读性。
另外注意:当前代码中id变量在列表的itemBuilder中赋值,最终会变成最后一条文档的ID,点击Show unassigned时传递的是最后一条数据的ID,这可能不是你想要的逻辑,建议根据实际需求调整这个变量的使用方式。
内容的提问来源于stack exchange,提问作者Lim
相关产品推荐
相关产品推荐

