Flutter项目中Goat列表搜索功能失效问题求助
搜索逻辑无效的问题修复
问题描述
我的搜索逻辑无法正常工作,期望输入关键词后能显示过滤后的山羊列表,但实际没有效果。我是编程初学者,Add按钮的插入逻辑是硬编码的。
现有代码
HomeScreen.dart
import 'package:domingo_etr/helpers/db_helper.dart'; import 'package:domingo_etr/models/Goat.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({Key? key}) : super(key: key); @override State<HomeScreen> createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { bool isSearchVisible = false; late FocusNode _searchFocusNode; TextEditingController _searchController = TextEditingController(); @override void initState() { super.initState(); _searchFocusNode = FocusNode(); _searchFocusNode.addListener(_onFocusChange); } @override void dispose() { _searchFocusNode.removeListener(_onFocusChange); _searchFocusNode.dispose(); _searchController.dispose(); super.dispose(); } void _onFocusChange() { if (!_searchFocusNode.hasFocus) { print("TextField value: ${_searchController.text}"); setState(() { isSearchVisible = false; }); _searchController.clear(); } } @override Widget build(BuildContext context) { SystemChrome.setSystemUIOverlayStyle( const SystemUiOverlayStyle( statusBarColor: Colors.white, statusBarIconBrightness: Brightness.dark, systemNavigationBarColor: Colors.white, systemNavigationBarIconBrightness: Brightness.dark, ), ); List<Goat> goats = []; return Scaffold( backgroundColor: Colors.white, appBar: AppBar( backgroundColor: Colors.white, title: Text( "Apacapra", style: GoogleFonts.satisfy( color: Colors.black, fontSize: 30, fontWeight: FontWeight.w900, ), ), actions: [ isSearchVisible ? Expanded( flex: 1, child: Container( margin: EdgeInsets.symmetric(horizontal: 20), child: TextField( controller: _searchController, focusNode: _searchFocusNode, decoration: InputDecoration( hintText: "Search by name", hintStyle: TextStyle(fontWeight: FontWeight.normal), suffix: GestureDetector( onTap: () { _searchFocusNode.unfocus(); }, child: FaIcon( FontAwesomeIcons.xmark, size: 20, color: Colors.black, ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.black, width: 1.0), ), enabledBorder: OutlineInputBorder(), ), onChanged: (value) { setState(() { // Update the existing list based on the entered text goats = goats .where((goat) => goat.pasternId .toLowerCase() .contains(value.toLowerCase()) || goat.breed .toLowerCase() .contains(value.toLowerCase()) || goat.name .toLowerCase() .contains(value.toLowerCase())) .toList(); }); }, ), ), ) : IconButton( onPressed: () { setState(() { isSearchVisible = true; }); _searchFocusNode.requestFocus(); }, icon: FaIcon( FontAwesomeIcons.magnifyingGlass, size: 20, color: Colors.black, ), ), ], ), body: SafeArea( child: Column( children: [ Expanded( child: FutureBuilder( future: DbHelper.getAllGoats(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { // While the Future is still running, display a loading indicator return Center( child: CircularProgressIndicator(), ); } else if (snapshot.hasError) { // Handle errors return Text('Error: ${snapshot.error}'); } else if (snapshot.data == null) { // Handle the case where snapshot.data is null return Text('No data available'); } else { // Data is available, proceed with displaying it goats = snapshot.data!.toList(); return ListView.builder( itemCount: goats.length, itemBuilder: (context, index) { final goat = goats[index]; return ListTile( title: Text('ID: ${goat.pasternId}'), subtitle: Text( 'Breed: ${goat.breed}, Weight: ${goat.currentWeight}, Name: ${goat.name}', ), ); }, ); } }, ), ), ElevatedButton( onPressed: () { final goat = Goat( pasternId: "GT-12", name: "Nene", breed: "Anglo-Nubian", dateOfBirth: "2019-02-12", currentWeight: 40, ); DbHelper.insertGoat(goat); setState(() {}); }, child: Text("Add"), ) ], ), ), ); } }
db_helper.dart
import 'package:domingo_etr/models/Goat.dart'; import 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; class DbHelper { // Constants static const dbName = 'goat_management.db'; static const dbVersion = 1; // Table constants static const tbGoat = 'Goat'; static const tbVaccine = 'Vaccine'; static const tbVitamin = 'Vitamin'; // Columns for the 'Goat' table static const colGoatId = 'goatId'; static const colPasternId = 'pasternId'; static const colName = 'name'; static const colBreed = 'breed'; static const colDateOfBirth = 'dateOfBirth'; static const colCurrentWeight = 'currentWeight'; // Columns for the 'Vaccine' table static const colVaccineId = 'vaccineId'; static const colVaccineGoatId = 'goatId'; // Foreign key reference static const colVaccineName = 'vaccineName'; static const colVaccineBrandName = 'brandName'; static const colVaccineType = 'type'; static const colVaccineDosage = 'dosage'; // Columns for the 'Vitamin' table static const colVitaminId = 'vitaminId'; static const colVitaminGoatId = 'goatId'; // Foreign key reference static const colVitaminName = 'vitaminName'; static const colVitaminBrandName = 'brandName'; static const colVitaminType = 'type'; static const colVitaminDosage = 'dosage'; // Methods for db static Future<Database> openDB() async { var path = join(await getDatabasesPath(), DbHelper.dbName); var db = await openDatabase( path, version: DbHelper.dbVersion, onCreate: (db, version) { _createTables(db); }, onUpgrade: (db, oldVersion, newVersion) { _upgradeTables(db, oldVersion, newVersion); }, ); return db; } static void _createTables(Database db) { // Create the 'Goat' table var goatTableSql = ''' CREATE TABLE IF NOT EXISTS $tbGoat( $colGoatId INTEGER PRIMARY KEY AUTOINCREMENT, $colPasternId TEXT NOT NULL UNIQUE, $colName TEXT NOT NULL, $colBreed TEXT NOT NULL, $colDateOfBirth TEXT NOT NULL, $colCurrentWeight INTEGER NOT NULL ) '''; db.execute(goatTableSql); // Create the 'Vaccine' table var vaccineTableSql = ''' CREATE TABLE IF NOT EXISTS $tbVaccine( $colVaccineId INTEGER PRIMARY KEY AUTOINCREMENT, $colVaccineGoatId INTEGER REFERENCES $tbGoat($colGoatId), $colVaccineName TEXT NOT NULL, $colVaccineBrandName TEXT NOT NULL, $colVaccineType TEXT NOT NULL, $colVaccineDosage TEXT NOT NULL ) '''; db.execute(vaccineTableSql); // Create the 'Vitamin' table var vitaminTableSql = ''' CREATE TABLE IF NOT EXISTS $tbVitamin( $colVitaminId INTEGER PRIMARY KEY AUTOINCREMENT, $colVitaminGoatId INTEGER REFERENCES $tbGoat($colGoatId), $colVitaminName TEXT NOT NULL, $colVitaminBrandName TEXT NOT NULL, $colVitaminType TEXT NOT NULL, $colVitaminDosage INTEGER NOT NULL ) '''; db.execute(vitaminTableSql); } static void _upgradeTables(Database db, int oldVersion, int newVersion) { // Handle table upgrades here if (newVersion <= oldVersion) return; // Drop existing tables and recreate them db.execute('DROP TABLE IF EXISTS $tbGoat'); db.execute('DROP TABLE IF EXISTS $tbVaccine'); db.execute('DROP TABLE IF EXISTS $tbVitamin'); // Recreate tables _createTables(db); } // Methods na CRUD Ops ----------------- // Get a goat static Future<List<Goat>> getAllGoats() async { final Database db = await openDB(); final List<Map<String, dynamic>> maps = await db.query(tbGoat); return List.generate(maps.length, (i) { return Goat( goatId: maps[i]['goatId'], pasternId: maps[i]['pasternId'], name: maps[i]['name'], breed: maps[i]['breed'], dateOfBirth: maps[i]['dateOfBirth'], currentWeight: maps[i]['currentWeight'], ); }); } // Insert a goat record static Future<void> insertGoat(Goat goat) async { final Database db = await openDB(); await db.insert( tbGoat, goat.toMap(), conflictAlgorithm: ConflictAlgorithm.replace, ); } }
问题原因
goats变量作用域错误:在build方法里定义List<Goat> goats = [];,每次setState()触发重建时,变量会被重新初始化,且FutureBuilder会重新执行DbHelper.getAllGoats(),覆盖过滤后的结果。- 过滤逻辑基于已过滤数据:
onChanged中用goats = goats.where(...).toList(),每次输入都在已过滤列表上再次过滤,导致搜索范围越来越小,重建后过滤状态丢失。 FutureBuilder的future直接写在build中:每次build都会重新请求数据库,浪费资源且重置数据。
修复方案
修改_HomeScreenState类,拆分原始数据源和过滤后数据为成员变量,优化数据请求逻辑:
修改后的HomeScreen代码
import 'package:domingo_etr/helpers/db_helper.dart'; import 'package:domingo_etr/models/Goat.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({Key? key}) : super(key: key); @override State<HomeScreen> createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { bool isSearchVisible = false; late FocusNode _searchFocusNode; TextEditingController _searchController = TextEditingController(); // 保存原始所有山羊数据 List<Goat> _allGoats = []; // 保存过滤后的山羊数据,用于显示 List<Goat> _filteredGoats = []; // 保存数据请求的Future,避免重复调用 late Future<List<Goat>> _goatsFuture; @override void initState() { super.initState(); _searchFocusNode = FocusNode(); _searchFocusNode.addListener(_onFocusChange); // 初始化数据请求 _goatsFuture = _fetchAllGoats(); } @override void dispose() { _searchFocusNode.removeListener(_onFocusChange); _searchFocusNode.dispose(); _searchController.dispose(); super.dispose(); } // 获取所有山羊数据并初始化原始列表 Future<List<Goat>> _fetchAllGoats() async { final goats = await DbHelper.getAllGoats(); setState(() { _allGoats = goats; _filteredGoats = goats; }); return goats; } void _onFocusChange() { if (!_searchFocusNode.hasFocus) { print("TextField value: ${_searchController.text}"); setState(() { isSearchVisible = false; // 清空搜索框并恢复原始列表 _searchController.clear(); _filteredGoats = _allGoats; }); } } // 处理搜索过滤逻辑 void _filterGoats(String query) { if (query.isEmpty) { _filteredGoats = _allGoats; } else { final lowerQuery = query.toLowerCase(); _filteredGoats = _allGoats.where((goat) => goat.pasternId.toLowerCase().contains(lowerQuery) || goat.breed.toLowerCase().contains(lowerQuery) || goat.name.toLowerCase().contains(lowerQuery)).toList(); } setState(() {}); } @override Widget build(BuildContext context) { SystemChrome.setSystemUIOverlayStyle( const SystemUiOverlayStyle( statusBarColor: Colors.white, statusBarIconBrightness: Brightness.dark, systemNavigationBarColor: Colors.white, systemNavigationBarIconBrightness: Brightness.dark, ), ); return Scaffold( backgroundColor: Colors.white, appBar: AppBar( backgroundColor: Colors.white, title: Text( "Apacapra", style: GoogleFonts.satisfy( color: Colors.black, fontSize: 30, fontWeight: FontWeight.w900, ), ), actions: [ isSearchVisible ? Expanded( flex: 1, child: Container( margin: EdgeInsets.symmetric(horizontal: 20), child: TextField( controller: _searchController, focusNode: _searchFocusNode, decoration: InputDecoration( hintText: "Search by name", hintStyle: TextStyle(fontWeight: FontWeight.normal), suffix: GestureDetector( onTap: () { _searchFocusNode.unfocus(); }, child: FaIcon( FontAwesomeIcons.xmark, size: 20, color: Colors.black, ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.black, width: 1.0), ), enabledBorder: OutlineInputBorder(), ), onChanged: _filterGoats, // 调用过滤方法 ), ), ) : IconButton( onPressed: () { setState(() { isSearchVisible = true; }); _searchFocusNode.requestFocus(); }, icon: FaIcon( FontAwesomeIcons.magnifyingGlass, size: 20, color: Colors.black, ), ), ], ), body: SafeArea( child: Column( children: [ Expanded( child: FutureBuilder( future: _goatsFuture, // 使用成员变量的Future builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Center( child: CircularProgressIndicator(), ); } else if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); }
相关产品推荐
相关产品推荐

