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?