buildListView<T, K> method

Widget buildListView<T, K>({
  1. required BuildContext context,
  2. required List<TListItem<T, K>> items,
  3. required ListItemBuilder<T, K> itemBuilder,
  4. required AnimationController animationController,
  5. required ScrollController scrollController,
  6. required ScrollController horizontalScrollController,
  7. required TListController<T, K> listController,
  8. required TListInteraction<T> interaction,
  9. required bool loading,
  10. required bool hasError,
  11. required TListError? error,
  12. required bool hasMoreItems,
  13. double? height,
})

Implementation

Widget buildListView<T, K>({
  required BuildContext context,
  required List<TListItem<T, K>> items,
  required ListItemBuilder<T, K> itemBuilder,
  required AnimationController animationController,
  required ScrollController scrollController,
  required ScrollController horizontalScrollController,
  required TListController<T, K> listController,
  required TListInteraction<T> interaction,
  required bool loading,
  required bool hasError,
  required TListError? error,
  required bool hasMoreItems,
  double? height,
}) {
  final effectiveInfiniteScroll = infiniteScroll == true && listController.hasMoreItems;

  // Error state takes priority
  if (hasError && error != null && items.isEmpty) {
    return buildErrorState(context, error);
  }

  // Empty state when no items and not loading
  if (items.isEmpty && !loading) {
    return buildEmptyState(context);
  }

  Widget buildItem(BuildContext context, int index) {
    // Header (non-sticky)
    if (headerWidget != null && headerSticky != true && index == 0) {
      return headerWidget!;
    }

    // Calculate item index offset
    final itemOffset = (headerWidget != null && headerSticky != true) ? 1 : 0;
    final itemIndex = index - itemOffset;

    // Infinite scroll indicator
    final infiniteScrollIndex = items.length + itemOffset;
    if (effectiveInfiniteScroll && index == infiniteScrollIndex) {
      return _buildInfiniteScrollFooter(context, items, loading, hasMoreItems);
    }

    // Footer (non-sticky)
    final footerIndex = items.length + itemOffset + (effectiveInfiniteScroll ? 1 : 0);
    if (footerWidget != null && footerSticky != true && index == footerIndex) {
      return footerWidget!;
    }

    // Regular item
    final item = items[itemIndex];
    Widget child = interaction.buildGestureDetector(
      key: ObjectKey(item),
      item: item.data,
      index: itemIndex,
      selectable: listController.selectable,
      expandable: listController.expandable,
      controller: listController,
      child: itemBuilder(context, item, itemIndex, listController.isMultiSelect),
    );

    // Apply animation
    if (animationBuilder != null) {
      child = animationBuilder!(context, animationController, child, itemIndex);
    }

    // Add spacing
    if (itemSpacing > 0) {
      child = Padding(
        padding: EdgeInsets.only(bottom: itemSpacing),
        child: child,
      );
    }

    return child;
  }

  final totalItemCount = items.length +
      (headerWidget != null && headerSticky != true ? 1 : 0) +
      (footerWidget != null && footerSticky != true ? 1 : 0) +
      (effectiveInfiniteScroll ? 1 : 0);

  final effectivePhysics = physics ?? (shrinkWrap ? const NeverScrollableScrollPhysics() : const AlwaysScrollableScrollPhysics());

  Widget listView;

  if (showSeparators && separatorBuilder != null) {
    listView = ListView.separated(
      controller: shrinkWrap ? null : scrollController,
      shrinkWrap: shrinkWrap,
      physics: effectivePhysics,
      padding: padding,
      itemCount: totalItemCount,
      itemBuilder: buildItem,
      separatorBuilder: separatorBuilder!,
    );
  } else {
    listView = ListView.builder(
      controller: shrinkWrap ? null : scrollController,
      shrinkWrap: shrinkWrap,
      physics: effectivePhysics,
      padding: padding,
      itemCount: totalItemCount,
      itemBuilder: buildItem,
    );
  }

  // Build column with sticky elements
  final column = Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      if (loading && items.isEmpty) buildLoadingIndicator(context),
      if (headerWidget != null && headerSticky == true) headerWidget!,
      if (shrinkWrap) listView else if (height != null) SizedBox(height: height, child: listView) else Expanded(child: listView),
      if (footerWidget != null && footerSticky == true) footerWidget!,
    ],
  );

  // Wrap with horizontal scroll if needed
  final scrollableContent = needsHorizontalScroll ? _buildHorizontalScroll(column, horizontalScrollController) : column;

  return scrollableContent;
}