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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 10:20:40