Can we access your project?
Current Behavior
The API call succeeds and returns the materials array, but when I assign $.materials to my App State list of MaterialInfomationStruct, the description field is populated while materialCode remains empty/null.
This appears to happen because FlutterFlow’s generated parser only reads data['materialCode'], but the API response key is material_code. There does not appear to be a UI option to map or alias material_code to the materialCode field when parsing the API response into a custom Data Type.
Expected Behavior
FlutterFlow should allow me to map API response keys to custom Data Type fields when the names differ.
In this case, when assigning $.materials to my App State list of MaterialInfomationStruct, FlutterFlow should support mapping the API field material_code into the Data Type field materialCode, so both materialCode and description are populated correctly from the API response.
Steps to Reproduce
Create a custom Data Type called MaterialInfomationStruct with fields:
materialCode
description
Create an App State variable:
Name: materialInfomation
Type: List
Create or connect an API call that returns this response shape:
{
"count": 6559,
"materials": [
{
"material_code": "16L040",
"description": "SUP044"
}
]
}
On page load, call the API.
Set App State materialInfomation from the API response path:
$.materials
Run the app and inspect the App State / UI values.
Notice that description is populated, but materialCode is empty because the API key is material_code while the Data Type field is materialCode.
Reproducible from Blank
Bug Report Code (Required)
import '/auth/firebase_auth/auth_util.dart'; import '/backend/api_requests/api_calls.dart'; import '/backend/backend.dart'; import '/backend/schema/enums/enums.dart'; import '/backend/schema/structs/index.dart'; import '/components/update_app/update_app_widget.dart'; import '/flutter_flow/flutter_flow_theme.dart'; import '/flutter_flow/flutter_flow_util.dart'; import '/flutter_flow/flutter_flow_widgets.dart'; import 'dart:ui'; import '/custom_code/actions/index.dart' as actions; import '/flutter_flow/custom_functions.dart' as functions; import '/index.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; import 'splash_screen_model.dart'; export 'splash_screen_model.dart'; class SplashScreenWidget extends StatefulWidget { const SplashScreenWidget({super.key}); static String routeName = 'SplashScreen'; static String routePath = '/splashScreen'; @OverRide State createState() => _SplashScreenWidgetState(); } class _SplashScreenWidgetState extends State { late SplashScreenModel _model; final scaffoldKey = GlobalKey(); @OverRide void initState() { super.initState(); model = createModel(context, () => SplashScreenModel()); // On page load action. SchedulerBinding.instance.addPostFrameCallback(() async { _model.builderversion = await actions.getAppBuildVersion(); FFAppState().version = _model.builderversion!; await currentUserReference!.update(createUsersRecordData( versionNumber: _model.builderversion, )); _model.user = await UsersRecord.getDocumentOnce(currentUserReference!); if (_model.user?.status == Status.Active.name) { if (isWeb) { if ((valueOrDefault(currentUserDocument?.role, '') == Role.Admin.name) || (valueOrDefault(currentUserDocument?.role, '') == functions .separateWordsonCaps(Role.SalesRepresentative.name))) { context.pushNamed(MaterialCostingDashboardWidget.routeName); FFAppState().page = Pages.MaterialCosting; safeSetState(() {}); } else if (valueOrDefault(currentUserDocument?.role, '') == Role.Driver.name) { context.pushNamed(HomePageWidget.routeName); FFAppState().page = Pages.Invoices; safeSetState(() {}); } else { context.pushNamed(JobcardsWidget.routeName); FFAppState().page = Pages.Jobcards; safeSetState(() {}); } } else { if ((valueOrDefault(currentUserDocument?.role, '') == Role.Driver.name) || (valueOrDefault(currentUserDocument?.role, '') == Role.Admin.name) || (valueOrDefault(currentUserDocument?.role, '') == functions .separateWordsonCaps(Role.SalesRepresentative.name))) { context.pushNamed(HomePageWidget.routeName); FFAppState().page = Pages.Invoices; safeSetState(() {}); } else { context.pushNamed(JobcardsWidget.routeName); FFAppState().page = Pages.Jobcards; safeSetState(() {}); } } } else { context.pushNamed(FailedPageWidget.routeName); } _model.appConfig = await queryAppConfigRecordOnce( singleRecord: true, ).then((s) => s.firstOrNull); if (((FFAppState().version != _model.appConfig?.androidVersion) && isAndroid) || ((FFAppState().version != _model.appConfig?.iosVersion) && isiOS)) { await showDialog( context: context, builder: (dialogContext) { return Dialog( elevation: 0, insetPadding: EdgeInsets.zero, backgroundColor: Colors.transparent, alignment: AlignmentDirectional(0, 0) .resolve(Directionality.of(context)), child: GestureDetector( onTap: () { FocusScope.of(dialogContext).unfocus(); FocusManager.instance.primaryFocus?.unfocus(); }, child: UpdateAppWidget(), ), ); }, ); } await Future.wait([ Future(() async { _model.customerList = await CustomerListCall.call(); if ((_model.customerList?.succeeded ?? true)) { FFAppState().customerDetails = ((_model.customerList?.jsonBody ?? '') .toList() .map<CustomerInfomationStruct?>( CustomerInfomationStruct.maybeFromMap) .toList() as Iterable<CustomerInfomationStruct?>) .withoutNulls .toList() .cast(); safeSetState(() {}); } }), Future(() async { _model.usersUpdatingMaterial = await ListOfUsersUpdatingMaterialsCall.call(); if ((_model.usersUpdatingMaterial?.succeeded ?? true)) { FFAppState().MaterialUpdatedBy = (getJsonField( (_model.usersUpdatingMaterial?.jsonBody ?? ''), r'''$.users''', true, ) as List?)! .map((e) => e.toString()) .toList() .cast() .toList() .cast(); safeSetState(() {}); } }), Future(() async { _model.materialClass = await MaterialListClassesCall.call(); if ((_model.materialClass?.succeeded ?? true)) { FFAppState().MaterialClass = (getJsonField( (_model.materialClass?.jsonBody ?? ''), r'''$.classes''', true, ) as List?)! .map((e) => e.toString()) .toList() .cast() .toList() .cast(); safeSetState(() {}); } }), Future(() async { _model.listOfMaterials = await MaterialListCall.call(); if ((_model.listOfMaterials?.succeeded ?? true)) { FFAppState().materialInfomation = ((model.listOfMaterials?.jsonBody ?? '') .toList() .map<MaterialInfomationStruct?>( MaterialInfomationStruct.maybeFromMap) .toList() as Iterable<MaterialInfomationStruct?>) .withoutNulls .toList() .cast(); safeSetState(() {}); } }), ]); }); WidgetsBinding.instance.addPostFrameCallback(() => safeSetState(() {})); } @OverRide void dispose() { _model.dispose(); super.dispose(); } @OverRide Widget build(BuildContext context) { context.watch(); return Builder( builder: (context) => GestureDetector( onTap: () { FocusScope.of(context).unfocus(); FocusManager.instance.primaryFocus?.unfocus(); }, child: Scaffold( key: scaffoldKey, backgroundColor: FlutterFlowTheme.of(context).primaryBackground, body: SafeArea( top: true, child: Align( alignment: AlignmentDirectional(0, 0), child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( padding: EdgeInsets.all(20), child: ClipRRect( borderRadius: BorderRadius.circular(8), child: Image.asset( 'assets/images/Logo_design_for_IRP_Automation_Flow.png', width: 500, height: 400, fit: BoxFit.contain, ), ), ), ], ), ), ), ), ), ); } }
Visual documentation
Environment
- FlutterFlow version:
- Platform:
- Browser name and version:
- Operating system and version affected:
Additional Information
No response
Can we access your project?
Current Behavior
The API call succeeds and returns the materials array, but when I assign $.materials to my App State list of MaterialInfomationStruct, the description field is populated while materialCode remains empty/null.
This appears to happen because FlutterFlow’s generated parser only reads data['materialCode'], but the API response key is material_code. There does not appear to be a UI option to map or alias material_code to the materialCode field when parsing the API response into a custom Data Type.
Expected Behavior
FlutterFlow should allow me to map API response keys to custom Data Type fields when the names differ.
In this case, when assigning $.materials to my App State list of MaterialInfomationStruct, FlutterFlow should support mapping the API field material_code into the Data Type field materialCode, so both materialCode and description are populated correctly from the API response.
Steps to Reproduce
Create a custom Data Type called MaterialInfomationStruct with fields:
materialCode
description
Create an App State variable:
Name: materialInfomation
Type: List
Create or connect an API call that returns this response shape:
{
"count": 6559,
"materials": [
{
"material_code": "16L040",
"description": "SUP044"
}
]
}
On page load, call the API.
Set App State materialInfomation from the API response path:
$.materials
Run the app and inspect the App State / UI values.
Notice that description is populated, but materialCode is empty because the API key is material_code while the Data Type field is materialCode.
Reproducible from Blank
Bug Report Code (Required)
import '/auth/firebase_auth/auth_util.dart'; import '/backend/api_requests/api_calls.dart'; import '/backend/backend.dart'; import '/backend/schema/enums/enums.dart'; import '/backend/schema/structs/index.dart'; import '/components/update_app/update_app_widget.dart'; import '/flutter_flow/flutter_flow_theme.dart'; import '/flutter_flow/flutter_flow_util.dart'; import '/flutter_flow/flutter_flow_widgets.dart'; import 'dart:ui'; import '/custom_code/actions/index.dart' as actions; import '/flutter_flow/custom_functions.dart' as functions; import '/index.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; import 'splash_screen_model.dart'; export 'splash_screen_model.dart'; class SplashScreenWidget extends StatefulWidget { const SplashScreenWidget({super.key}); static String routeName = 'SplashScreen'; static String routePath = '/splashScreen'; @OverRide State createState() => _SplashScreenWidgetState(); } class _SplashScreenWidgetState extends State { late SplashScreenModel _model; final scaffoldKey = GlobalKey(); @OverRide void initState() { super.initState(); model = createModel(context, () => SplashScreenModel()); // On page load action. SchedulerBinding.instance.addPostFrameCallback(() async { _model.builderversion = await actions.getAppBuildVersion(); FFAppState().version = _model.builderversion!; await currentUserReference!.update(createUsersRecordData( versionNumber: _model.builderversion, )); _model.user = await UsersRecord.getDocumentOnce(currentUserReference!); if (_model.user?.status == Status.Active.name) { if (isWeb) { if ((valueOrDefault(currentUserDocument?.role, '') == Role.Admin.name) || (valueOrDefault(currentUserDocument?.role, '') == functions .separateWordsonCaps(Role.SalesRepresentative.name))) { context.pushNamed(MaterialCostingDashboardWidget.routeName); FFAppState().page = Pages.MaterialCosting; safeSetState(() {}); } else if (valueOrDefault(currentUserDocument?.role, '') == Role.Driver.name) { context.pushNamed(HomePageWidget.routeName); FFAppState().page = Pages.Invoices; safeSetState(() {}); } else { context.pushNamed(JobcardsWidget.routeName); FFAppState().page = Pages.Jobcards; safeSetState(() {}); } } else { if ((valueOrDefault(currentUserDocument?.role, '') == Role.Driver.name) || (valueOrDefault(currentUserDocument?.role, '') == Role.Admin.name) || (valueOrDefault(currentUserDocument?.role, '') == functions .separateWordsonCaps(Role.SalesRepresentative.name))) { context.pushNamed(HomePageWidget.routeName); FFAppState().page = Pages.Invoices; safeSetState(() {}); } else { context.pushNamed(JobcardsWidget.routeName); FFAppState().page = Pages.Jobcards; safeSetState(() {}); } } } else { context.pushNamed(FailedPageWidget.routeName); } _model.appConfig = await queryAppConfigRecordOnce( singleRecord: true, ).then((s) => s.firstOrNull); if (((FFAppState().version != _model.appConfig?.androidVersion) && isAndroid) || ((FFAppState().version != _model.appConfig?.iosVersion) && isiOS)) { await showDialog( context: context, builder: (dialogContext) { return Dialog( elevation: 0, insetPadding: EdgeInsets.zero, backgroundColor: Colors.transparent, alignment: AlignmentDirectional(0, 0) .resolve(Directionality.of(context)), child: GestureDetector( onTap: () { FocusScope.of(dialogContext).unfocus(); FocusManager.instance.primaryFocus?.unfocus(); }, child: UpdateAppWidget(), ), ); }, ); } await Future.wait([ Future(() async { _model.customerList = await CustomerListCall.call(); if ((_model.customerList?.succeeded ?? true)) { FFAppState().customerDetails = ((_model.customerList?.jsonBody ?? '') .toList() .map<CustomerInfomationStruct?>( CustomerInfomationStruct.maybeFromMap) .toList() as Iterable<CustomerInfomationStruct?>) .withoutNulls .toList() .cast(); safeSetState(() {}); } }), Future(() async { _model.usersUpdatingMaterial = await ListOfUsersUpdatingMaterialsCall.call(); if ((_model.usersUpdatingMaterial?.succeeded ?? true)) { FFAppState().MaterialUpdatedBy = (getJsonField( (_model.usersUpdatingMaterial?.jsonBody ?? ''), r'''$.users''', true, ) as List?)! .map((e) => e.toString()) .toList() .cast() .toList() .cast(); safeSetState(() {}); } }), Future(() async { _model.materialClass = await MaterialListClassesCall.call(); if ((_model.materialClass?.succeeded ?? true)) { FFAppState().MaterialClass = (getJsonField( (_model.materialClass?.jsonBody ?? ''), r'''$.classes''', true, ) as List?)! .map((e) => e.toString()) .toList() .cast() .toList() .cast(); safeSetState(() {}); } }), Future(() async { _model.listOfMaterials = await MaterialListCall.call(); if ((_model.listOfMaterials?.succeeded ?? true)) { FFAppState().materialInfomation = ((model.listOfMaterials?.jsonBody ?? '') .toList() .map<MaterialInfomationStruct?>( MaterialInfomationStruct.maybeFromMap) .toList() as Iterable<MaterialInfomationStruct?>) .withoutNulls .toList() .cast(); safeSetState(() {}); } }), ]); }); WidgetsBinding.instance.addPostFrameCallback(() => safeSetState(() {})); } @OverRide void dispose() { _model.dispose(); super.dispose(); } @OverRide Widget build(BuildContext context) { context.watch(); return Builder( builder: (context) => GestureDetector( onTap: () { FocusScope.of(context).unfocus(); FocusManager.instance.primaryFocus?.unfocus(); }, child: Scaffold( key: scaffoldKey, backgroundColor: FlutterFlowTheme.of(context).primaryBackground, body: SafeArea( top: true, child: Align( alignment: AlignmentDirectional(0, 0), child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( padding: EdgeInsets.all(20), child: ClipRRect( borderRadius: BorderRadius.circular(8), child: Image.asset( 'assets/images/Logo_design_for_IRP_Automation_Flow.png', width: 500, height: 400, fit: BoxFit.contain, ), ), ), ], ), ), ), ), ), ); } }
Visual documentation
Environment
Additional Information
No response