Widgets

Pullable

Einleitung

Das Pullable-Widget fuegt Pull-to-Refresh- und Nachladen-Funktionalitaet zu jedem scrollbaren Inhalt hinzu. Es umschliesst Ihr Child-Widget mit gestengesteuertem Aktualisierungs- und Paginierungsverhalten und unterstuetzt mehrere Header-Animationsstile.

Aufgebaut auf dem pull_to_refresh_flutter3-Paket bietet Pullable eine uebersichtliche API mit benannten Konstruktoren fuer gaengige Konfigurationen.

Pullable(
  onRefresh: () async {
    // Fetch fresh data
    await fetchData();
  },
  child: ListView(
    children: items.map((item) => ListTile(title: Text(item))).toList(),
  ),
)

Grundlegende Verwendung

Umschliessen Sie jedes scrollbare Widget mit Pullable:

Pullable(
  onRefresh: () async {
    await loadLatestPosts();
  },
  child: ListView.builder(
    itemCount: posts.length,
    itemBuilder: (context, index) => PostCard(post: posts[index]),
  ),
)

Wenn der Benutzer die Liste nach unten zieht, wird der onRefresh-Callback ausgeloest. Der Aktualisierungsindikator wird automatisch abgeschlossen, wenn der Callback fertig ist.

Konstruktoren

Pullable bietet benannte Konstruktoren fuer gaengige Konfigurationen:

Konstruktor Header-Stil Beschreibung
Pullable() Water Drop Standardkonstruktor
Pullable.classicHeader() Classic Klassischer Pull-to-Refresh-Stil
Pullable.waterDropHeader() Water Drop Wassertropfen-Animation
Pullable.materialClassicHeader() Material Classic Material-Design-Klassik-Stil
Pullable.waterDropMaterialHeader() Water Drop Material Material-Wassertropfen-Stil
Pullable.bezierHeader() Bezier Bezier-Kurven-Animation
Pullable.noBounce() Konfigurierbar Reduzierter Bounce mit ClampingScrollPhysics
Pullable.custom() Benutzerdefiniertes Widget Eigene Header-/Footer-Widgets verwenden
Pullable.builder() Konfigurierbar Vollstaendige PullableConfig-Kontrolle

Beispiele

// 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

Fuer detaillierte Kontrolle verwenden Sie PullableConfig mit dem Pullable.builder()-Konstruktor:

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,
)

Alle Konfigurationsoptionen

Eigenschaft Typ Standard Beschreibung
enablePullDown bool true Pull-Down-zum-Aktualisieren aktivieren
enablePullUp bool false Pull-Up-zum-Nachladen aktivieren
physics ScrollPhysics? null Benutzerdefinierte Scroll-Physik
onRefresh Future<void> Function()? null Aktualisierungs-Callback
onLoading Future<void> Function()? null Nachladen-Callback
headerType PullableHeaderType waterDrop Header-Animationsstil
customHeader Widget? null Benutzerdefiniertes Header-Widget
customFooter Widget? null Benutzerdefiniertes Footer-Widget
refreshCompleteDelay Duration Duration.zero Verzoegerung vor Abschluss der Aktualisierung
loadCompleteDelay Duration Duration.zero Verzoegerung vor Abschluss des Nachladens
enableOverScroll bool true Ueber-Scroll-Effekt erlauben
cacheExtent double? null Scroll-Cache-Ausdehnung
semanticChildCount int? null Semantische Kindanzahl
dragStartBehavior DragStartBehavior start Wie Ziehgesten beginnen

Header-Stile

Waehlen Sie aus fuenf integrierten Header-Animationen:

enum PullableHeaderType {
  classic,           // Classic pull indicator
  waterDrop,         // Water drop animation (default)
  materialClassic,   // Material Design classic
  waterDropMaterial,  // Material water drop
  bezier,            // Bezier curve animation
}

Setzen Sie den Stil ueber den Konstruktor oder die Konfiguration:

// 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,
)

Nach oben ziehen zum Nachladen

Aktivieren Sie Paginierung mit Pull-Up-Laden:

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]),
  ),
)

Benutzerdefinierte Header und Footer

Stellen Sie eigene Header- und Footer-Widgets bereit:

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

Verwenden Sie einen RefreshController fuer programmatische Steuerung:

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;

Erweiterungsmethoden auf RefreshController

Methode/Getter Rueckgabetyp Beschreibung
triggerRefresh() void Aktualisierung manuell ausloesen
triggerLoading() void Nachladen manuell ausloesen
isRefreshing bool Ob eine Aktualisierung aktiv ist
isLoading bool Ob ein Nachladevorgang aktiv ist

Erweiterungsmethode

Jedes Widget kann mit Pull-to-Refresh umschlossen werden, indem die .pullable()-Erweiterung verwendet wird:

ListView(
  children: items.map((item) => ListTile(title: Text(item.name))).toList(),
).pullable(
  onRefresh: () async {
    await fetchItems();
  },
)

Mit benutzerdefinierter Konfiguration:

myListView.pullable(
  onRefresh: () async => await refreshData(),
  pullableConfig: PullableConfig(
    headerType: PullableHeaderType.classic,
    enablePullUp: true,
    onLoading: () async => await loadMore(),
  ),
)

CollectionView-Integration

CollectionView bietet Pullable-Varianten mit integrierter Paginierung:

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,
)

Pullable-spezifische Parameter

Parameter Typ Beschreibung
data Function(int iteration) Paginierter Daten-Callback (Iteration beginnt bei 1)
onRefresh Function()? Callback nach der Aktualisierung
beforeRefresh Function()? Hook vor Beginn der Aktualisierung
afterRefresh Function(dynamic)? Hook nach der Aktualisierung mit Daten
headerStyle String? Header-Typname (z. B. 'WaterDropHeader', 'ClassicHeader')
footerLoadingIcon Widget? Benutzerdefinierter Ladeindikator fuer den Footer

Beispiele

Paginierte Liste mit Aktualisierung

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]),
        ),
      ),
    );
  }
}

Einfache Aktualisierung mit Erweiterung

ListView(
  children: notifications
    .map((n) => ListTile(
      title: Text(n.title),
      subtitle: Text(n.body),
    ))
    .toList(),
).pullable(
  onRefresh: () async {
    notifications = await fetchNotifications();
    setState(() {});
  },
)