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

Flutter项目报错:'() => Map<String,dynamic>?'与目标Map类型不兼容

Flutter类型不匹配错误:() => Map<String, dynamic>? 无法转为 Map<String, dynamic>?

开发一款扫码识别商品并从Cloud Firestore获取商品信息的Flutter应用,首页展示时遇到上述类型不匹配错误。已尝试将类型改为Map<dynamic, dynamic>、移除空安全问号,更新所有Flutter库及pubspec,但问题未解决。

错误信息:

Error: type '() => Map<String, dynamic>?' is not a subtype of type 'Map<String, dynamic>?'

首页代码:

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/services.dart';
import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart';

Map<String, dynamic>? mapProducts = {};

//Initialize
@override
void initState() {
  if (userProfile.containsKey("Unordered Products")) {
    mapProducts = userProfile["Unordered Products"];
  }

  super.initState();
}

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

@override
Widget build(BuildContext context) {
  double size =
      MediaQuery.of(context).size.width < MediaQuery.of(context).size.height
          ? MediaQuery.of(context).size.width
          : (!kIsWeb)
              ? MediaQuery.of(context).size.height
              : MediaQuery.of(context).size.height / 2;

  //For Refreshing the theme
  if (userProfile.containsKey("Theme")) {
    myAppTheme = userProfile["Theme"] == "Light Theme"
        ? getMainThemeWithBrightness(context, Brightness.light)
        : getMainThemeWithBrightness(context, Brightness.dark);
  }

  MobileScannerController cameraController = MobileScannerController();

  return SafeArea(
    child: Scaffold(
      key: scaffoldKey,
      backgroundColor: myAppTheme?.scaffoldBackgroundColor,
      appBar: getAppBar(
        scaffoldKey: scaffoldKey,
        context: context,
        strAppBarTitle: "Spar Store",
        showBackButton: false,
      ),

      //drawer
      drawer: getDrawer(context, scaffoldKey),

      floatingActionButton: FloatingActionButton(
        backgroundColor: myAppTheme?.primaryColor,
        onPressed: () async {
          String? barcode = await scan();
          if (barcode != null && barcode != "") {
            if (mapProducts!.containsKey(barcode)) {
              //Increase count
              setState(() {
                mapProducts![barcode] = ++mapProducts![barcode];
              });
            } else {
              //Add a new product
              setState(() {
                mapProducts![barcode] = 1;
              });
            }
            userProfile["Unordered Products"] = mapProducts;

            //Update the new Unordered Products list to the Firebase CLoud Firestpre
            await authService.setData();
          }
        },
        child: Icon(Icons.add, color: myAppTheme?.iconTheme.color),
      ),

      //Body
      body: mapProducts != null && mapProducts!.length > 0
          ? Column(
              children: <Widget>[
                //Products in Cart List
                getUnorderedProducts(size),

                //Final invoice
                getInvoice(size),

                //Checkout Button
                getCheckoutButton(size),
              ],
            )
          : Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Image.asset(
                    "assets/images/empty-cart.png",
                    fit: BoxFit.fitWidth,
                  ),
                  Text(
                    "No Items Added.\nPlease Add items by scanning them",
                    style: myAppTheme?.textTheme.caption,
                    textAlign: TextAlign.center,
                  ),
                ],
              ),
            ),
    ),
  );
}

