Skip to content

Invoking a method on a Custom Interface instance stored in a library's App State does not unbox the Enum return type #7301

Description

@vdevc

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

As per the title of the issue invoking a method on a Custom Interface instance stored in a library's App State does not unbox the Enum return type — result is treated as an opaque Custom Interface value instead of the native Enum.

A dependency library project exposes a Custom Code File defining a plain Dart class (e.g. LocationService), whose methods have properly typed async signatures returning a FlutterFlow-native Enum defined in the same library project, e.g.:

Future<LocationPermissionState> getPermissionState() async {
  ...
}

LocationPermissionState is a real FlutterFlow Enum (created through the Enum editor / Data Types, not just a raw Dart enum), with values granted, denied, permanentlyDenied, serviceDisabled.

An instance of LocationService is held in the library's App State, declared with type Custom Class (Custom Interface), e.g.:

identifier:
  name: locationService
dataType:
  scalarType: CustomClass
  subType:
    customInterfaceIdentifier:
      name: LocationService
      uri: <key>

When invoking getPermissionState() on this App State instance from the visual Action Editor ("Invoke Method" action on a Custom Interface object), the generated action definition looks like this:

outputVariableName: locationPermissionResponse
key: h155kw9p
customInterfaceMethodCall:
  interfaceInstance:
    source: LOCAL_STATE
    baseVariable:
      localState:
        fieldIdentifier:
          name: locationService
        stateVariableType: APP_STATE
  methodIdentifier:
    name: getPermissionState
  parameterValues: {}

Note that, unlike a Custom Action definition, this customInterfaceMethodCall block carries no returnParameter/dataType at all. The action's output (locationPermissionResponse) is not typed as the native LocationPermissionState Enum anywhere in the project schema.

The practical consequence shows up as soon as you try to branch on the result. Comparing the output against the enum's own values does not use FlutterFlow's native ENUMS variable source (the one used for ordinary Enum comparisons elsewhere in the project). Instead, the condition editor generates a comparison against the enum treated as if it were itself another Custom Interface, exposing its values as static getters:

condition:
  variable:
    source: FUNCTION_CALL
    functionCall:
      values:
        - variable:
            source: LOCAL_STATE
            baseVariable:
              localState:
                fieldIdentifier:
                  name: locationPermissionState
                stateVariableType: WIDGET_CLASS_STATE
        - variable:
            source: CUSTOM_INTERFACE_STATIC
            baseVariable:
              customInterfaceStatic:
                customInterfaceIdentifier:
                  name: LocationPermissionState
                getterIdentifier:
                  name: granted
                parameterValues: {}
      condition:
        relation: EQUAL_TO

So a value that is, at the Dart level, a proper native Enum (LocationPermissionState.granted) ends up being represented and compared in the project schema as a CUSTOM_INTERFACE_STATIC getter lookup on a pseudo-interface named after the enum, not as an ENUMS variable. In the Action/Condition Editor UI this surfaces as the result of the method call being offered as a generic "Custom Class" type rather than being recognized and selectable as the actual LocationPermissionState Enum (no native enum-value dropdown, no direct ENUMS comparison option, no direct binding to widgets/App State fields typed as that Enum).

Expected Behavior

When a method exposed through a Custom Interface (a class living in a Custom Code File) is declared with a return type that is a real FlutterFlow Enum (or Data Type) — not dynamic, not a raw untyped value — invoking that method from the Action Editor should unbox the result as that native Enum type:

  • The outputVariableName of the customInterfaceMethodCall action should carry a returnParameter/dataType of scalarType: Enum with the correct enumIdentifier, exactly as a Custom Action's returnParameter does.
  • Subsequent conditions/comparisons against that output should be able to use the native ENUMS variable source and the standard enum-value picker, instead of being forced into a synthetic CUSTOM_INTERFACE_STATIC getter-based comparison.
  • The output should be directly assignable to any Page/Component/App State field typed as that same Enum, the same way a Custom Action's Enum return value is.
    In short: FlutterFlow already knows how to fully type an Enum return value coming from a Custom Action (see returnParameter.dataType.scalarType: Enum + subType.enumIdentifier). A method call on a Custom Interface instance whose underlying Dart signature returns that same Enum type should benefit from the same typing, instead of degrading to an opaque Custom Class result.

Steps to Reproduce

  1. In a Library project, create an Enum, e.g. MyEnum with a couple of values (valueA, valueB).
  2. In the same project, add a Custom Code File (Custom Code → Files → Add) defining a plain Dart class with an async method returning that Enum, e.g.:
   class MyService {
     static final MyService _instance = MyService._internal();
     factory MyService() => _instance;
     MyService._internal();
 
     Future<MyEnum> getState() async {
       return MyEnum.valueA;
     }
   }
  1. Add an App State field of type Custom Class, pointing to MyService as its Custom Interface.
  2. On a page/component action chain (ON_TAP or similar), add an "Invoke Method" action targeting that App State instance, calling getState(), and store the output in a variable.
  3. Try to use that output:
    • a) Attempt to set another Enum-typed App State/Page State field directly from this output.
    • b) Attempt to build a Condition comparing this output against MyEnum.valueA using the standard Enum comparison / ENUMS picker.
      Observed: neither (a) nor (b) is offered as a direct/native Enum interaction. The output is only usable as a generic Custom Class value; comparisons against enum values must go through a CUSTOM_INTERFACE_STATIC static-getter comparison (selecting the Enum itself as if it were a Custom Interface class), not the native Enum comparison path.

Expected: the output of the method call should be recognized and offered as a native MyEnum value throughout the Action/Condition Editor, since the Dart method signature already declares that return type unambiguously (Future<MyEnum>).

Reproducible from Blank

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

Bug Report Code (Required)

ITFfksrl25NOseEA0a6JccdojSosJVscTYMvjuphcx4cC73mP4ktd+D4Q0NKTu7heEFUAWGj+DkdpejFj+LDNfo7H06YQaY70JVAaBbNQme5MpCQPZOaWX9QL/pWGEyX07eg2yJ+BMx2ZVYH7U+lNq3qNleeY8aSfxBlZ7vfcPo=

Visual documentation

Additional context

  • Workaround currently in use: wrap the method call in a proper Custom Action (Future<MyEnum> getState() async { return await MyService().getState(); }) with an explicit returnParameter.dataType.scalarType: Enum pointing at MyEnum. This does correctly unbox the Enum and enables native Enum comparisons/assignments downstream.
  • That workaround, however, surfaced a second, related bug: when the Custom Action needs to live in the consumer project (rather than in the library that owns the enum) and its returnParameter references the library's Enum cross-project (subType.enumIdentifier.projectId: <library-project-id>), saving the Custom Action fails with "Unable to process return parameter" (filed separately). The only working configuration found so far is defining the wrapper Custom Action inside the library project itself, where the Enum reference is local (no projectId qualifier needed).
  • This suggests the underlying gap is broader than just the Custom Action save error: FlutterFlow's Custom Interface method-call mechanism does not carry return-type metadata the way Custom Actions/Functions do, so any Enum (or Data Type) returned by a Custom Interface method is invisible to the rest of the visual builder's type system unless manually re-wrapped in a properly-typed Custom Action.

Environment

- FlutterFlow version: v7.0.32
- Platform: MacOS app
- Browser name and version:
- Operating system and version affected: Sequoia 15.7.3

Additional Information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: needs triageA potential issue that has not been confirmed as a bug.

    Type

    No type

    Fields

    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