Pullable
Introduzione
Il widget Pullable aggiunge funzionalita' di pull-to-refresh e caricamento aggiuntivo a qualsiasi contenuto scrollabile. Avvolge il tuo widget figlio con un comportamento di refresh e paginazione guidato da gesture, supportando diversi stili di animazione dell'header.
Costruito sopra il pacchetto pull_to_refresh_flutter3, Pullable fornisce un'API pulita con costruttori con nome per le configurazioni comuni.
Pullable(
onRefresh: () async {
// Fetch fresh data
await fetchData();
},
child: ListView(
children: items.map((item) => ListTile(title: Text(item))).toList(),
),
)
Utilizzo Base
Avvolgi qualsiasi widget scrollabile con Pullable:
Pullable(
onRefresh: () async {
await loadLatestPosts();
},
child: ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) => PostCard(post: posts[index]),
),
)
Quando l'utente trascina verso il basso sulla lista, viene eseguito il callback onRefresh. L'indicatore di refresh si completa automaticamente quando il callback termina.
Costruttori
Pullable fornisce costruttori con nome per le configurazioni comuni:
| Costruttore | Stile Header | Descrizione |
|---|---|---|
Pullable() |
Water Drop | Costruttore predefinito |
Pullable.classicHeader() |
Classic | Stile pull-to-refresh classico |
Pullable.waterDropHeader() |
Water Drop | Animazione goccia d'acqua |
Pullable.materialClassicHeader() |
Material Classic | Stile classico Material Design |
Pullable.waterDropMaterialHeader() |
Water Drop Material | Stile goccia d'acqua Material |
Pullable.bezierHeader() |
Bezier | Animazione curva di Bezier |
Pullable.noBounce() |
Configurabile | Rimbalzo ridotto con ClampingScrollPhysics |
Pullable.custom() |
Widget personalizzato | Usa i tuoi widget header/footer |
Pullable.builder() |
Configurabile | Controllo completo tramite PullableConfig |
Esempi
// Classic header
Pullable.classicHeader(
onRefresh: () async => await refreshData(),
child: myListView,
)
// Material header
Pullable.materialClassicHeader(
onRefresh: () async => await refreshData(),
child: myListView,
)
// No bounce effect
Pullable.noBounce(
onRefresh: () async => await refreshData(),
headerType: PullableHeaderType.classic,
child: myListView,
)
// Custom header widget
Pullable.custom(
customHeader: MyCustomRefreshHeader(),
onRefresh: () async => await refreshData(),
child: myListView,
)
PullableConfig
Per un controllo dettagliato, usa PullableConfig con il costruttore Pullable.builder():
Pullable.builder(
config: PullableConfig(
enablePullDown: true,
enablePullUp: true,
headerType: PullableHeaderType.materialClassic,
onRefresh: () async => await refreshData(),
onLoading: () async => await loadMoreData(),
refreshCompleteDelay: Duration(milliseconds: 500),
loadCompleteDelay: Duration(milliseconds: 300),
physics: BouncingScrollPhysics(),
),
child: myListView,
)
Tutte le Opzioni di Configurazione
| Proprieta' | Tipo | Predefinito | Descrizione |
|---|---|---|---|
enablePullDown |
bool |
true |
Abilita pull-down-to-refresh |
enablePullUp |
bool |
false |
Abilita pull-up-to-load-more |
physics |
ScrollPhysics? |
null | Fisica di scroll personalizzata |
onRefresh |
Future<void> Function()? |
null | Callback di refresh |
onLoading |
Future<void> Function()? |
null | Callback di caricamento aggiuntivo |
headerType |
PullableHeaderType |
waterDrop |
Stile di animazione dell'header |
customHeader |
Widget? |
null | Widget header personalizzato |
customFooter |
Widget? |
null | Widget footer personalizzato |
refreshCompleteDelay |
Duration |
Duration.zero |
Ritardo prima del completamento del refresh |
loadCompleteDelay |
Duration |
Duration.zero |
Ritardo prima del completamento del caricamento |
enableOverScroll |
bool |
true |
Permette l'effetto over-scroll |
cacheExtent |
double? |
null | Estensione della cache di scroll |
semanticChildCount |
int? |
null | Conteggio figli semantici |
dragStartBehavior |
DragStartBehavior |
start |
Come iniziano le gesture di trascinamento |
Stili dell'Header
Scegli tra cinque animazioni dell'header integrate:
enum PullableHeaderType {
classic, // Classic pull indicator
waterDrop, // Water drop animation (default)
materialClassic, // Material Design classic
waterDropMaterial, // Material water drop
bezier, // Bezier curve animation
}
Imposta lo stile tramite il costruttore o la configurazione:
// Via named constructor
Pullable.bezierHeader(
onRefresh: () async => await refreshData(),
child: myListView,
)
// Via config
Pullable.builder(
config: PullableConfig(
headerType: PullableHeaderType.bezier,
onRefresh: () async => await refreshData(),
),
child: myListView,
)
Pull-Up per Caricare Altro
Abilita la paginazione con il caricamento pull-up:
Pullable.builder(
config: PullableConfig(
enablePullDown: true,
enablePullUp: true,
onRefresh: () async {
// Reset to page 1
page = 1;
items = await fetchItems(page: page);
setState(() {});
},
onLoading: () async {
// Load next page
page++;
List<Item> more = await fetchItems(page: page);
items.addAll(more);
setState(() {});
},
),
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) => ItemTile(item: items[index]),
),
)
Header e Footer Personalizzati
Fornisci i tuoi widget header e footer:
Pullable.custom(
customHeader: Container(
height: 60,
alignment: Alignment.center,
child: CircularProgressIndicator(),
),
customFooter: Container(
height: 40,
alignment: Alignment.center,
child: Text("Loading more..."),
),
enablePullUp: true,
onRefresh: () async => await refreshData(),
onLoading: () async => await loadMore(),
child: myListView,
)
Controller
Usa un RefreshController per il controllo programmatico:
final RefreshController _controller = RefreshController();
Pullable(
controller: _controller,
onRefresh: () async => await refreshData(),
child: myListView,
)
// Trigger refresh programmatically
_controller.triggerRefresh();
// Trigger loading programmatically
_controller.triggerLoading();
// Check state
bool refreshing = _controller.isRefreshing;
bool loading = _controller.isLoading;
Metodi di Estensione su RefreshController
| Metodo/Getter | Tipo di Ritorno | Descrizione |
|---|---|---|
triggerRefresh() |
void |
Attiva manualmente un refresh |
triggerLoading() |
void |
Attiva manualmente il caricamento aggiuntivo |
isRefreshing |
bool |
Se il refresh e' attivo |
isLoading |
bool |
Se il caricamento e' attivo |
Metodo di Estensione
Qualsiasi widget puo' essere avvolto con pull-to-refresh usando l'estensione .pullable():
ListView(
children: items.map((item) => ListTile(title: Text(item.name))).toList(),
).pullable(
onRefresh: () async {
await fetchItems();
},
)
Con configurazione personalizzata:
myListView.pullable(
onRefresh: () async => await refreshData(),
pullableConfig: PullableConfig(
headerType: PullableHeaderType.classic,
enablePullUp: true,
onLoading: () async => await loadMore(),
),
)
Integrazione con CollectionView
CollectionView fornisce varianti pullable con paginazione integrata:
CollectionView.pullable
CollectionView<User>.pullable(
data: (iteration) async => api.getUsers(page: iteration),
builder: (context, item) => UserTile(user: item.data),
onRefresh: () => print('Refreshed!'),
headerStyle: 'WaterDropHeader',
)
CollectionView.pullableSeparated
CollectionView<User>.pullableSeparated(
data: (iteration) async => api.getUsers(page: iteration),
builder: (context, item) => UserTile(user: item.data),
separatorBuilder: (context, index) => Divider(),
)
CollectionView.pullableGrid
CollectionView<Product>.pullableGrid(
data: (iteration) async => api.getProducts(page: iteration),
builder: (context, item) => ProductCard(product: item.data),
crossAxisCount: 2,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
)
Parametri Specifici di Pullable
| Parametro | Tipo | Descrizione |
|---|---|---|
data |
Function(int iteration) |
Callback per dati paginati (l'iterazione parte da 1) |
onRefresh |
Function()? |
Callback dopo il refresh |
beforeRefresh |
Function()? |
Hook prima dell'inizio del refresh |
afterRefresh |
Function(dynamic)? |
Hook dopo il refresh con i dati |
headerStyle |
String? |
Nome del tipo di header (es. 'WaterDropHeader', 'ClassicHeader') |
footerLoadingIcon |
Widget? |
Indicatore di caricamento personalizzato per il footer |
Esempi
Lista Paginata con Refresh
class _PostListState extends NyState<PostListPage> {
List<Post> posts = [];
int page = 1;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Pullable.builder(
config: PullableConfig(
enablePullDown: true,
enablePullUp: true,
headerType: PullableHeaderType.materialClassic,
onRefresh: () async {
page = 1;
posts = await api<PostApiService>((request) => request.getPosts(page: page));
setState(() {});
},
onLoading: () async {
page++;
List<Post> more = await api<PostApiService>((request) => request.getPosts(page: page));
posts.addAll(more);
setState(() {});
},
),
child: ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) => PostCard(post: posts[index]),
),
),
);
}
}
Refresh Semplice con Estensione
ListView(
children: notifications
.map((n) => ListTile(
title: Text(n.title),
subtitle: Text(n.body),
))
.toList(),
).pullable(
onRefresh: () async {
notifications = await fetchNotifications();
setState(() {});
},
)