Flutter搜索框输入字符时页面重复加载问题求助
解决Flutter搜索框输入时页面整体重加载问题
问题根源
- 每次调用
setState会触发整个页面的build方法重新执行,包括外层的StreamBuilder,导致页面整体重绘。 - 未缓存原始数据,每次筛选都直接依赖
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(), ); }, ), ), ], ); }, ), ), ); } }
关键修改说明
- 缓存原始数据:新增
_allData变量保存Firestore返回的所有数据,避免每次筛选重复从快照读取。 - 使用ValueNotifier管理筛选状态:替换原有的
filteredData列表为ValueNotifier,通过更新其value触发局部重绘,而非调用setState刷新整个页面。 - ValueListenableBuilder包裹列表:仅在筛选结果变化时重绘列表区域,页面其他部分(如AppBar、搜索框)不会重新构建。
- 清空搜索恢复原始数据:点击清除按钮时,将筛选结果重置为所有原始数据。
内容的提问来源于stack exchange,提问作者hemanth_101
相关产品推荐
相关产品推荐

