Flutter无限滚动插件endless的使用

Flutter无限滚动插件endless的使用

Endless

一个基于 CustomScrollView 构建的滚动视图库,提供无限加载功能,支持分页(pagination)、流(streams)和Firestore流。以下是该库的一些特性:

Demo

特性

  1. 数据加载:许多可滚动列表应该用一组初始项目填充,并且当用户向下滚动列表时加载更多数据。此库的主要工作就是将这种逻辑抽象为一个易于使用的API,以便构建动态加载更多数据的无限滚动视图。

  2. 通用滚动元素:许多滚动视图小部件包含一组常见的UI元素,这些元素已经内置到库中,包括用于头部尾部加载指示器空状态的构建器。

  3. 多种数据源:内置对多种数据源的支持,如分页API和流,以最小化客户端在处理滚动视图时需要进行的数据转换工作。

  4. 列表+网格:对于移动应用,大多数情况下可滚动视图使用列表,而在桌面端,更多的屏幕空间适合展示网格。该库提供了带共享API的列表和网格视图。

分页

最常见的无限列表数据源通常是某种分页API。该库提供了两个分页小部件 EndlessPaginationListViewEndlessPaginationGridView 用于处理这种类型的数据。让我们来看一些基本示例:

基本列表示例

import 'package:flutter/material.dart';
import 'package:endless/endless.dart';

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Endless分页列表视图')),
        body: EndlessPaginationListView<String>(
          // 一个异步函数,返回要添加到滚动视图中的项目列表。当您滚动超过配置的`extentAfterFactor`时,它会调用`loadMore`来获取更多项目。
          loadMore: (pageIndex) async => [...],
          // 分页配置决定了何时停止获取项目。
          // 滚动视图将因以下原因停止获取更多数据:
          // 1. 从`loadMore`返回的项目数小于给定的`pageSize`。
          // 2. 它已获取了可选的`maxPages`指定的最大页面数。
          paginationDelegate: EndlessPaginationDelegate(
            pageSize: 5,
            maxPages: 10,
          ),
          itemBuilder: (
            context, {
            required item,
            required index,
            required totalItems,
          }) {
            return Text(item);
          },
        ),
      ),
    );
  }
}

基本网格示例

import 'package:flutter/material.dart';
import 'package:endless/endless.dart';

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Endless分页网格视图')),
        body: EndlessPaginationGridView<String>(
          loadMore: (pageIndex) async => [...],
          paginationDelegate: EndlessPaginationDelegate(
            pageSize: 5,
            maxPages: 10,
          ),
          // 基本列表和网格视图之间的唯一区别在于网格视图指定了其委托,例如在交叉轴上放置多少个项目。
          gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 3,
          ),
          itemBuilder: (
            context, {
            required item,
            required index,
            required totalItems,
          }) {
            return Text(item);
          },
        ),
      ),
    );
  }
}

高级示例

Endless 滚动视图支持一组可选的构建器函数,以构建具有以下自顶向下UI的复杂无限滚动列表:

头部 -> headerBuilder
项目 -> itemBuilder
空状态 -> emptyBuilder
加载指示器 -> loadingBuilder
加载更多小部件(例如 TextButton)-> loadMoreBuilder
尾部 -> footerBuilder
import 'package:flutter/material.dart';
import 'package:endless/endless.dart';

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Endless网格视图')),
        body: EndlessPaginationListView<String>(
          loadMore: (pageIndex) async => [...],
          paginationDelegate: EndlessPaginationDelegate(
            pageSize: 5,
            maxPages: 10,
          ),
          headerBuilder: () => Container(
            color: Colors.blue,
            child: const Text('头部'),
          ),
          itemBuilder: (
            context, {
            required item,
            required index,
            required totalItems,
          }) {
            return Text(item);
          },
        ),
      ),
    );
  }
}

在这个示例中,我们在列表中添加了一个头部。如果我们只想在加载项目后显示头部怎么办?Endless 滚动视图使用了在 Flutter Material 的核心小部件中找到的 StateProperty 模式,例如 MaterialStateProperty

Material UI 使用此模式允许核心小部件(如 ElevatedButton)在处于不同状态(悬停、按下等)时对其进行不同的样式设置。来自文档的基本示例如下:

ElevatedButton(
  style: ButtonStyle(
    // 将绿色作为所有按钮状态的背景颜色。
    backgroundColor: MaterialStateProperty.all<Color>(Colors.green),
  ),
);

ElevatedButton(
  style: ButtonStyle(
    backgroundColor: MaterialStateProperty.resolveWith<Color>(
      // 状态属性传递按钮当前状态的所有状态,
      // 以便可以根据状态自定义按钮样式。
      (Set<MaterialState> states) {
        // 当按钮处于按下状态时,将其颜色变浅。
        if (states.contains(MaterialState.pressed))
          return Theme.of(context).colorScheme.primary.withOpacity(0.5);
        return null;
      },
    ),
  ),
);

我们使用相同的模式来支持根据滚动视图的状态进行自定义。可能的状态如下:

enum EndlessState { empty, loading, done }

然后我们可以检查滚动视图的当前状态以自定义我们的头部:

import 'package:flutter/material.dart';
import 'package:endless/endless.dart';

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Endless分页列表视图')),
        body: EndlessPaginationListView<String>(
          loadMore: (pageIndex) async => [...],
          paginationDelegate: EndlessPaginationDelegate(
            pageSize: 5,
            maxPages: 10,
          ),
          // 每个构建器都有一个对应的与状态相关的UI构建器。
          headerBuilderState: EndlessStateProperty.resolveWith((states) {
            if (states.contains(EndlessState.empty)) {
              return null;
            }

            return Container(
              color: Colors.blue,
              child: const Text('头部'),
            );
          }),
          itemBuilder: (
            context, {
            required item,
            required index,
            required totalItems,
          }) {
            return Text(item);
          },
        ),
      ),
    );
  }
}

完整的 Endless 状态属性帮助器如下:

  • EndlessStateProperty.all
  • EndlessStateProperty.loading
  • EndlessStateProperty.empty
  • EndlessStateProperty.done
  • EndlessStateProperty.never
  • EndlessStateProperty.resolveWith

某些构建器函数具有默认的状态属性行为。例如,emptyBuilder 参数默认被包装在一个 emptyStateBuilder 中,仅在滚动视图为空且不加载时才构建,如下所示:

EndlessStateProperty<Widget?> resolveEmptyBuilderToStateProperty(
  Builder<Widget>? builder,
) {
  return _resolveBuilderToStateProperty<Widget>(
    builder,
    (Builder<Widget> builder) =>
        EndlessStateProperty.resolveWith<Widget>((context, states) {
      if (states.contains(EndlessState.empty) &&
          !states.contains(EndlessState.loading)) {
        return builder(context);
      }
      return null;
    }),
  );
}

这些默认值(如 empty 状态)的目标是为无限滚动视图提供典型的默认行为。如果这不是你想要的空状态默认行为,没问题!你可以随时提供自己的 emptyBuilderState 来覆盖它。

基本示例

import 'package:flutter/material.dart';
import 'package:endless/endless.dart';

final streamController = StreamController<List<String>>();

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Endless流列表视图')),
        body: EndlessStreamListView<String>(
          // 当您滚动超过可配置的`extentAfterFactor`时,调用此函数以告诉流添加更多项。
          loadMore: () => [...],
          // 流中发出的项目被添加到滚动视图中。一旦流完成,滚动视图就知道不再尝试获取更多项目。
          stream: streamController.stream,
          itemBuilder: (
            context, {
            required item,
            required index,
            required totalItems,
          }) {
            return Text(item);
          },
        ),
      ),
    );
  }
}

更多关于Flutter无限滚动插件endless的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter无限滚动插件endless的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,实现无限滚动功能可以使用endless_list插件,不过需要注意的是,endless_list这个具体的插件名称并不是Flutter官方或广泛使用的插件。通常情况下,无限滚动是通过监听列表滚动到底部并动态加载更多数据来实现的。这里我将展示一个基本的实现方法,不使用特定的endless_list插件,而是使用Flutter内置的ListView.builder和滚动监听器来实现无限滚动功能。

以下是一个简单的代码示例,展示如何在Flutter中实现无限滚动:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Infinite Scroll Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: InfiniteScrollPage(),
    );
  }
}

class InfiniteScrollPage extends StatefulWidget {
  @override
  _InfiniteScrollPageState createState() => _InfiniteScrollPageState();
}

class _InfiniteScrollPageState extends State<InfiniteScrollPage> {
  final List<String> items = List.generate(20, (index) => "Item $index");
  bool isLoading = false;
  ScrollController _scrollController = ScrollController();

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(_scrollListener);
  }

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

  void _scrollListener() {
    if (_scrollController.position.atEdge &&
        _scrollController.position.pixels == 0) {
      // Scrolled to the top
    } else if (_scrollController.position.atEdge &&
        !_scrollController.position.outOfRange) {
      // Scrolled to the bottom
      if (!isLoading) {
        setState(() {
          isLoading = true;
        });
        _loadMoreItems();
      }
    }
  }

  Future<void> _loadMoreItems() async {
    await Future.delayed(Duration(seconds: 2)); // Simulate network delay
    setState(() {
      int start = items.length;
      int end = start + 20;
      items.addAll(List.generate(end - start, (index) => "Item ${start + index}"));
      isLoading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Infinite Scroll Demo'),
      ),
      body: ListView.builder(
        controller: _scrollController,
        itemCount: items.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(items[index]),
          );
        },
      ),
    );
  }
}

代码解释:

  1. State管理

    • 使用StatefulWidget来管理列表的状态,包括加载更多数据的逻辑。
  2. 滚动监听

    • 使用ScrollController来监听列表的滚动事件。
    • initState中注册滚动监听器_scrollListener
  3. 滚动事件处理

    • _scrollListener方法检查列表是否滚动到底部。如果是,并且当前没有正在加载的数据,则调用_loadMoreItems方法来加载更多数据。
  4. 数据加载

    • _loadMoreItems方法模拟网络延迟(使用Future.delayed),然后向列表中添加更多数据项。
  5. 列表构建

    • 使用ListView.builder来构建列表,这样可以高效地处理大量数据项。

这种方法不使用特定的endless_list插件,而是利用Flutter的内置功能实现了无限滚动功能。如果你确实需要一个特定的插件,可能需要在pub.dev上搜索并查看相关插件的文档和示例代码。

回到顶部