Navigation

Pagination

Display the current active page and enable navigation between multiple pages.

FPagination(control: const FPaginationControl.managed(pages: 10));

CLI

To generate and customize this style:

dart run forui style create pagination

Usage

FPagination(...)

FPagination(
  control: FPaginationControl.managed(
    pages: 20,
    initial: 4,
    showEdges: false,
    siblings: 2,
    onChange: (page) {},
  ),
  style: (style) => style.copyWith(...),
  previous: const Placeholder(),
  next: const Placeholder(),
);

Examples

Siblings

FPagination(
  control: const FPaginationControl.managed(
    pages: 20,
    siblings: 2,
    initial: 9,
  ),
);

Hide Edges

FPagination(
  control: const FPaginationControl.managed(
    pages: 8,
    showEdges: false,
  ),
);

Custom Icons

class CustomIconPagination extends StatefulWidget {
  const CustomIconPagination({super.key});

  @override
  State<CustomIconPagination> createState() => _CustomIconPaginationState();
}

class _CustomIconPaginationState extends State<CustomIconPagination> {
  final _controller = FPaginationController(pages: 10, page: 4);

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final style = context.theme.paginationStyle;
    return FPagination(
      control: FPaginationControl.managed(controller: _controller),
      next: Padding(
        padding: style.itemPadding,
        child: ConstrainedBox(
          constraints: style.itemConstraints,
          child: FButton.icon(
            style: FButtonStyle.ghost(),
            onPress: _controller.next,
            child: IconTheme(data: style.itemIconStyle.resolve({}), child: const Icon(FIcons.bird)),
          ),
        ),
      ),
      previous: Padding(
        padding: style.itemPadding,
        child: ConstrainedBox(
          constraints: style.itemConstraints,
          child: FButton.icon(
            style: FButtonStyle.ghost(),
            onPress: _controller.previous,
            child: IconTheme(data: style.itemIconStyle.resolve({}), child: const Icon(FIcons.anchor)),
          ),
        ),
      ),
    );
  }
}

With a PageView

class PageViewExample extends StatefulWidget {
  const PageViewExample({super.key});

  @override
  State<PageViewExample> createState() => _PageViewExampleState();
}

class _PageViewExampleState extends State<PageViewExample> {
  final _controller = PageController();
  final _paginationController = FPaginationController(pages: 10);

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _paginationController.value = PageStorage.maybeOf(context)?.readState(context) ?? 0;
  }

  void _handlePageChange(int page) {
    final old = _controller.page?.round();
    if (old case final old when old != page) {
      if (page == old! + 1 || page == old - 1) {
        setState(() {
          _controller.animateToPage(page, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut);
        });
      } else {
        setState(() {
          _controller.jumpToPage(page);
        });
      }
    }
  }

  @override
  void dispose() {
    _paginationController.dispose();
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final colors = context.theme.colors;
    return Column(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      spacing: 10,
      children: [
        SizedBox(
          height: 300,
          width: 300,
          child: NotificationListener<ScrollEndNotification>(
            onNotification: (notification) {
              if (_controller.hasClients) {
                _paginationController.value = _controller.page!.round();
                return true;
              }
              return false;
            },
            child: PageView.builder(
              itemCount: 10,
              controller: _controller,
              itemBuilder: (context, index) => ColoredBox(
                color: index.isEven ? colors.hover(colors.primary) : colors.mutedForeground,
                child: Center(
                  child: DefaultTextStyle(
                    style: TextStyle(fontSize: 45, color: colors.primaryForeground),
                    child: Text('Page ${index + 1}'),
                  ),
                ),
              ),
            ),
          ),
        ),
        FPagination(
          control: FPaginationControl.managed(controller: _paginationController, onChange: _handlePageChange),
        ),
      ],
    );
  }
}

On this page