I found a solution to use multiple custom widgets on the same page, and each one manages its own position in the AppState list.
Now the widget works with an intelligent management system! Here are the main features:
How it works:
Unique ID: Each widget has a mandatory
uniqueId
that identifies itStorage: Values are saved in the format
"uniqueId:value"
in the listIsolated Updates: When you change a widget, only its value is updated in the list
How to use:
dart
// First question
CircleOptionSelector(
uniqueId: 'satisfaction_question',
colors: [Colors.red, Colors.orange, Colors.grey, Colors.lightGreen, Colors.green],
values: ['1', '2', '3', '4', '5'],
defaultValue: '3', // optional
)
// Second question
CircleOptionSelector(
uniqueId: 'pain_question',
colors: [Colors.red, Colors.orange, Colors.grey, Colors.lightGreen, Colors.green],
values: ['1', '2', '3', '4', '5'],
)
// Third question
CircleOptionSelector(
uniqueId: 'mood_question',
colors: [Colors.red, Colors.orange, Colors.grey, Colors.lightGreen, Colors.green],
values: ['1', '2', '3', '4', '5'],
)
In FlutterFlow's AppState: You need to create a field called selectedValuesList
of type List<String>
.
Example of saved data:
[
"satisfaction_question:4",
"pain_question:2",
"mood_question:5"
]
To retrieve specific values: If you need to retrieve the value of a specific widget elsewhere in the app, you can create a Custom Function:
dart
String? getValueForWidget(List<String> valuesList, String widgetId) {
for (String item in valuesList) {
if (item.startsWith('$widgetId:')) {
return item.split(':')[1];
}
}
return null;
}
This way each widget maintains its own independent state and you can have as many as you want on the same page!
Code widget custom
// Automatic FlutterFlow imports
import '/backend/schema/structs/index.dart';
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import '/custom_code/widgets/index.dart'; // Imports other custom widgets
import '/custom_code/actions/index.dart'; // Imports custom actions
import '/flutter_flow/custom_functions.dart'; // Imports custom functions
import 'package:flutter/material.dart';
// Begin custom widget code
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!
class CircleOptionSelector extends StatefulWidget {
const CircleOptionSelector({
super.key,
this.width,
this.height,
this.colors,
this.values,
required this.uniqueId, // ID único para identificar este widget
this.defaultValue, // Valor padrão inicial
});
final double? width;
final double? height;
final List<Color>? colors;
final List<String>? values;
final String uniqueId; // Identificador único obrigatório
final String? defaultValue;
@override
State<CircleOptionSelector> createState() => _CircleOptionSelectorState();
}
class _CircleOptionSelectorState extends State<CircleOptionSelector> {
String? selected;
@override
void initState() {
super.initState();
// Inicializar a lista no AppState se não existir
if (FFAppState().selectedValuesList == null) {
FFAppState().update(() {
FFAppState().selectedValuesList = [];
});
}
// Buscar valor existente para este widget específico
final existingValue = _getValueForWidget();
if (existingValue != null) {
selected = existingValue;
} else if (widget.defaultValue != null) {
selected = widget.defaultValue;
_updateValueInList(widget.defaultValue!);
}
}
// Busca o valor atual deste widget na lista
String? _getValueForWidget() {
final valuesList = FFAppState().selectedValuesList ?? [];
// Procura por um item com o ID deste widget
for (String item in valuesList) {
if (item.startsWith('${widget.uniqueId}:')) {
return item.split(':')[1];
}
}
return null;
}
// Atualiza ou adiciona o valor deste widget na lista
void _updateValueInList(String value) {
FFAppState().update(() {
List<String> currentList = List<String>.from(FFAppState().selectedValuesList ?? []);
// Remove valor anterior deste widget se existir
currentList.removeWhere((item) => item.startsWith('${widget.uniqueId}:'));
// Adiciona novo valor
currentList.add('${widget.uniqueId}:$value');
FFAppState().selectedValuesList = currentList;
});
}
@override
Widget build(BuildContext context) {
final values = widget.values ?? ['1', '2', '3', '4', '5'];
final colors = widget.colors ??
[
Color(0xFFD32F2F), // vermelho
Color(0xFFD32F2F), // vermelho
Color(0xFF64748B),
Color(0xFF58cd42), // verde claro
Color(0xFF58cd42), // verde claro
];
final sizes = [52.0, 44.0, 36.0, 44.0, 52.0];
return Container(
width: widget.width ?? double.infinity,
height: widget.height ?? 80,
child: Column(
children: [
// Opcional: mostrar o ID do widget para debug
// Text('ID: ${widget.uniqueId}', style: TextStyle(fontSize: 10, color: Colors.grey)),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: values.asMap().entries.map((entry) {
final i = entry.key;
final value = entry.value;
final color = colors[i % colors.length];
final size = sizes[i % sizes.length];
final isSelected = selected == value;
return GestureDetector(
onTap: () {
setState(() {
selected = value;
});
// Atualizar apenas o valor deste widget específico
_updateValueInList(value);
},
child: Container(
width: size,
height: size,
margin: const EdgeInsets.symmetric(horizontal: 4),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isSelected ? color : Colors.transparent,
border: Border.all(color: color, width: 2),
),
child: isSelected
? const Icon(Icons.check, color: Colors.white, size: 20)
: null,
),
);
}).toList(),
),
],
),
);
}
}
// Widget auxiliar para visualizar todos os valores salvos (útil para debug)
class ShowSavedValues extends StatelessWidget {
const ShowSavedValues({super.key});
@override
Widget build(BuildContext context) {
final values = FFAppState().selectedValuesList ?? [];
return Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(8),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Valores Salvos:', style: TextStyle(fontWeight: FontWeight.bold)),
...values.map((value) => Text(value)),
],
),
);
}
}
I hope it helps you 🙂