//Get the list of all the Orders still in Cart
getUnorderedProducts(double size) {
  return SizedBox(
    width: MediaQuery.of(context).size.width,
    height: MediaQuery.of(context).size.height * 0.7,
    child: ListView.builder(
        itemCount: mapProducts!.length,
        itemBuilder: (context, index) {
          return FutureBuilder(
            builder: (BuildContext context, AsyncSnapshot snapshot) {
              if (snapshot.data == null || snapshot.hasData == false) {
                return Center(
                  child: CircularProgressIndicator(
                    valueColor: AlwaysStoppedAnimation<Color>(
                        myAppTheme!.primaryColor),
                    strokeWidth: 5,
                  ),
                );
              } else {
                Map<String, dynamic>? mapProduct = snapshot.data;

                return GestureDetector(
                  child: Card(
                    elevation: myAppTheme?.cardTheme.elevation,
                    color: myAppTheme?.cardTheme.color,
                    shape: myAppTheme?.cardTheme.shape,
                    child: Row(
                      children: <Widget>[
                        mapProduct!.containsKey("photo url")
                            ? ClipRRect(
                                borderRadius: BorderRadius.circular(15),
                                child: SizedBox(
                                    width: 120,
                                    child: Image.network(
                                        mapProduct["photo url"][0])))
                            : Icon(
                                Icons.category,
                                color: myAppTheme?.iconTheme.color,
                              ),
                        SizedBox(
                          width: size - 150,
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.end,
                            children: <Widget>[
                              //Title
                              Padding(
                                padding: const EdgeInsets.all(8.0),
                                child: Text(
                                  mapProduct["name"],
                                  style: myAppTheme?.textTheme.caption,
                                  overflow: TextOverflow.ellipsis,
                                  textAlign: TextAlign.right,
                                ),
                              ),

                              //Model
                              Padding(
                                padding: const EdgeInsets.all(8.0),
                                child: Text(mapProduct["model"],
                                    style: myAppTheme?.textTheme.bodyText1,
                                    overflow: TextOverflow.ellipsis,
                                    textAlign: TextAlign.right),
                              ),

                              //Discount
                              Padding(
                                padding: const EdgeInsets.all(8.0),
                                child: Text(
                                    "Discount: ${mapProduct["discount"]}%",
                                    style: myAppTheme?.textTheme.bodyText1
                                        ?.copyWith(
                                            fontWeight: FontWeight.bold),
                                    overflow: TextOverflow.ellipsis,
                                    textAlign: TextAlign.right),
                              ),

                              //Price
                              Padding(
                                padding: const EdgeInsets.all(8.0),
                                child: Text("Price: ${mapProduct["price"]}",
                                    style: myAppTheme?.textTheme.bodyText1
                                        ?.copyWith(
                                            fontWeight: FontWeight.bold),
                                    overflow: TextOverflow.ellipsis,
                                    textAlign: TextAlign.right),
                              ),

                              //Quantity
                              Container(
                                padding: const EdgeInsets.all(8.0),
                                height: 60,
                                child: StepperTouch(
                                  initialValue:
                                      mapProducts?.values.elementAt(index),
                                  direction: Axis.horizontal,
                                  withSpring: true,
                                  primaryColor:
                                      myAppTheme?.colorScheme.secondary,
                                  textColor:
                                      myAppTheme?.textTheme.bodyText1?.color,
                                  onChanged: (int value) {
                                    if (value == 0) {
                                      setState(() {
                                        mapProducts?.remove(mapProducts?.keys
                                            .elementAt(index));
                                      });
                                    } else if (value > 0) {
                                      setState(() {
                                        mapProducts![mapProducts!.keys
                                            .elementAt(index)] = value;
                                      });
                                    }

                                    userProfile["Unordered Products"] =
                                        mapProducts;
                                  },
                                ),
                              ),
                            ],
                          ),
                        ),
                      ],
                    ),
                  ),

                  //Open
                  onTap: () {
                    Navigator.push(
                      context,
                      MaterialPageRoute(
                          builder: (context) =>
                              ViewProductScreen(mapProduct)),
                    );
                  },
                );
              }
            },
            future: getProduct(mapProducts!.keys.elementAt(index)),
          );
        }),
  );
}

//Get the Total Price and the Total discount
getInvoice(double size) {
  return FutureBuilder(
    builder: (BuildContext context, AsyncSnapshot snapshot) {
      if (snapshot == null ||
          snapshot.data == null ||
          snapshot.hasData == false) {
        return Center(
          child: CircularProgressIndicator(
            valueColor:
                AlwaysStoppedAnimation<Color>(myAppTheme!.primaryColor),
            strokeWidth: 5,
          ),
        );
      } else {
        Map mapProduct = snapshot.data;

        double dbTotalDiscount = mapProduct["discount"],
            dbTotalPrice = mapProduct["price"];

        return Column(
          crossAxisAlignment: CrossAxisAlignment.end,
          children: <Widget>[
            
            //Total Price
            Padding(
              padding: const EdgeInsets.fromLTRB(0, 5, 10, 5),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.end,
                children: <Widget>[
                  Text(
                    "Total Price:   ",
                    style: myAppTheme?.textTheme.bodyText1,
                    overflow: TextOverflow.ellipsis,
                  ),
                  Text(
                    dbTotalPrice.toString(),
                    style: myAppTheme?.textTheme.bodyText1,
                    overflow: TextOverflow.ellipsis,
                  ),
                ],
              ),
            ),
          ],
        );
      }
    },
    future: getInvoiceDetails(),
  );
}

