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函数:
- 返回类型错误:原函数返回
Future<Map<String, dynamic>? Function()>,表示返回一个函数,而非实际的Map数据。 - 未调用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()方法获取实际数据 }
补充优化建议
- 简化类型转换:修改
getProduct后,getInvoiceDetails中无需强制类型转换,直接接收返回值即可:Map<String, dynamic>? mapProduct = await getProduct(strProductID); - 安全空值处理:使用
??运算符避免空指针异常:dbTotalDiscount += double.tryParse( (mapProduct?["discount"] * mapProducts![key]).toString()) ?? 0;
内容的提问来源于stack exchange,提问作者Alexander Mwachande
相关产品推荐
相关产品推荐

