Flutter图书应用:搜索栏调用API并跳转结果页的问题
图书应用搜索功能问题:无法传递搜索文本到API及页面跳转失败
我正在开发一款图书应用,想要实现搜索栏功能:将输入的内容拼接到URL后发起GET请求,但目前无法把搜索文本传递给API,也完成不了页面跳转。以下是相关代码:
Main.dart代码
import 'package:betterreadsui/api_calls.dart'; import 'package:flutter/material.dart'; import 'signUp.dart'; import 'searchResults.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Better Reads', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: const MyHomePage(title: "Better Reads"), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { late final SearchController controller; @override void initState() { super.initState(); controller = SearchController(); } @override void dispose() { controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), bottomNavigationBar: BottomNavigationBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, items:const <BottomNavigationBarItem> [ BottomNavigationBarItem( icon: Icon(Icons.home), label: 'Home' ), BottomNavigationBarItem( icon: Icon(Icons.library_books_rounded), label: 'My Books' ), BottomNavigationBarItem( icon: Icon(Icons.settings), label: 'Settings' ), ], ), body: HomePage( ctrl: controller,)//this line calls my home page and a ); } } //lays out the homepage class HomePage extends StatelessWidget { const HomePage({super.key, required this.ctrl}); final SearchController ctrl; @override Widget build(BuildContext context){ return Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Align( alignment: Alignment.topRight, child: Padding( padding:const EdgeInsets.only(top: 10), child: SearchBar( controller: ctrl, leading:const IconButton( icon: Icon(Icons.search_rounded), onPressed: (search){ searchurl = searchurl + search; Navigator.of(context).push(MaterialPageRoute(builder: (context)=>const SearchResults())); }, ), trailing:const <Widget>[ IconButton( onPressed: null, icon: Icon(Icons.camera_alt_rounded) ), ], ), ), ), //Place holder lists not full implemented yet and probably not related to my problem const Text( 'Your Books', ), const SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: <Widget>[ SizedBox( height: 100, width: 50, child: Placeholder( color: Colors.black, ), ), SizedBox( height: 100, width: 50, child: Placeholder( color: Colors.blue, fallbackHeight: 100, fallbackWidth: 50, ), ), SizedBox( height: 100, width: 50, child: Placeholder( color: Colors.red, fallbackHeight: 100, fallbackWidth: 50, ), ), SizedBox( height: 100, width: 50, child: Placeholder( color: Colors.green, fallbackHeight: 100, fallbackWidth: 50, ), ), SizedBox( height: 100, width: 50, child: Placeholder( color: Colors.pink, fallbackHeight: 100, fallbackWidth: 50, ), ), SizedBox( height: 100, width: 50, child: Placeholder( color: Colors.purple, fallbackHeight: 100, fallbackWidth: 50, ), ), SizedBox( height: 100, width: 50, child: Placeholder( color: Colors.orange, fallbackHeight: 100, fallbackWidth: 50, ), ), SizedBox( height: 100, width: 50, child: Placeholder( color: Colors.brown, fallbackHeight: 100, fallbackWidth: 50, ), ), SizedBox( height: 100, width: 50, child: Placeholder( color: Colors.grey, fallbackHeight: 100, fallbackWidth: 50, ), ), ], ), ), ], ); } //tried to use this function but still gave me errors in on pressed void searchBtn(String search, BuildContext context){ searchurl = searchurl + search; Navigator.push(context, MaterialPageRoute(builder: (context)=>const SearchResults())); } }
控制器代码
late final SearchController controller; @override void initState() { super.initState(); controller = SearchController(); } @override void dispose() { controller.dispose(); super.dispose(); }
api_calls.dart代码
import 'dart:async'; import 'package:http/http.dart' as http; import 'Books.dart'; import 'dart:convert'; Future<List<Book>> fetchBooks() async { final response = await http.get(Uri.parse(searchurl)); if(response.statusCode==200){ var json = response.body; //log('data: $json'); return decodeBook(json); }else{ throw Exception("Somtehing went wrong"); } } List<Book> decodeBook(String responseBody){ final parsed = json.decode(responseBody).cast<Map<String, dynamic>>(); return parsed.map<Book> ((json)=>Book.fromMap(json)).toList(); } String searchurl = "http://10.0.2.2:8080/Books/";
问题根源分析
- SearchBar的leading按钮参数错误:
IconButton的onPressed是无参回调,无法直接接收search参数,且未通过控制器获取搜索框输入内容。 - 全局变量
searchurl滥用:直接修改全局URL会导致后续请求的URL被重复拼接,引发错误。 - 页面跳转未传递参数:跳转到
SearchResults时未携带搜索关键词,API无法获取正确的查询条件。 IconButton被标记为const:const组件无法访问上下文context和外部控制器ctrl,会导致编译或运行异常。
修正方案
1. 修复Main.dart中搜索按钮逻辑
移除IconButton的const修饰,通过控制器获取输入内容,跳转时传递参数给结果页:
class HomePage extends StatelessWidget { const HomePage({super.key, required this.ctrl}); final SearchController ctrl; @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Align( alignment: Alignment.topRight, child: Padding( padding: const EdgeInsets.only(top: 10), child: SearchBar( controller: ctrl, // 移除const修饰,允许访问上下文和控制器 leading: IconButton( icon: const Icon(Icons.search_rounded), onPressed: () { final searchText = ctrl.text.trim(); if (searchText.isNotEmpty) { // 跳转时传递搜索关键词 Navigator.of(context).push( MaterialPageRoute( builder: (context) => SearchResults(searchKeyword: searchText), ), ); ctrl.clear(); // 清空搜索框 } }, ), trailing: const <Widget>[ IconButton( onPressed: null, icon: Icon(Icons.camera_alt_rounded), ), ], ), ), ), // 其余代码保持不变 ], ); } }
2. 重构api_calls.dart的API请求函数
让fetchBooks接收搜索关键词参数,动态生成请求URL,不再依赖全局变量:
import 'dart:async'; import 'package:http/http.dart' as http; import 'Books.dart'; import 'dart:convert'; Future<List<Book>> fetchBooks(String searchKeyword) async { final baseUrl = "http://10.0.2.2:8080/Books/"; final response = await http.get(Uri.parse('$baseUrl$searchKeyword')); if (response.statusCode == 200) { var json = response.body; return decodeBook(json); } else { throw Exception("请求失败"); } } List<Book> decodeBook(String responseBody) { final parsed = json.decode(responseBody).cast<Map<String, dynamic>>(); return parsed.map<Book>((json) => Book.fromMap(json)).toList(); }
3. 实现SearchResults页面接收参数并发起请求
确保结果页能接收搜索关键词,调用API获取数据:
import 'package:flutter/material.dart'; import 'api_calls.dart'; import 'Books.dart'; class SearchResults extends StatefulWidget { final String searchKeyword; const SearchResults({super.key, required this.searchKeyword}); @override State<SearchResults> createState() => _SearchResultsState(); } class _SearchResultsState extends State<SearchResults> { late Future<List<Book>> futureBooks; @override void initState() { super.initState(); // 用传递的关键词发起API请求 futureBooks = fetchBooks(widget.searchKeyword); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("搜索结果")), body: FutureBuilder<List<Book>>( future: futureBooks, builder: (context, snapshot) { if (snapshot.hasData) { return ListView.builder( itemCount: snapshot.data!.length, itemBuilder: (context, index) { return ListTile(title: Text(snapshot.data![index].title)); }, ); } else if (snapshot.hasError) { return Center(child: Text("${snapshot.error}")); } return const Center(child: CircularProgressIndicator()); }, ), ); } }
内容的提问来源于stack exchange,提问作者cynlla
相关产品推荐
相关产品推荐

