Skip to content

API response snake_case fields do not map into custom Data Type fields #7141

@Tsikaapp

Description

@Tsikaapp

Can we access your project?

  • I give permission for members of the FlutterFlow team to access and test my project for the sole purpose of investigating this issue.

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

  • The steps to reproduce above start from a blank project.

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

Image

Environment

- FlutterFlow version: 
- Platform:
- Browser name and version:
- Operating system and version affected:

Additional Information

No response

Metadata

Metadata

Assignees

Labels

status: needs informationMore information/context is needed for assessment.

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions