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

Flutter搜索框输入字符时页面重复加载问题求助

解决Flutter搜索框输入时页面整体重加载问题

问题根源

  1. 每次调用setState会触发整个页面的build方法重新执行,包括外层的StreamBuilder,导致页面整体重绘。
  2. 未缓存原始数据,每次筛选都直接依赖snapshot.data,且初始状态filteredData为空,首次加载无数据展示。

解决方案

通过缓存原始数据+使用ValueNotifier局部更新列表,实现仅筛选卡片而不重绘整个页面:

修改后的完整代码

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:ranv1/strings/images.dart';

import '../Detailed Pages/Apartment Detailed Page.dart';
import '../Detailed Pages/Villa Detailed Page.dart';
import '../cardFiles/CommercialCard.dart';
import '../cardFiles/apartmentCard.dart';
import '../cardFiles/farmLandCard.dart';
import '../cardFiles/plotCard.dart';
import '../cardFiles/villaCard.dart';

class BuilderSearch extends StatefulWidget {
  @override
  _BuilderSearchState createState() => _BuilderSearchState();
}

class _BuilderSearchState extends State<BuilderSearch> {
  String propertyTitle = '';
  String propertyDescription = '';
  String propertyAddress = '';
  String propertyBedRooms = '';
  String propertyBathRooms = '';
  String propertyArea = '';
  String propertyPrice = '';
  final List<String> areaInSqft = [];
  
  // 用ValueNotifier管理筛选结果,实现局部更新
  final ValueNotifier<List<DocumentSnapshot>> filteredData = ValueNotifier([]);
  List<DocumentSnapshot> _allData = []; // 缓存所有原始数据
  final TextEditingController searchController = TextEditingController();
  final FirebaseFirestore _firestore = FirebaseFirestore.instance;
  late final User _user = FirebaseAuth.instance.currentUser!;