getInvoiceDetails() async {
  double dbTotalDiscount = 0.0, dbTotalPrice = 0.0;

  //Loop through all the products to get the Discount and the Price
  await Future.forEach(mapProducts!.keys, (key) async {
    String strProductID = key;
    Map<String, dynamic>? mapProduct =
        (await getProduct(strProductID)) as Map<String, dynamic>?;

    if (mapProduct!.containsKey("discount")) {
      dbTotalDiscount += double.tryParse(
          (mapProduct["discount"] * mapProducts![key]).toString())!;
    }
    if (mapProduct.containsKey("price")) {
      dbTotalPrice += double.tryParse(
          (mapProduct["price"] * mapProducts![key]).toString())!;
    }
//      print("discount: " + dbTotalDiscount.toString());
//      print("price: " + dbTotalPrice.toString());
  });

  return {"discount": dbTotalDiscount, "price": dbTotalPrice};
}

//Get Checkout button
getCheckoutButton(double size) {
  return ButtonTheme(
    minWidth: double.infinity,
    child: Padding(
      padding: const EdgeInsets.fromLTRB(80, 20, 80, 10),
      child: primaryRaisedButton(
        context: context,
        text: "Checkout now",
        color: myAppTheme?.primaryColor,
        onPressed: () async {
          //Go to Checkout
          await Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => CheckoutScreen()),
          );

          setState(() {
            //Refresh the UI after returning back from the Checkout screen
            mapProducts = userProfile["Unordered Products"];
          });
        },
      ),
    ),
  );
}

//=================================================================================
//Get details of product
Future<Map<String, dynamic>? Function()> getProduct(String barcode) async {
  return (await FirebaseFirestore.instance
          .collection("Products")
          .doc(barcode)
          .get())
      .data;
}

//Scan QR code
Future<String?> scan() async {
  try {
    String barcode = (await FlutterBarcodeScanner.scanBarcode(
        '#ff6666', 'Cancel', true, ScanMode.QR));
    return barcode;
  } on PlatformException catch (e) {
    if (e.code == BarcodeScanner.cameraAccessDenied) {
      showSnackBar(
          scaffoldKey: scaffoldKey,
          text: "The user did not grant the camera permission!",
          buttonText: '',
          onPressed: () {});
    } else {
      showSnackBar(
          scaffoldKey: scaffoldKey,
          text: "Unknown error: $e",
          buttonText: '',
          onPressed: () {});
    }

    return null;
  } on FormatException {
    showSnackBar(
        scaffoldKey: scaffoldKey,
        text:
            'null (User returned using the "back"-button before scanning anything. Result)',
        buttonText: '',
        onPressed: () {});
  } catch (e) {
    showSnackBar(
        scaffoldKey: scaffoldKey,
        text: "Unknown error: $e",
        buttonText: '',
        onPressed: () {});
  }
  return null;
}

问题根源与修复

错误的核心在于getProduct函数:

  1. 返回类型错误:原函数返回Future<Map<String, dynamic>? Function()>,表示返回一个函数,而非实际的Map数据。
  2. 未调用data方法:Firestore的DocumentSnapshot.data是一个需要带括号调用的方法,原代码直接返回.data得到的是方法引用,而非执行后的Map结果。

修改后的getProduct函数:

//Get details of product
Future<Map<String, dynamic>?> getProduct(String barcode) async {
  return (await FirebaseFirestore.instance
          .collection("Products")
          .doc(barcode)
          .get())
      .data(); // 调用data()方法获取实际数据
}

补充优化建议

  1. 简化类型转换:修改getProduct后,getInvoiceDetails中无需强制类型转换,直接接收返回值即可:
    Map<String, dynamic>? mapProduct = await getProduct(strProductID);
    
  2. 安全空值处理:使用??运算符避免空指针异常:
    dbTotalDiscount += double.tryParse(
        (mapProduct?["discount"] * mapProducts![key]).toString()) ?? 0;
    

内容的提问来源于stack exchange,提问作者Alexander Mwachande

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 11:16:11