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

Flutter中如何传递变量并从API获取channelName与appId?

解决方案

一、核心错误梳理

  1. API请求格式错误:原URL拼接为https://jsonplaceholder.typicode.com/posts/1=$myController,不符合HTTP查询参数规范,正确参数传递应使用?开头(如https://xxx?channelName=$myController,需匹配实际API参数名)。
  2. 状态码判断错误:把成功状态码200写成2000,导致永远无法进入数据解析逻辑。
  3. ApiService结构错误:getData嵌套在loadData内部,外部无法调用;且loadData未返回数据、未触发请求。
  4. 数据转换语法错误:map转换时.toList()位置错误,无法生成List<Data>。
  5. 页面跳转逻辑错误:Navigator.push的builder要求返回Widget,但直接调用了异步函数loadData,逻辑完全错误。
  6. 模型类型不匹配:Data类成员是required String,但fromJson允许返回null,导致类型报错。
  7. FutureBuilder使用错误:调用了不存在的client.getData(),且ListView在Column中未设置shrinkWrap: true,引发布局异常。

二、修正后的代码实现

1. 修正ApiService类

import 'dart:convert';
import 'package:http/http.dart' as http;
import '../model/appIdModel.dart';

class ApiService {
  // 接收频道名,发起API请求
  Future<List<Data>> fetchChannelData(String channelName) async {
    // 替换为实际API地址,示例用查询参数传递频道名
    final String url = 'https://jsonplaceholder.typicode.com/posts?channelName=$channelName';
    
    final response = await http.get(Uri.parse(url));
    
    if (response.statusCode == 200) {
      Map<String, dynamic> jsonResponse = jsonDecode(response.body);
      List<dynamic> dataList = jsonResponse['data'];
      return dataList.map((item) => Data.fromJson(item)).toList();
    } else {
      throw Exception('无法获取数据,状态码:${response.statusCode}');
    }
  }
}

2. 修正Data模型类

class Data {
  String? appId;
  String? channelName;

  Data({
    this.appId,
    this.channelName,
  });

  factory Data.fromJson(Map<String, dynamic> json) {
    return Data(
      appId: json['appId'],
      channelName: json['channelName'],
    );
  }
}

3. 创建数据展示页面

import 'package:flutter/material.dart';
import '../service/apiService.dart';
import '../model/appIdModel.dart';

class ChannelDataPage extends StatelessWidget {
  final String channelName;
  final ApiService apiService = ApiService();

  ChannelDataPage({required this.channelName, Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('频道数据')),
      body: FutureBuilder<List<Data>>(
        future: apiService.fetchChannelData(channelName),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(child: CircularProgressIndicator());
          } else if (snapshot.hasError) {
            return Center(child: Text('错误:${snapshot.error}'));
          } else if (snapshot.hasData && snapshot.data!.isNotEmpty) {
            final data = snapshot.data!;
            return ListView.builder(
              shrinkWrap: true,
              itemCount: data.length,
              itemBuilder: (context, index) {
                return Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text('频道名:${data[index].channelName ?? '未获取到'}'),
                      const SizedBox(height: 8),
                      Text('AppID:${data[index].appId ?? '未获取到'}'),
                    ],
                  ),
                );
              },
            );
          } else {
            return const Center(child: Text('无数据'));
          }
        },
      ),
    );
  }
}

4. 修正首页逻辑

class _MyHomePageState extends State<MyHomePage> {
  final myController = TextEditingController();
  bool _validateError = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Center(
          child: SingleChildScrollView(
            clipBehavior: Clip.antiAliasWithSaveLayer,
            physics: const BouncingScrollPhysics(),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const SizedBox(height: 20),
                const SizedBox(height: 20),
                Container(
                  width: MediaQuery.of(context).size.width * 0.8,
                  child: TextFormField(
                    controller: myController,
                    decoration: InputDecoration(
                      labelText: 'Channel Name',
                      labelStyle: const TextStyle(color: Colors.blue),
                      hintText: 'test',
                      hintStyle: const TextStyle(color: Colors.black45),
                      errorText: _validateError ? 'Channel name is mandatory' : null,
                      border: OutlineInputBorder(
                        borderSide: const BorderSide(color: Colors.blue),
                        borderRadius: BorderRadius.circular(20),
                      ),
                      enabledBorder: OutlineInputBorder(
                        borderSide: const BorderSide(color: Colors.blue),
                        borderRadius: BorderRadius.circular(20),
                      ),
                      disabledBorder: OutlineInputBorder(
                        borderSide: const BorderSide(color: Colors.blue),
                        borderRadius: BorderRadius.circular(20),
                      ),
                      focusedBorder: OutlineInputBorder(
                        borderSide: const BorderSide(color: Colors.blue),
                        borderRadius: BorderRadius.circular(20),
                      ),
                    ),
                  ),
                ),
                const SizedBox(height: 30),
                Container(
                  width: MediaQuery.of(context).size.width * 0.25,
                  child: MaterialButton(
                    onPressed: onJoin,
                    height: 40,
                    color: Colors.blueAccent,
                    child: const Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: <Widget>[
                        Text(
                          'Join',
                          style: TextStyle(color: Colors.white),
                        ),
                        Icon(
                          Icons.arrow_forward,
                          color: Colors.white,
                        ),
                      ],
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Future<void> onJoin() async {
    setState(() {
      _validateError = myController.text.isEmpty;
    });

    if (!_validateError) {
      // 跳转到数据展示页面,传入频道名
      Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => ChannelDataPage(channelName: myController.text),
        ),
      );
    }
  }
}

三、关键说明

  • 确保API地址和参数格式与实际后端一致,示例中用?channelName=传递参数,需根据真实API调整。
  • 移除首页错误的FutureBuilder,改为跳转到新页面处理数据请求,逻辑更清晰。
  • 将Data类成员改为可空类型,避免null赋值错误。
  • ListView添加shrinkWrap: true,解决Column内布局溢出问题。

内容的提问来源于stack exchange,提问作者Dasun Dola

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 10:35:19