  @override
  void dispose() {
    searchController.dispose();
    filteredData.dispose(); // 释放资源
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        appBar: AppBar(
          title: Image.asset(
            rlogo,
            fit: BoxFit.contain,
            height: 30,
          ),
          centerTitle: true,
          leading: const Icon(
            Icons.menu,
            color: Color(0xFF242A38),
          ),
          elevation: 0,
          backgroundColor: Colors.transparent,
        ),
        body: StreamBuilder<QuerySnapshot>(
          stream: _firestore
              .collection('builderData')
              .where('Builder Id', isEqualTo: _user.uid)
              .snapshots(),
          builder:
              (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
            if (snapshot.hasError) {
              return const Center(child: Text('加载出错'));
            }

            if (snapshot.connectionState == ConnectionState.waiting) {
              return const Center(child: CircularProgressIndicator());
            }

            // 缓存原始数据,仅在数据更新时刷新
            if (snapshot.hasData) {
              _allData = snapshot.data!.docs;
              // 初始状态显示所有数据
              if (filteredData.value.isEmpty) {
                filteredData.value = _allData;
              }
            }

            return Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 20),
                  child: Text(
                    'Search Your Listings',
                    style: GoogleFonts.poppins(
                      fontWeight: FontWeight.bold,
                      fontSize: 20,
                    ),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: TextField(
                    controller: searchController,
                    decoration: InputDecoration(
                      hintText: 'Search',
                      suffixIcon: IconButton(
                        onPressed: () {
                          searchController.clear();
                          // 清空搜索时恢复显示所有数据
                          filteredData.value = _allData;
                        },
                        icon: const Icon(Icons.clear),
                      ),
                    ),
                    onChanged: (query) {
                      final result = _allData.where((document) {
                        final data = document.data()! as Map<String, dynamic>;
                        return data['Property Title']
                            .toLowerCase()
                            .contains(query.toLowerCase());
                      }).toList();
                      // 更新筛选结果,仅触发列表重绘
                      filteredData.value = result;
                    },
                  ),
                ),
                Expanded(
                  // 用ValueListenableBuilder监听筛选结果变化,仅重绘列表
                  child: ValueListenableBuilder<List<DocumentSnapshot>>(
                    valueListenable: filteredData,
                    builder: (context, currentData, child) {
                      return ListView(
                        padding: const EdgeInsets.all(10),
                        children: currentData.map((DocumentSnapshot document) {
                          final data = document.data()! as Map<String, dynamic>;
                          return GestureDetector(
                            onTap: () {
                              if (data['Property Type'] == 'Apartment') {
                                Navigator.push(
                                  context,
                                  MaterialPageRoute(
                                    builder: (context) => ApartmentDetailsPage(
                                      propertyTitle: data['Property Title'] ?? '',
                                      maxInSqft: data['Max Sqft'] ?? '',
                                      minInSqft: data['Min Sqft'] ?? '',
                                      propertyDescription:
                                          data['Property Description'] ?? '',
                                      propertyCover: data['Property Cover'] ?? '',
                                      propertyImages: data['Property Images'] ?? [],
                                      documentId: document.id,
                                      propertyType: data['Property Type'] ?? '',
                                      areaInSqft: data['Area Sqft'] ?? '',
                                      propertyStatus: data['Property Status'] ?? '',
                                      totalUnits: data['Total Units'] ?? '',
                                      launchDate: data['Launch Date'] ?? '',
                                      oneBhk: data['One Bhk'] ?? '',
                                      twoBhk: data['Two Bhk'] ?? '',
                                      threeBhk: data['Three Bhk'] ?? '',
                                      totalProjectArea:
                                          data['Total Project Area'] ?? '',
                                      fourBhk: data['Four Bhk'] ?? '',
                                      fivePlusBhk: data['FivePlus Bhk'] ?? '',
                                      selectedUnits: data['Selected Units'] ?? '',
                                      possessDate: data['Possession Date'] ?? '',
                                      propertyReraId: data['Rera Id'] ?? '',
                                      totalFloors: data['Total Floors'] ?? '',
                                      pricePerSqft: data['Price Sqft'],
                                      propertyAddress:
                                          data['Property Address'] ?? '',
                                      img1: data['Image 1'] ?? '',
                                      img2: data['Image 2'] ?? '',
                                      img3: data['Image 3'] ?? '',
                                      img4: data['Image 4'] ?? '',
                                      img5: data['Image 5'] ?? '',
                                      gMapLink: data['Google Maps Link'] ?? '',
                                    ),
                                  ),
                                );
                              }
                              if (data['Property Type'] == 'Villa') {
                                Navigator.push(
                                  context,
                                  MaterialPageRoute(
                                    builder: (context) => VillaDetailsPage(
                                      propertyTitle: data['Property Title'] ?? '',
                                      propertyDescription:
                                          data['Property Description'] ?? '',
                                      propertyCover: data['Property Cover'] ?? '',
                                      propertyImages: data['Property Images'] ?? [],
                                      documentId: document.id,
                                      propertyType: data['Property Type'] ?? '',
                                      propertyStatus: data['Property Status'] ?? '',
                                      totalUnits: data['Total Units'] ?? '',
                                      launchDate: data['Launch Date'] ?? '',
                                      oneBhk: data['One Bhk'] ?? '',
                                      twoBhk: data['Two Bhk'] ?? '',
                                      threeBhk: data['Three Bhk'] ?? '',
                                      totalProjectArea:
                                          data['Total Project Area'] ?? '',
                                      fourBhk: data['Four Bhk'] ?? '',
                                      fivePlusBhk: data['FivePlus Bhk'] ?? '',
                                      selectedUnits: data['Selected Units'] ?? '',
                                      possessDate: data['Possession Date'] ?? '',
                                      propertyReraId: data['Rera Id'] ?? '',
                                      totalFloors: data['Total Floors'] ?? '',
                                      propertyAddress:
                                          data['Property Address'] ?? '',
                                      img1: data['Image 1'] ?? '',
                                      img2: data['Image 2'] ?? '',
                                      img3: data['Image 3'] ?? '',
                                      img4: data['Image 4'] ?? '',
                                      img5: data['Image 5'] ?? '',
                                      totalPrice: data['Total Price'] ?? '',
                                      landInSqyd: data['Land Sqyd'] ?? '',
                                      maxInSqyd: data['Max Sqyd'] ?? '',
                                      minInSqyd: data['Min Sqyd'] ?? '',
                                      gMapLink: data['Google Maps Link'] ?? '',
                                    ),
                                  ),
                                );
                              }
                            },
                            child: Card(
                              child: Container(
                                padding: const EdgeInsets.symmetric(
                                    horizontal: 10, vertical: 10),
                                child: Column(
                                  children: [
                                    if (data['Property Type'] == 'Apartment')
                                      ApartmentCard(data: data),
                                    if (data['Property Type'] == 'Villa')
                                      VillaCard(data: data),
                                    if (data['Property Type'] == 'Plot')
                                      PlotCard(data: data),
                                    if (data['Property Type'] == 'Farm Land')
                                      FarmLandCard(data: data),
                                    if (data['Property Type'] == 'Commercial')
                                      CommercialCard(data: data),
                                  ],
                                ),
                              ),
                            ),
                          );
                        }).toList(),
                      );
                    },
                  ),
                ),
              ],
            );
          },
        ),
      ),
    );
  }
}

关键修改说明

  1. 缓存原始数据:新增_allData变量保存Firestore返回的所有数据,避免每次筛选重复从快照读取。
  2. 使用ValueNotifier管理筛选状态:替换原有的filteredData列表为ValueNotifier,通过更新其value触发局部重绘,而非调用setState刷新整个页面。
  3. ValueListenableBuilder包裹列表:仅在筛选结果变化时重绘列表区域,页面其他部分(如AppBar、搜索框)不会重新构建。
  4. 清空搜索恢复原始数据:点击清除按钮时,将筛选结果重置为所有原始数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 21:38:07