(Solution) Multiple custom widgets on the same page, and each one manages its own position in the AppState list.

Custom Code

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:

  1. Unique ID: Each widget has a mandatory uniqueId that identifies it

  2. Storage: Values are saved in the format "uniqueId:value" in the list

  3. Isolated 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 🙂

What have you tried so far?

Solution

Did you check FlutterFlow's Documentation for this topic?
No
2