Lo scroll infinito (Infinite Scroll) è una tecnica di caricamento automatico del contenuto quando l’utente raggiunge la fine dell’elenco corrente. Secondo UX Design Collective, 2024, l’Infinite Scroll aumenta il tempo di sessione nei social network del 40–60% rispetto alla paginazione. Nello sviluppo mobile, questa tecnica viene implementata attraverso una combinazione di scroll listener e richieste API con paginazione basata su cursore. Lo scroll infinito è diventato lo standard de facto per i feed di contenuti, ma richiede un’implementazione attenta per evitare problemi di prestazioni e navigazione.
Punti Chiave
Lo scroll infinito (Infinite Scroll) è un pattern di caricamento dati in cui nuovi elementi vengono automaticamente aggiunti alla fine dell’elenco man mano che l’utente scorre. L’utente non clicca sui pulsanti «Avanti» o «Carica altro» — il sistema stesso determina quando richiedere il lotto successivo di dati e inserisce senza soluzione di continuità nuove voci nell’elenco esistente.
L’Infinite Scroll ha guadagnato popolarità grazie ai social network — Twitter, Instagram e TikTok lo utilizzano come meccanismo principale di distribuzione dei contenuti. Secondo Nielsen Norman Group (2024), lo scroll infinito aumenta il coinvolgimento del 30–50% per le applicazioni di contenuti perché riduce il carico cognitivo: l’utente non deve decidere di passare alla pagina successiva. Tuttavia, per attività che richiedono una navigazione precisa (ricerca, confronto prodotti), lo scroll infinito può ridurre l’efficienza.
Tecnicamente, lo scroll infinito è composto da tre componenti: un scroll listener (tiene traccia della posizione di scorrimento), una soglia (distanza dalla fine dell’elenco per attivare il caricamento) e un meccanismo di paginazione (richiesta API e inserimento dati). La corretta configurazione della soglia è fondamentale: se il trigger è troppo presto (1000 px dalla fine), l’utente riceverà richieste non necessarie; se è troppo tardi (50 px), l’utente noterà una pausa nel caricamento.
L’architettura dello scroll infinito si basa su un modello basato su eventi: il componente elenco genera un evento quando viene raggiunta la soglia di scorrimento, il ViewModel lo elabora e chiama il repository per caricare il lotto successivo di dati. Dopo aver ricevuto la risposta, i nuovi elementi vengono inseriti nell’elenco e l’interfaccia utente viene aggiornata tramite l’adattatore. Questa catena deve essere asincrona e non deve bloccare il thread dell’interfaccia utente.
L’algoritmo base dello scroll infinito comprende quattro passaggi. Inizializzazione: all’apertura iniziale dello schermo, viene caricato il primo lotto di dati (pagina 1 o cursore = null). Monitoraggio: lo scroll listener verifica se l’utente ha raggiunto la soglia — generalmente 200–500 px dalla fine dell’elenco. Caricamento: viene inviata una richiesta all’API con parametri di paginazione, un indicatore di caricamento (spinner nel piè di pagina) viene mostrato nell’interfaccia. Inserimento: i nuovi elementi vengono aggiunti all’adattatore, la posizione di scorrimento viene regolata per evitare salti.
Un aspetto critico è il debounce delle richieste. Se l’utente scorre rapidamente fino alla fine, il trigger può attivarsi più volte prima di ricevere una risposta dal server. Senza debounce, ciò porta a richieste duplicate (condizione di competizione). La soluzione è bloccare le nuove richieste fino al completamento della precedente. Un flag isLoading nel ViewModel previene chiamate multiple: imposta isLoading = true all’invio di una richiesta, reimposta al ricevimento di una risposta o di un errore.
Sulle piattaforme mobili, vengono utilizzati meccanismi specializzati per lo scroll infinito. Su iOS, è il prefetchDataSource in UICollectionView, che richiede automaticamente dati per le celle fuori schermo. Su Android, è la libreria Paging 3 di Google, che fornisce un’architettura pronta con PagingSource, PagingData e PagingDataAdapter. Paging 3 supporta RemoteMediator per combinare dati di rete e locali e gestisce automaticamente lo stato di caricamento.
La paginazione basata su offset utilizza parametri di pagina e dimensione: page=2, size=20 restituisce i record 21–40. Questo approccio è semplice da implementare ma ha un problema fondamentale — se vengono aggiunti o rimossi record dal database tra le richieste, l’offset si disallinea (l’utente vede duplicati o lacune). Per i feed con elevata frequenza di modifiche (notizie, commenti), la paginazione basata su offset produce risultati errati.
La paginazione basata su cursore utilizza un identificatore univoco dell’ultimo elemento (cursore): after=id_12345&limit=20. Il server restituisce 20 record dopo il cursore specificato. Questo approccio garantisce la coerenza dei dati indipendentemente da inserimenti ed eliminazioni. Secondo GraphQL Best Practices (2024), la paginazione basata su cursore è raccomandata per tutte le applicazioni in tempo reale dove i dati cambiano dinamicamente.
La scelta tra gli approcci dipende dal tipo di applicazione. Per i social network (Instagram, TikTok) — solo basata su cursore, poiché il feed viene costantemente aggiornato. Per i cataloghi con modifiche poco frequenti (categorie di prodotti di negozi online), la paginazione basata su offset è accettabile. Per scenari ibridi, Google raccomanda Paging 3 RemoteMediator, che combina la paginazione basata su cursore dalla rete con la paginazione basata su offset dal database locale Room.
Su Android, l’approccio standard è la libreria Paging 3 di Jetpack. PagingSource definisce la fonte dei dati (rete o database), PagingData contiene blocchi di dati e PagingDataAdapter li visualizza in RecyclerView. Paging 3 gestisce automaticamente la distanza di precaricamento, i tentativi e l’aggiornamento. Per l’integrazione di rete, viene utilizzato RemoteMediator: carica i dati dall’API, li salva in Room e notifica PagingSource degli aggiornamenti. Secondo Google I/O 2024, oltre il 60% delle applicazioni Android con scroll infinito utilizza Paging 3.
Esempio di implementazione base di Paging 3:
class FeedPagingSource(
private val api: FeedApi
) : PagingSource<String, Post>() {
override suspend fun load(
params: LoadParams<String>
): LoadResult<String, Post> {
val response = api.getFeed(
cursor = params.key,
limit = params.loadSize
)
return LoadResult.Page(
data = response.items,
prevKey = null,
nextKey = response.nextCursor
)
}
}
Su iOS, lo scroll infinito viene implementato utilizzando UICollectionView con prefetchDataSource. Il protocollo UICollectionViewDataSourcePrefetching contiene il metodo collectionView(_:prefetchItemsAt:), che viene chiamato quando il sistema anticipa lo scorrimento verso determinati index paths. A differenza di Android Paging 3, iOS non ha una libreria di paginazione integrata — gli sviluppatori la implementano manualmente o utilizzano soluzioni di terze parti come RxSwift + NSLayoutConstraint o pipeline basate su Combine.
Esempio di precaricamento su iOS:
extension FeedViewController: UICollectionViewDataSourcePrefetching {
func collectionView(
_ collectionView: UICollectionView,
prefetchItemsAt indexPaths: [IndexPath]
) {
let lastRow = collectionView.numberOfItems(inSection: 0) - 1
if indexPaths.contains(IndexPath(row: lastRow, section: 0)) {
viewModel.loadNextPage()
}
}
}
SwiftUI offre un approccio più dichiarativo attraverso il modificatore onAppear. Lo sviluppatore posiziona un ProgressView alla fine dell’elenco e quando appare, attiva il caricamento della pagina successiva. Secondo Apple WWDC 2024, le nuove API AsyncSequence e Swift Algorithms semplificano l’implementazione dello scroll infinito fornendo operatori integrati di chunking e debounce.
Il principale problema UX dello scroll infinito è la perdita del piè di pagina e della navigazione. Nei negozi online, gli utenti spesso vogliono andare al piè di pagina per contatti o link. Lo scroll infinito rende il piè di pagina inaccessibile — si sposta continuamente verso il basso man mano che viene caricato più contenuto. La soluzione è aggiungere un pulsante di azione fluttuante (FAB) per lo scorrimento rapido verso l’alto o fissare il piè di pagina separatamente dall’elenco.
Il secondo problema è la mancanza di cronologia di scorrimento. Se un utente vede un prodotto interessante alla posizione 3, scorre fino alla posizione 50 e poi preme «Indietro» — torna all’inizio dell’elenco e deve scorrere di nuovo fino alla posizione 50. La soluzione è salvare la posizione di scorrimento nel ViewModel o utilizzare il ripristino dello stato a livello di Activity/UIViewController. iOS supporta NSUserActivity per il ripristino della posizione, Android supporta onSaveInstanceState.
Il terzo problema è le prestazioni con migliaia di elementi. Se la virtualizzazione non è configurata, dopo 500–1000 elementi caricati l’applicazione inizia a rallentare a causa dell’aumento del consumo di memoria. La soluzione è utilizzare la virtualizzazione con RecyclerView o UICollectionView, che mantiene in memoria solo le celle visibili e precaricate. La pulizia periodica dei dati vecchi (scarto delle pagine oltre N pagine) riduce anche il carico.
Domande frequenti
Lo scroll infinito (Infinite Scroll) è una tecnica per caricare automaticamente il contenuto quando l’utente raggiunge il fondo dell’elenco. I nuovi dati vengono aggiunti senza soluzione di continuità senza bisogno di cliccare sui pulsanti di paginazione. Viene utilizzato nei social network, nei feed di notizie e nei cataloghi con contenuti dinamici.
La paginazione richiede la navigazione manuale tra le pagine (pulsanti «1, 2, 3»), mentre l’Infinite Scroll carica i dati automaticamente. La paginazione è prevedibile e preserva il contesto di navigazione; lo scroll infinito aumenta il coinvolgimento ma complica l’accesso al piè di pagina e alla cronologia di scorrimento. La scelta dipende dal tipo di contenuto e dagli obiettivi dell’applicazione.
Su Android, la libreria raccomandata è Paging 3 di Jetpack. Fornisce PagingSource per la fonte dati, PagingData per i blocchi e PagingDataAdapter per RecyclerView. Paging 3 gestisce automaticamente il precaricamento, lo stato di caricamento e i tentativi. Per scenari ibridi offline/online, utilizzare RemoteMediator.
Le richieste duplicate vengono prevenute tramite un flag di debounce isLoading. Quando viene inviata la prima richiesta, il flag viene impostato su true e blocca nuove chiamate fino al ricevimento di una risposta. Dopo una risposta riuscita, il flag viene reimpostato. Inoltre, è possibile annullare le coroutine (Kotlin) o Cancellable (Swift) durante lo scorrimento all’indietro.
Lo scroll infinito non è adatto per l’e-commerce con ricerca e confronto prodotti, per applicazioni con un piè di pagina importante (contatti, link), per pagine di risultati di ricerca (l’utente deve tornare a un elemento specifico) e per pagine di statistiche/report dove il conteggio totale è importante. In questi casi, utilizzare la paginazione classica o un pulsante «Carica altro».
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche