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

Flutter集成Mercado Pago Pix支付时出现Null转String类型错误

Mercado Pago Pix支付实现中Null类型错误问题

问题现象

调用Navigator.of(context).push(MaterialPageRoute(builder: ((context) => TelaDadosPix(resultado))))时触发类型错误,提示传递了Null而非String类型值。实际错误根源是TelaDadosPix的build方法中,读取widget.dadosPix["status"]、widget.dadosPix["qr_code"]等字段时这些值为Null,但Text组件要求必须传入String类型。

报错信息

EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following TypeErrorImpl was thrown building TelaDadosPix(dirty, state: _TelaDadosPix#e6117):
Expected a value of type 'String', but got one of type 'Null'
The relevant error-causing widget was:
  TelaDadosPix
TelaDadosPix:file:///C:/Users/pedro/OneDrive/%C3%81rea%20de%20Trabalho/Horario/horario/lib/features/pix/presentation/cria_chave_pix.dart:52:52
When the exception was thrown, this was the stack:
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 294:49      throw_
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 127:3       castError
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 742:12  cast
dart-sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart 674:17                as
packages/horario/features/pix/presentation/tela_dados_pix.dart 50:21              build
packages/flutter/src/widgets/framework.dart 5409:27                               build
packages/flutter/src/widgets/framework.dart 5297:15                               performRebuild
packages/flutter/src/widgets/framework.dart 5462:11                               performRebuild
packages/flutter/src/widgets/framework.dart 5016:7                                rebuild
packages/flutter/src/widgets/framework.dart 5279:5                                [_firstBuild]
packages/flutter/src/widgets/framework.dart 5453:11                               [_firstBuild]
packages/flutter/src/widgets/framework.dart 5273:5                                mount
packages/flutter/src/widgets/framework.dart 4182:15                               inflateWidget
packages/flutter/src/widgets/framework.dart 3707:18                               updateChild
packages/flutter/src/widgets/framework.dart 6434:14                               mount
packages/flutter/src/widgets/framework.dart 4182:15                               inflateWidget
packages/flutter/src/widgets/framework.dart 3707:18                               updateChild

相关代码

CriaChavePix类

import 'dart:convert';
import 'package:mercadopago_sdk/mercadopago_sdk.dart';
import 'package:mp_flutter/mp_flutter.dart';

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:horario/core/configs/route.dart';
import 'package:horario/features/pix/presentation/tela_dados_pix.dart';
import 'package:http/http.dart' as http;


class CriaChavePix extends StatefulWidget {
  Map<String, dynamic> dadosPix;
  CriaChavePix(this.dadosPix);

  @override
  _CriaChavePix createState() => _CriaChavePix();
}

class _CriaChavePix extends State<CriaChavePix> {
  String chavePix = '';

  Future<void> criaChavePix() async {
    var bodyMap = {
      "transaction_amount": 2000,
      "payment_method_id": "pix",
      "payer": {
        "email": "test@mail.com",
        "first_name": "first_name",
        "last_name": "last_name",
        "identification": {
          "type": "identification_type",
          "number": "number_cpf"
        }
      }
    };

    var url = Uri.parse('https://api.mercadopago.com/v1/payments/');

    var response = await http.post(url,
        headers: {
          "Authorization":
              "Bearer ...",
        },
        body: json.encode(bodyMap));

    if (response.statusCode == 201) {
      Map<String, dynamic> resultado = json.decode(response.body);

      Navigator.of(context).push(
          MaterialPageRoute(builder: ((context) => TelaDadosPix(resultado))));
    } else {
      throw Exception(response.body);
    }
  }

  @override
  void initState() {
    super.initState();
    criaChavePix();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      child: Center(
        child: CircularProgressIndicator(
          color: Colors.blueAccent,
        ),
      ),
    );
  }
}

TelaDadosPix类

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;

class TelaDadosPix extends StatefulWidget {
  Map<String, dynamic> dadosPix;

  TelaDadosPix(this.dadosPix);

  _TelaDadosPix createState() => _TelaDadosPix();
}

class _TelaDadosPix extends State<TelaDadosPix> {
  String statusPix = " ";

  Future<void> pesquisaStatusPix() async {
    var url = Uri.parse('https://api.mercadopago.com/v1/payments/');
    var response =
        await http.post(url, body: {"idPix": widget.dadosPix["idPIX"]});

    statusPix = response.body;
  }

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView(
        children: <Widget>[
          Column(
            children: <Widget>[
              SizedBox(
                  width: double.infinity,
                  height: 200.0,
                  child: Image.asset("assets/images/img_pix.jpeg",
                      fit: BoxFit.cover)),
              Padding(
                padding: const EdgeInsets.all(12.0),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: <Widget>[
                    Text("Status do Pagamento Pix:",
                        style: TextStyle(
                            fontSize: 25.0, fontWeight: FontWeight.bold)),
                    Text(widget.dadosPix["status"],
                        style: TextStyle(fontSize: 18.0, color: Colors.green)),
                    SizedBox(
                      height: 16.0,
                    ),
                    Text(widget.dadosPix["total_a_pagar"].toString(),
                        style: TextStyle(fontSize: 25.0, color: Colors.green))
                  ],
                ),
              ),
              Padding(
                padding: EdgeInsets.all(12.0),
                child: Row(
                  children: <Widget>[
                    SizedBox(
                        height: 36,
                        width: 36,
                        child: Image.asset(
                          "assets/images/um.png",
                        )),
                    SizedBox(
                      width: 8.0,
                    ),
                    Expanded(
                        child: Text(
                            "Abra o app do seu banco ou seu app de pagamentos.",
                            style:
                                TextStyle(fontSize: 18.0, color: Colors.black)))
                  ],
                ),
              ),
              SizedBox(
                height: 16.0,
              ),
              Padding(
                padding: EdgeInsets.all(12.0),
                child: Row(
                  children: <Widget>[
                    SizedBox(
                        height: 36,
                        width: 36,
                        child: Image.asset(
                          "assets/images/dois.png",
                        )),
                    SizedBox(
                      width: 8.0,
                    ),
                    Expanded(
                        child: Text("Busque á opção de pagar com pix.",
                            style:
                                TextStyle(fontSize: 18.0, color: Colors.black)))
                  ],
                ),
              ),
              SizedBox(
                height: 16.0,
              ),
              Padding(
                padding: EdgeInsets.all(12.0),
                child: Row(
                  children: <Widget>[
                    SizedBox(
                        height: 36,
                        width: 36,
                        child: Image.asset(
                          "assets/images/tres.png",
                        )),
                    SizedBox(
                      width: 8.0,
                    ),
                    Expanded(
                        child: Text("Copie e cole o seguinte código.",
                            style:
                                TextStyle(fontSize: 18.0, color: Colors.black)))
                  ],
                ),
              ),
              Padding(
                padding: const EdgeInsets.all(12.0),
                child: SizedBox(
                  width: double.infinity,
                  child: Container(
                    decoration: BoxDecoration(
                      border: Border.all(
                        color: Colors.blue,
                      ),
                      borderRadius: BorderRadius.circular(10.0),
                    ),
                    child: Padding(
                        padding: EdgeInsets.all(8.0),
                        child: Text(
                          widget.dadosPix["qr_code"],
                          style: TextStyle(fontSize: 14.0),
                        )),
                  ),
                ),
              ),
              SizedBox(
                height: 16.0,
              ),
              Padding(
                padding: const EdgeInsets.all(12.0),
                child: SizedBox(
                  height: 44.0,
                  width: double.infinity,
                  child: TextButton(
                    onPressed: () {
                      final data =
                          ClipboardData(text: widget.dadosPix["qr_code"]);
                      Clipboard.setData(data);
                    },
                    child: Text(
                      "COPIAR CHAVE",
                      style: TextStyle(fontSize: 18.0),
                    ),
                  ),
                ),
              ),
              SizedBox(
                height: 12.0,
              ),
            ],
          )
        ],
      ),
    );
  }
}

解决方法

1. 字段空值处理

在TelaDadosPix的build方法中,对可能为Null的字段添加默认值或空值判断,确保传递给Text组件的是String类型:

// 状态字段添加默认值
Text(widget.dadosPix["status"] ?? "待处理",
    style: TextStyle(fontSize: 18.0, color: Colors.green)),
// 金额字段先判断Null再转字符串
Text((widget.dadosPix["total_a_pagar"] ?? 0).toString(),
    style: TextStyle(fontSize: 25.0, color: Colors.green)),
// 二维码字段添加空字符串默认值
Text(widget.dadosPix["qr_code"] ?? "",
    style: TextStyle(fontSize: 14.0)),
// 复制功能同样处理空值
final data = ClipboardData(text: widget.dadosPix["qr_code"] ?? "");

2. 验证API返回字段

检查Mercado Pago API返回的resultado结构,确认字段名是否正确:

  • 在criaChavePix方法中打印返回数据,确认实际字段路径:
if (response.statusCode == 201) {
  Map<String, dynamic> resultado = json.decode(response.body);
  print(resultado); // 打印API返回数据,核对字段名
  Navigator.of(context).push(
      MaterialPageRoute(builder: ((context) => TelaDadosPix(resultado))));
}

Mercado Pago Pix支付的二维码通常嵌套在point_of_interaction.transaction_data.qr_code路径下,而非直接的qr_code字段;金额字段是transaction_amount而非total_a_pagar。

3. 类型安全优化

使用模型类解析API返回数据,避免直接操作Map导致的字段名错误和空值问题:

class PixPaymentResponse {
  final String status;
  final double totalAmount;
  final String qrCode;

  PixPaymentResponse({
    required this.status,
    required this.totalAmount,
    required this.qrCode,
  });

  factory PixPaymentResponse.fromJson(Map<String, dynamic> json) {
    return PixPaymentResponse(
      status: json["status"] ?? "pending",
      totalAmount: json["transaction_amount"]?.toDouble() ?? 0.0,
      qrCode: json["point_of_interaction"]?["transaction_data"]?["qr_code"] ?? "",
    );
  }
}

修改criaChavePix和TelaDadosPix的参数类型:

// CriaChavePix中解析数据
PixPaymentResponse paymentResponse = PixPaymentResponse.fromJson(resultado);
Navigator.of(context).push(
    MaterialPageRoute(builder: ((context) => TelaDadosPix(paymentResponse))));

// TelaDadosPix改为接收模型类
class TelaDadosPix extends StatefulWidget {
  final PixPaymentResponse paymentData;

  TelaDadosPix(this.paymentData);

  _TelaDadosPix createState() => _TelaDadosPix();
}

后续直接通过widget.paymentData.status等方式访问字段,彻底避免空值和字段名错误问题。


内容的提问来源于stack exchange,提问作者Pedro Rodrigues

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 11:32:32