· Business Owner

App state variable is blank after being set from custom widget.

Custom Code

This custom widget allows the user to add new elements to a drop down list and saves the element to pocketbase DB. This all works well. When opening an existing record, the drop down defaults to the correct value. Since we cannot access the current state of a custom widget, I am trying to update an app state variable within the widget code when the selection changes, however, despite the debug telling me the app state has been updated, it appears blank in Flutterflow so my DB update is not working correctly. Here is the entire code:

import 'package:pocketbase/pocketbase.dart';
import '../actions/create_record.dart'; // Ensure this path is correct

class FFAppState {
  static final FFAppState _instance = FFAppState._internal();

  factory FFAppState() {
    return _instance;
  }

  FFAppState._internal();

  Map<String, dynamic> _fields = {};

  void setField(String fieldName, dynamic value) {
    _fields[fieldName] = value;
  }
  dynamic getField(String fieldName) {
    return _fields[fieldName];
  }
}

// Use this to access the instance
final appState = FFAppState();

class DynamicDropdown extends StatefulWidget {
  const DynamicDropdown({
    super.key,
    this.width,
    this.height,
    required this.authenticatedUserId,
    required this.tableName,
    required this.displayFieldName,
    required this.ownerIdFieldName,
    this.initialValue, // Optional initial value for the dropdown
    required this.appStateField,
  });

  final double? width;
  final double? height;
  final String authenticatedUserId;
  final String tableName;
  final String displayFieldName;
  final String ownerIdFieldName;
  final String? initialValue; // Store the initial value if provided
  final String appStateField;

  @override
  State<DynamicDropdown> createState() => _DynamicDropdownState();
}

class _DynamicDropdownState extends State<DynamicDropdown> {
  static final pb = PocketBase('https://xxxxx.pockethost.io');
  List<dynamic> items = [];
  String? selectedValue;
  bool isLoading = true;

  @override
  void initState() {
    super.initState();
    selectedValue = widget.initialValue; // Set the initial value if provided
    _loadData();
  }

  Future<void> _loadData() async {
    setState(() {
      isLoading = true;
    });

    try {
      final records = await pb.collection(widget.tableName).getList(
            filter:
                '${widget.ownerIdFieldName}="${widget.authenticatedUserId}"',
          );

      List<dynamic> updatedItems = records.items.map((record) {
        return {
          'id': record.id,
          'data': record.data,
        };
      }).toList();

      // Add the 'Add New...' option
      updatedItems.add({
        'id': 'add_new',
        'data': {widget.displayFieldName: 'Add New...'}
      });

      setState(() {
        items = updatedItems;
        isLoading = false;
      });
    } catch (e) {
      print('Failed to load data: $e');
      setState(() => isLoading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return isLoading
        ? const CircularProgressIndicator()
        : DropdownButtonFormField<String>(
            value: selectedValue,
            decoration: InputDecoration(
              border: OutlineInputBorder(),
              contentPadding:
                  EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
            ),
            hint: Text('Select an option'),
            items: items.map<DropdownMenuItem<String>>((dynamic item) {
              String displayText =
                  item['data'][widget.displayFieldName]?.toString() ?? '';
              String recordId = item['id']?.toString() ?? '';

              return DropdownMenuItem<String>(
                value: recordId,
                child: Text(displayText),
              );
            }).toList(),
            onChanged: (String? newValue) {
              if (newValue == 'add_new') {
                _addNewIngredientCategory();
              } else {
                setState(() {
                  selectedValue = newValue;
                  appState.setField(widget.appStateField,
                      newValue); // Update the app state here
                });
                print(
                    "AppState updated to: ${appState.getField(widget.appStateField)}");
                // Update app state using the method
              }
            },
          );
  }

  Future<void> _addNewIngredientCategory() async {
    final TextEditingController controller = TextEditingController();
    await showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text('Add New Category'),
          content: TextField(
            controller: controller,
            decoration: InputDecoration(hintText: "Enter new category name"),
          ),
          actions: <Widget>[
            TextButton(
              child: Text('Cancel'),
              onPressed: () {
                Navigator.of(context).pop();
              },
            ),
            TextButton(
              child: Text('Save'),
              onPressed: () async {
                if (controller.text.isNotEmpty) {
                  var newRecord = {
                    widget.displayFieldName: controller.text,
                    widget.ownerIdFieldName: widget.authenticatedUserId,
                  };
                  print(newRecord); // Debug print
                  bool success =
                      await createRecord(newRecord, widget.tableName);
                  if (success) {
                    Navigator.of(context).pop();
                    _loadData(); // Reload data to include the new category
                  } else {
                    print('Error saving new item');
                  }
                }
              },
            ),
          ],
        );
      },
    );
  }
}

With focus on these sections:

class FFAppState {
  static final FFAppState _instance = FFAppState._internal();

  factory FFAppState() {
    return _instance;
  }

  FFAppState._internal();

  Map<String, dynamic> _fields = {};

  void setField(String fieldName, dynamic value) {
    _fields[fieldName] = value;
  }
  dynamic getField(String fieldName) {
    return _fields[fieldName];
  }
}
onChanged: (String? newValue) {
              if (newValue == 'add_new') {
                _addNewIngredientCategory();
              } else {
                setState(() {
                  selectedValue = newValue;
                  appState.setField(widget.appStateField,
                      newValue); // Update the app state here
                });
                print(
                    "AppState updated to: ${appState.getField(widget.appStateField)}");
                // Update app state using the method
              }

The javascript console displays as expected but when using alert dialogue in FF, the appState is empty. Can anyone try and help?

What have you tried so far?

I have used debug, everything appears to work.

Did you check FlutterFlow's Documentation for this topic?
Yes
1
1 reply