Creare Form dinamici e scalabili in Flutter: una soluzione modulare

Gestire form complessi e dinamici in applicazioni mobile, specialmente quando i campi variano in base al contesto o non sono noti a priori, può rapidamente trasformarsi in una fonte di frustrazione per gli sviluppatori.

Spesso, la soluzione si riduce a un costrutto rigido e poco scalabile, con effetti negativi sull’esperienza dell’utente e sulla manutenibilità del codice. Esiste, tuttavia, un approccio più modulare ed efficiente che consente di superare queste criticità.

Il Contesto. Form dinamici in Flutter

Nello sviluppo di applicazioni enterprise in Flutter – ad esempio moduli per raccolta di dati, configuratori o questionari  – è frequente incontrare formi la cui struttura è definita dinamicamente dal server o modificata in tempo reale dall’interazione dell’utente Questa esigenza di dinamismo si scontra con gli approcci tradizionali, basati su interfacce statiche.

Tentare di gestire questa flessibilità con soluzioni rigide porta inevitabilmente a codice fortemente duplicato, di difficile lettura, complesso da testare e soprattutto da mantenere nel tempo. Ogni variazione nella struttura del form può richiedere, infatti, interventi su più parti dell’applicazione, rallentando lo sviluppo, aumentando i costi e il rischio di introdurre bug.

L’impatto si manifesta a più livelli. Per il business la difficoltà nel gestire e adattare rapidamente i moduli comporta ritardi nel rilascio delle funzionalità, allungando il time-to-market. Inoltre, la scarsa affidabilità dell’interfaccia può compromettere la qualità dei dati raccolti. Per il team tecnico gli effetti sono rilevanti: la complessità del codice cresce rapidamente con l’aumentare dei casi d’uso, rendendo più onerosa la manutenzione e limitando la possibilità di scalare il sistema in modo efficiente. Quando i form diventano numerosi o variano sensibilmente tra diversi contesti, l’approccio statico mostra tutti i suoi limiti.
 

Il Problema

Le sfide principali nell’implementazione di form dinamici in Flutter sono generalmente legate ai seguenti aspetti:

  1. numero variabile di campi da gestire; 
  2. validazioni personalizzate dei singoli campi;
  3. gestione dello stato dei campi e relativa modifica in tempo reale dell’interfaccia.

Spesso si tenta di risolverlo hard-codando le interfacce oppure replicando porzioni di codice per ogni tipo di form, con evidenti problemi:Spesso questo problema viene affrontato creando interfacce hard-coded o replicando parti di codice per ogni tipo di form. Questo approccio presenta, nondimeno, evidenti problematiche come la difficoltà nel mantenere coerenza tra i form, le modifiche che richiedono interventi su più punti del codice e l’elevato rischio di bug e regressioni.

Gli Obiettivi

Per superare le criticità, si devono perseguire obiettivi precisi, volti a migliorare efficienza, flessibilità e manutenibilità. 

  • È fondamentale, in primis, realizzare form modulari e generabili dinamicamente.
  • Questo permette di ridurre drasticamente la duplicazione del codice, rendendolo più pulito e gestibile. 
  • La soluzione deve garantire massima flessibilità e reattività, consentendo di integrare facilmente nuove tipologie di campo, comportamenti condizionali e regole di validazione complesse senza richiedere modifiche invasive. 
  • È, infine, cruciale assicurare una gestione centralizzata dello stato del form e dei dati immessi, migliorando la coerenza e l'affidabilità dell'applicazione.

La Soluzione

La soluzione consigliata si basa su un form dinamico in Flutter che utilizza un approccio modulare e scalabile tramite l’utilizzo di tre liste principali, che condividono lo stesso id univoco per il singolo campo:

  1. una lista di controller per gestire i valori inseriti;
  2. una lista di condition per gestire la visibilità dei campi;
  3. una lista di field che rappresentano le domande ricevute dal server

Per implementare la soluzione si procede, dunque, seguendo questi passaggi.

[1] Il primo riguarda la ricezione e il parsing dei dati dal backend, da cui arriva un elenco di domande(field) che viene trasformato in una lista di oggetti dinamici.

