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

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/";

问题根源分析

  1. SearchBar的leading按钮参数错误:IconButton的onPressed是无参回调,无法直接接收search参数,且未通过控制器获取搜索框输入内容。
  2. 全局变量searchurl滥用:直接修改全局URL会导致后续请求的URL被重复拼接,引发错误。
  3. 页面跳转未传递参数:跳转到SearchResults时未携带搜索关键词,API无法获取正确的查询条件。
  4. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 20:07:02