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

Flutter Web对接Spring Boot后端登录失败求助

Flutter Web 调用 Spring Boot 登录接口报错排查与解决

问题描述

开发基于Flutter Web的项目,后端采用Spring Boot框架,实现认证登录功能时遇阻:用Insomnia测试登录接口完全正常,但Flutter端调用时出现报错。当前登录页面代码如下:

import 'dart:convert';
import 'package:basic_utils/basic_utils.dart';
import 'dart:io';
import 'package:cryptoo/main.dart';
import 'package:cryptoo/screens/signup.dart';
import 'package:cryptoo/user.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:http/http.dart';

class SignInScreen extends StatefulWidget {   
  static const kBackgroundColor = Colors.white;
  static const kPrimaryColor = Colors.amber;
  @override
  State<SignInScreen> createState() => _SignInScreenState();
}

class _SignInScreenState extends State<SignInScreen> {
  final _formkey = GlobalKey<FormState>();
  User user = User("", "");
  String api = "192.168.1.105";

  Future save() async {
    String url = "http://" + api + ":8011/users/login";
    String username = user.username;
    String password = user.password;
    Map<String, String> headers = {"Accept": "application/json"};
    var res = await http.post(Uri.parse(url),    `problem here .......
        headers: headers,
        body: jsonEncode({'username': 'Yosry1', 'password': 'Hamdi24963'}));
    print(res.statusCode);
    /*print(user.username);
    print(user.password);*/
    if (res.statusCode == 200) {
      await Navigator.push(
          context, MaterialPageRoute(builder: (context) => HomeScreen()));
      print("connected");
    }
  }

  var size, height, width;

  @override
  Widget build(BuildContext context) {
    size = MediaQuery.of(context).size;
    height = size.height;
    width = size.width;
    return Scaffold(
      body: Form(
        key: _formkey,
        child: Container(
          width: width,
          height: height,
          decoration: BoxDecoration(
            color: Color(0xff1f2630),
          ),
          padding: const EdgeInsets.symmetric(horizontal: 16),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Row(
                children: <Widget>[
                  Icon(
                    Icons.alternate_email,
                    color: SignInScreen.kPrimaryColor,
                  ),
                  Container(
                    padding: EdgeInsets.all(10),
                    width: width * 0.8,
                    child: TextFormField(
                      controller: TextEditingController(text: user.username),
                      onChanged: (val) {
                        user.username = val;
                      },
                      validator: (value) {
                        if (value!.isEmpty) {
                          return 'Username is Empty';
                        }
                        return null;
                      },
                      decoration: InputDecoration(
                        hintStyle: TextStyle(color: Colors.white),
                        hintText: "Username",
                      ),
                    ),
                  )
                ],
              ),
              Row(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  Icon(
                    Icons.lock,
                    color: SignInScreen.kPrimaryColor,
                  ),
                  Container(
                    padding: EdgeInsets.all(10),
                    width: width * 0.8,
                    child: TextFormField(
                      controller: TextEditingController(text: user.password),
                      onChanged: (val) {
                        user.password = val;
                      },
                      validator: (value) {
                        if (value!.isEmpty) {
                          return 'Password is Empty';
                        }
                        return null;
                      },
                      decoration: InputDecoration(
                        hintStyle: TextStyle(color: Colors.white),
                        hintText: "Password",
                      ),
                    ),
                  ),
                ],
              ),
              TextButton(
                onPressed: () {
                  Navigator.push(
                      context,
                      MaterialPageRoute(
                        builder: (context) => SignUpScreen(),
                      ));
                },
                child: Text("SingUp"),
              ),
              SizedBox(height: 5),
              Container(
                width: width * 0.7,
                height: 50,
                child: ElevatedButton(
                  onPressed: () async {
                    /* Navigator.push(
                        context,
                        MaterialPageRoute(
                            builder: (context) => HomeScreen();*/
                    //if (_formkey.currentState!.validate()) {}
                    save();
                  },
                  child: Text("SignIn"),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}    

排查与修复步骤

1. 修复代码语法错误

代码中http.post调用行存在多余的反引号,直接导致语法报错,同时当前硬编码了测试账号,应替换为用户输入内容:

// 错误代码
var res = await http.post(Uri.parse(url),    `problem here .......
    headers: headers,
    body: jsonEncode({'username': 'Yosry1', 'password': 'Hamdi24963'}));

// 修正后代码
var res = await http.post(Uri.parse(url),
    headers: headers,
    body: jsonEncode({'username': user.username, 'password': user.password}));

2. 补充请求头参数

Spring Boot接口需要明确识别请求体格式,当前仅添加了Accept头,需补充Content-Type:

Map<String, String> headers = {
  "Accept": "application/json",
  "Content-Type": "application/json"
};

3. 解决CORS跨域问题(核心)

Flutter Web运行在浏览器环境下受同源策略限制,这是接口测试正常但前端调用失败的最常见原因。需要在Spring Boot端配置CORS:

  • 全局配置示例:
@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:5000") // 替换为你的Flutter Web运行地址
                .allowedMethods("GET", "POST", "PUT", "DELETE")
                .allowedHeaders("*")
                .allowCredentials(true);
    }
}
  • 或在登录接口方法上添加注解:
@CrossOrigin(origins = "http://localhost:5000")
@PostMapping("/users/login")
public ResponseEntity<?> login(@RequestBody UserLoginRequest request) {
    // 登录逻辑实现
}

4. 捕获并输出详细错误信息

修改save方法,捕获异常并打印完整错误信息,方便定位问题:

Future save() async {
  try {
    String url = "http://" + api + ":8011/users/login";
    Map<String, String> headers = {
      "Accept": "application/json",
      "Content-Type": "application/json"
    };
    var res = await http.post(Uri.parse(url),
        headers: headers,
        body: jsonEncode({'username': user.username, 'password': user.password}));
    print("状态码: ${res.statusCode}");
    print("响应内容: ${res.body}");
    if (res.statusCode == 200) {
      await Navigator.push(
          context, MaterialPageRoute(builder: (context) => HomeScreen()));
      print("登录成功");
    } else {
      print("登录失败: ${res.body}");
    }
  } catch (e) {
    print("请求异常: $e");
  }
}

5. 验证网络连通性

确保Flutter Web运行设备能访问192.168.1.105:8011,可直接在浏览器中输入接口地址测试是否能正常响应。


内容的提问来源于stack exchange,提问作者Mohamed hedi Hamdi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 12:31:03