List<dynamic> questions = jsonData['questions'];

[2] Il secondo setp è l'inizializzazione delle strutture dati: scorrendo la lista di oggetti dinamici si generano le tre liste fondamentali aggiungendo alle relative liste il controller, la condizione e il field.

Map<String, TextEditingController> controllers = [];
Map<String, bool> conditions = [];
Map<String, dynamic> fields = [];

for (var question in questions) {
  final id = question['id'];

  controllers[id] = TextEditingController();
  conditions[id] = true; //visibile di default
  fields[id] = question;
}

[3] Si procede, dunque, con la costruzione dinamica dell’interfaccia. Scorrendo la lista dei field si costruiscono dinamicamente i widget di input, agganciando i controller (dalla lista) e gestendo la visibilità (dalla lista condition)

List<Widget> buildForm() {
  return fields.entries.map((entry) {
    final id = entry.key;
    final field = entry.value;

    if (!conditions[id]!) return const SizedBox.shrink();

    return TextFormField(
      controller: controllers[id],
      decoration: InputDecoration(
        labelText: field['question'],
      ),
      onChanged: (_) => evaluateConditions(),
    );
  }).toList();
}

[4] La gestione della visibilità condizionata è il passo successivo, in questa fase si gestisce la modifica dei campi aggiornando le eventuali condition all’interno della lista e aggiornando di conseguenza l’interfaccia.

void evaluateConditions() {
  fields.forEach((id, field) {
    final condition = field['condition'];
    if (condition != null) {
      final dependsOn = condition['id'];
      final expectedValue = condition['value'];

      final actualValue = controllers[dependsOn]?.text ?? '';
      conditions[id] = actualValue == expectedValue;
    }
  });
  setState(() {});
}

[5] Infine si procede alla validazione e all’invio dei dati: alla conferma del form si raccolgono i dati dalla lista dei controller, vengono ripuliti gli eventuali dati “sporchi” controllando la lista delle condition e si invia il risultato al backend.

Map<String, String> getFormValues() {
  final result = <String, String>{};

  controllers.forEach((id, controller) {
    if (conditions[id] == true) {
      result[id] = controller.text;
    }
  });

  return result;
}

 

I Risultati

Adottando questo approccio, come si può evincere anche dal codice dinamico, si ottengono molti vantaggi:

  1. il numero dei campi può variare senza modificare la logica della UI;
  2. si ha la separazione dei dati dalla logica di rendering;
  3. è facilmente estendibile per supportare dropdown, checkbox, datepicker e altro;
  4. il codice è leggibile, manutenibile e testabile;
  5. si ottiene una riduzione dei tempi di sviluppo e debug;
  6. il tutto si concretizza in costi minori di sviluppo (e di modifiche future).

Conclusioni

Considerando dunque la possibilità di realizzare form dinamici in Flutter, il consiglio per chi sviluppa applicazioni mobile non può che essere quello di abbracciare la modularità. Creare UI che si adattano dinamicamente ai dati, infatti, non solo rende l’applicazione più robusta, ma riduce la complessità nelle attività di gestione e manutenzione. Miriade, come partner tecnologico esperto, ha realizzato molti progetti con quest’ottica e può accompagnarti nel percorso.

Se sei interessato allo sviluppo di applicazioni in Flutter potrebbe esserti utile anche questo approfondimento sugli acquisti in-app con Flutter (strategia per monetizzare le applicazioni mobile)

 

Ti è piaciuto quanto hai letto? Iscriviti a MISPECIAL, la nostra newsletter, per ricevere altri interessanti contenuti.

Iscriviti a MISPECIAL
Contenuti simili
DIGITAL ENTERPRISE
Apr 24, 2025

Gestire form complessi e dinamici in applicazioni mobile, specialmente quando i campi variano in base al contesto o non sono noti a priori, può rapidamente trasformarsi in una fonte di frustrazione per gli sviluppatori.

DIGITAL ENTERPRISE
Mar 24, 2025

Con l'introduzione di Android 13 (API level 33), Google ha modificato il sistema dei permessi. Per le applicazioni Flutter si rende necessaria una nuova configurazione per garantire il download e la corretta gestione dei file.