widgets_extended

High-performance sliver widgets for Flutter: animated tree, sectioned list, and drag-and-drop reorder.

  • SectionedSliverList — header + items list with sticky headers, expand/collapse, and animated insert/remove.
  • SyncedSliverTree — declarative tree that diffs against a source-of-truth and animates the transitions.
  • SliverReorderableTree — drag-and-drop reorder layer on top of the tree.
  • SliverTree + TreeController — imperative escape hatch.

All widgets are built on the same sliver/TreeController core: viewport-aware lazy layout, ECS-style state storage, animation finalization that doesn't relayout idle rows.

SectionedSliverList

Header + items convenience sliver. Two constructor shapes:

// Declarative SectionInputs.
SectionedSliverList<String, String, Folder, FileItem>(
  sections: [
    SectionInput(
      key: folder.id,
      section: folder,
      items: [
        for (final f in folder.files) ItemInput(key: f.id, item: f),
      ],
    ),
    // ...
  ],
  headerBuilder: (context, view) => view.watch(
    builder: (ctx, v) => ListTile(
      title: Text(v.section.name),
      trailing: Icon(v.isExpanded ? Icons.expand_more : Icons.chevron_right),
      onTap: v.toggle,
    ),
  ),
  itemBuilder: (context, view) => ListTile(title: Text(view.item.name)),
  stickyHeaders: true,
  hideEmptySections: false,
  initiallyExpanded: true,
)
// groupListsBy-shaped: pass a Map<Section, List<Item>>.
SectionedSliverList<String, String, Folder, FileItem>.grouped(
  sections: groupedFolders, // Map<Folder, List<FileItem>>
  sectionKeyOf: (folder) => folder.id,
  itemKeyOf: (file) => file.id,
  headerBuilder: ...,
  itemBuilder: ...,
)

Pass a SectionedListController when you need imperative mutations (addItem, removeSection, moveItem, runBatch, ...). Without one, the widget owns its controller internally.

SyncedSliverTree

Use tree: for the simplest entry point when you already have a nested immutable tree:

SyncedSliverTree<String, Folder>(
  tree: <SyncedTreeNode<String, Folder>>[
    SyncedTreeNode<String, Folder>(
      key: root.id,
      data: root,
      children: <SyncedTreeNode<String, Folder>>[
        SyncedTreeNode<String, Folder>(key: child.id, data: child),
      ],
    ),
  ],
  itemBuilder: (context, node) => ListTile(
    title: Text(node.item.name),
    leading: node.hasChildren
        ? IconButton(
            icon: Icon(node.isExpanded ? Icons.expand_more : Icons.chevron_right),
            onPressed: node.toggle,
          )
        : null,
  ),
)

Use .nodes(...) when your data already exists as roots plus childrenOf(key):

SyncedSliverTree<String, RowData>.nodes(
  roots: viewModel.roots,
  childrenOf: viewModel.childrenOf,
  itemBuilder: (context, node) => buildRow(node),
)

SyncedSliverTree also supports .hierarchy(...), .flat(...), and .snapshot(...) for other source-data shapes.

SliverReorderableTree

Wraps a TreeController with a TreeReorderController to add drag-and-drop reorder, including reparenting between branches:

SliverReorderableTree<String, RowData>(
  controller: treeController,
  reorderController: reorderController,
  nodeBuilder: (context, key, depth, wrap) => wrap(
    child: ListTile(title: Text(treeController.getNodeData(key)!.data.label)),
  ),
  indentPerDepth: 24.0,
  dropIndicatorColor: Colors.blue,
)

The wrap(child:) callback turns an arbitrary row into a draggable target with the framework-managed drop indicator.

SliverTree + TreeController (imperative)

The lowest layer. Build it directly when you want full control over insert/remove/expand/collapse timing:

final controller = TreeController<String, RowData>(vsync: this);
controller.setRoots([TreeNode(key: "root", data: root)]);
controller.expand(key: "root", animate: true);

CustomScrollView(slivers: [
  SliverTree<String, RowData>(
    controller: controller,
    nodeBuilder: (context, key, depth) => buildRow(controller.getNodeData(key)!.data),
  ),
])

TreeController exposes addListener (structure changes), addAnimationListener (animation ticks — no relayout), and runBatch(...) (coalesce mutations into one notification).

Libraries

sectioned_sliver_list/_internal_keys
Internal sealed wrapper types used to keep section keys, item keys, section payloads, and item payloads in disjoint domains within a single underlying TreeController.
sectioned_sliver_list/section_input
Immutable inputs for declarative SectionedSliverList construction.
sectioned_sliver_list/sectioned_list_controller
Imperative controller for SectionedSliverList.
sectioned_sliver_list/sectioned_sliver_list
SectionedSliverList — header + items convenience sliver.
sectioned_sliver_list/sectioned_sliver_list_widget
Header + items convenience sliver, built on top of SliverTree.
sectioned_sliver_list/views
Views handed to headerBuilder / itemBuilder callbacks, plus the selective-rebuild helpers (SectionView.watch, ItemView.watch).
sliver_tree/_node_id_registry
Internal: bidirectional key↔nid mapping with free-list recycling.
sliver_tree/_node_store
Internal: structural-component storage for TreeController.
sliver_tree/_sticky_header_computer
Internal: sticky-header layout helper for RenderSliverTree.
sliver_tree/_sync_helpers
Internal sync-time helpers shared by widgets that drive a TreeController through a TreeSyncController. Not exported from the package barrel.
sliver_tree/_visible_order_buffer
Internal: flattened visible-order buffer for a TreeController.
sliver_tree/render_sliver_tree
Render object for SliverTree that handles sliver layout and painting.
sliver_tree/sliver_reorderable_tree
Declarative wrapper around SliverTree that adds drag-and-drop reorder over a TreeReorderController.
sliver_tree/sliver_tree
sliver_tree/sliver_tree_element
Element for SliverTree that manages child element lifecycle.
sliver_tree/sliver_tree_widget
Widget for displaying a tree structure as a sliver.
sliver_tree/synced_sliver_tree
A declarative sliver tree with data-first input modes.
sliver_tree/synced_tree_node
Immutable nested tree node for SyncedSliverTree.
sliver_tree/tree_controller
Controller that manages tree state, visibility, and animations.
sliver_tree/tree_node_builder
A widget that rebuilds only when a specific node's state changes.
sliver_tree/tree_reorder_controller
Orchestrates drag-and-drop reorder over a TreeController-backed SliverTree: gesture lifecycle, drop-target resolution, autoscroll near viewport edges, and FLIP slide animation on commit.
sliver_tree/tree_sync_controller
A diffing/syncing layer on top of TreeController.
sliver_tree/types
Core types for the sliver tree system.
widgets_extended