Flutter无限滚动插件scroll_infinity的使用

发布于 1周前 作者 htzhanglong 来自 Flutter

Flutter无限滚动插件scroll_infinity的使用

安装

运行以下命令来安装scroll_infinity插件:

flutter pub add scroll_infinity

使用示例

以下是使用该插件创建带有无限滚动功能的列表的一些示例。

垂直滚动示例
import 'package:flutter/material.dart';
import 'package:scroll_infinity/scroll_infinity.dart';

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

  [@override](/user/override)
  State<Example> createState() => _ExampleState();
}

class _ExampleState extends State<Example> {
  static const _maxItems = 20;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: ScrollInfinity(
        maxItems: _maxItems, // 每次加载的最大项目数
        loadData: (pageKey) async { // 加载数据的函数
          await Future.delayed(const Duration(seconds: 2)); // 模拟网络延迟

          return List.generate(_maxItems, (index) { // 生成数据
            return _maxItems * pageKey + index + 1;
          });
        },
        itemBuilder: (value, index) { // 构建每个项目的函数
          return ListTile(
            title: Text('Item $value'), // 显示项目值
            subtitle: const Text('Subtitle'),
            trailing: const Icon(Icons.keyboard_arrow_right_rounded),
          );
        },
      ),
    );
  }
}
水平滚动示例
import 'package:flutter/material.dart';
import 'package:scroll_infinity/scroll_infinity.dart';

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

  [@override](/user/override)
  State<Example> createState() => _ExampleState();
}

class _ExampleState extends State<Example> {
  static const _maxItems = 6;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: SizedBox(
          height: 64.0,
          child: ScrollInfinity(
            scrollDirection: Axis.horizontal, // 设置为水平滚动
            maxItems: _maxItems,
            loadData: (pageKey) async {
              await Future.delayed(const Duration(seconds: 2));

              return List.generate(_maxItems, (index) {
                return _maxItems * pageKey + index + 1;
              });
            },
            itemBuilder: (value, index) {
              return Center(
                child: SizedBox(
                  width: MediaQuery.sizeOf(context).width * 0.5,
                  child: ListTile(
                    onTap: () {},
                    title: Text('Item $value'),
                    subtitle: const Text('Subtitle'),
                    trailing: const Icon(Icons.keyboard_arrow_right_rounded),
                  ),
                ),
              );
            },
          ),
        ),
      ),
    );
  }
}
带间隔的示例
import 'dart:math';

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

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

  [@override](/user/override)
  State<Example> createState() => _ExampleState();
}

class _ExampleState extends State<Example> {
  static const _maxItems = 20;
  final _random = Random();

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: ScrollInfinity<int?>(
        maxItems: _maxItems,
        interval: 2, // 设置间隔
        loadData: (pageKey) async {
          await Future.delayed(const Duration(seconds: 2));

          if (_random.nextInt(4) == 0) {
            return null; // 随机返回null以模拟间隔
          }

          return List.generate(_maxItems, (index) {
            return _maxItems * pageKey + index + 1;
          });
        },
        itemBuilder: (value, index) {
          if (value == null) {
            return const SizedBox(
              height: 60.0,
              child: Placeholder(), // 占位符用于间隔
            );
          }

          return ListTile(
            title: Text('Item $value'),
            subtitle: const Text('Subtitle'),
            trailing: const Icon(Icons.keyboard_arrow_right_rounded),
          );
        },
      ),
    );
  }
}
带加载器的示例
import 'dart:math';

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

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

  [@override](/user/override)
  State<Example> createState() => _ExampleState();
}

class _ExampleState extends State<Example> {
  final _notifier = ScrollInfinityInitialItemsNotifier<int?>(null);

  static const _maxItems = 20;
  final _random = Random();

  Future<void> _initLoader() async {
    _notifier.value = await _loadData(0);
  }

  Future<List<int>?> _loadData(int pageIndex) async {
    await Future.delayed(const Duration(seconds: 2));

    if (_random.nextInt(4) == 0) {
      return null;
    }

    return List.generate(_maxItems, (index) {
      return _maxItems * pageIndex + index + 1;
    });
  }

  [@override](/user/override)
  void initState() {
    _initLoader();
    super.initState();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: ScrollInfinityLoader(
        notifier: _notifier,
        scrollInfinityBuilder: (items) {
          return ScrollInfinity<int?>(
            maxItems: _maxItems,
            initialPageIndex: 1,
            initialItems: items,
            interval: 2,
            loadData: _loadData,
            itemBuilder: (value, index) {
              if (value == null) {
                return const SizedBox(
                  height: 60.0,
                  child: Placeholder(),
                );
              }

              return ListTile(
                title: Text('Item $value'),
                subtitle: const Text('Subtitle'),
                trailing: const Icon(Icons.keyboard_arrow_right_rounded),
              );
            },
          );
        },
      ),
    );
  }
}

属性

  • scrollDirection: 定义列表的滚动方向。可以是 Axis.verticalAxis.horizontal

    scrollDirection: Axis.vertical,
    
  • scrollbars: 如果为 true,则显示滚动条。默认为 false

    scrollbars: true,
    
  • padding: 指定列表的内部填充。

    padding: EdgeInsets.all(8.0),
    
  • header: 列表头。

    header: HeaderWidget(),
    
  • initialPageIndex: 初始页索引。默认为 0

    initialPageIndex: 1,
    
  • enableRetryOnError: 决定是否在错误后重试加载数据。默认为 true

    enableRetryOnError: false,
    
  • empty: 当列表为空时显示自定义内容的小部件。

    empty: Text('No items available.'),
    
  • reset: 在重置期间显示自定义内容的小部件。

    reset: Text('Reseting...'),
    
  • error: 发生错误时显示自定义内容的小部件。

    error: Text('Error occurred.'),
    
  • initialItems: 指定要在列表中显示的初始项。

    initialItems: <Widget>[
      // items
    ],
    
  • interval: 指定传递 null 值的范围。

    interval: 20,
    
  • loading: 允许传递自定义加载组件。

    loading: LoadingWidget(),
    
  • loadingStyle: 定义 CircularProgressIndicator 的样式。

    loadingStyle: CircularProgressIndicator(
      color: Colors.blue,
      strokeWidth: 8.0,
    ),
    
  • tryAgainButtonBuilder: 允许传递自定义重试按钮组件,由回调触发。

    tryAgainButtonBuilder: (action) {
      return ElevatedButton(
        onPressed: action,
        child: Text('Retry'),
      );
    },
    
  • maxItems: 指定每次请求的最大项数。这将用于确定列表何时到达末尾。

    maxItems: 20,
    
  • loadData: 负责加载数据的函数。应返回一个项目列表。

    loadData: (pageIndex) async {
      // 加载数据的逻辑
    },
    
  • separatorBuilder: 构建列表项之间的分隔组件。

    separatorBuilder: (context, index) {
      return Divider(
        color: Colors.grey,
        height: 1.0,
      );
    },
    
  • itemBuilder: 构建列表中的项。此函数应返回表示列表中每个项的小部件。

    itemBuilder: (value, index) {
      final item = items[index];
    
      return ListTile(
        title: Text(item.title),
        subtitle: Text(item.subtitle),
      );
    },
    

完整示例代码

以下是一个完整的示例代码,包含上述所有功能:

import 'dart:math';

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

void main() {
  runApp(const MaterialApp(
    debugShowCheckedModeBanner: false,
    home: InfiniteScrollExample(),
  ));
}

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

  [@override](/user/override)
  State<InfiniteScrollExample> createState() => _InfiniteScrollExampleState();
}

class _InfiniteScrollExampleState extends State<InfiniteScrollExample> {
  static const _maxItems = 20;
  final _random = Random();

  Future<List<Color>?> _loadData(int pageIndex) async {
    await Future.delayed(const Duration(seconds: 2));

    if (_random.nextInt(4) == 0) {
      return null;
    }

    final isListEnd = _random.nextInt(5) == 0;
    final max = _maxItems - 1;

    return _generateColors(
      isListEnd ? (max == 0 ? 0 : _random.nextInt(max)) : _maxItems,
    );
  }

  List<Color> _generateColors(int amount) {
    return List.generate(amount, (index) {
      return Color.fromARGB(
        255,
        _random.nextInt(255),
        _random.nextInt(255),
        _random.nextInt(255),
      );
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: IconButton(
          onPressed: () {
            Navigator.pop(context);
          },
          icon: const Icon(Icons.arrow_back_ios_new_rounded),
        ),
      ),
      body: Column(
        children: <Widget>[
          ElevatedButton(
            onPressed: () {
              setState(() {}); // 重置列表
            },
            child: const Text('Reset'),
          ),
          const SizedBox(height: 20.0),
          const Divider(height: 0.0),
          Expanded(
            child: ScrollInfinity<Color>(
              maxItems: _maxItems,
              loadData: _loadData,
              itemBuilder: (value, index) {
                return Container(
                  height: 100.0,
                  color: value,
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

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

1 回复

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


当然,下面是一个关于如何在Flutter项目中使用scroll_infinity插件来实现无限滚动的代码示例。scroll_infinity插件可以帮助你轻松实现列表的无限滚动加载功能。

首先,你需要在你的pubspec.yaml文件中添加scroll_infinity依赖:

dependencies:
  flutter:
    sdk: flutter
  scroll_infinity: ^x.y.z  # 请替换为最新版本号

然后运行flutter pub get来安装依赖。

接下来,在你的Flutter项目中,你可以按照以下步骤使用ScrollInfinity来实现无限滚动:

  1. 导入必要的包
import 'package:flutter/material.dart';
import 'package:scroll_infinity/scroll_infinity.dart';
  1. 创建数据加载逻辑

这里我们假设你有一个API可以分页获取数据。我们定义一个简单的函数来模拟数据加载。

Future<List<String>> fetchData(int page) async {
  // 模拟网络请求延迟
  await Future.delayed(Duration(seconds: 1));
  // 返回模拟数据
  return List.generate(20, (index) => "Item ${(page - 1) * 20 + index + 1}");
}
  1. 使用ScrollInfinity构建UI

在你的主页面或者需要实现无限滚动的页面中使用ScrollInfinity

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: InfiniteScrollPage(),
    );
  }
}

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

class _InfiniteScrollPageState extends State<InfiniteScrollPage> {
  final ScrollController _scrollController = ScrollController();
  final PageController _pageController = PageController();
  List<String> _items = [];
  int _currentPage = 1;

  @override
  void initState() {
    super.initState();
    // 初始化时加载第一页数据
    loadMoreData();
    // 监听滚动事件
    _scrollController.addListener(() {
      if (_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
        // 滚动到底部时加载更多数据
        loadMoreData();
      }
    });
  }

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

  Future<void> loadMoreData() async {
    final List<String> newData = await fetchData(_currentPage);
    setState(() {
      _items.addAll(newData);
      _currentPage++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Infinite Scroll Example'),
      ),
      body: ScrollInfinity(
        controller: _scrollController,
        itemCount: _items.length, // 当前加载的item总数
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(_items[index]),
          );
        },
        onLoadMore: () async {
          // 当滚动到底部时触发此函数,可以在这里添加加载更多数据的逻辑
          // 这里我们已经在initState中通过滚动监听器实现了加载更多数据,所以这里不需要再写加载逻辑
          // 但为了符合ScrollInfinity的使用方式,我们还是调用一下loadMoreData函数(尽管它是空的)
          await Future.delayed(Duration.zero); // 模拟异步操作
        },
        // 可选参数,设置加载更多时的占位符
        loadingWidget: Center(child: CircularProgressIndicator()),
        // 可选参数,设置没有更多数据时显示的占位符
        noMoreWidget: Center(child: Text('No more data')),
      ),
    );
  }
}

在这个例子中,我们使用了ScrollInfinity来实现无限滚动。我们通过监听ScrollController的滚动事件,在滚动到底部时调用loadMoreData函数来加载更多数据。ScrollInfinity提供了onLoadMore回调,但在这个例子中,我们已经在滚动监听器中处理了数据加载,所以onLoadMore中只是简单地返回了一个异步操作(Future.delayed(Duration.zero))。

请注意,实际项目中,你可能需要根据你的API和数据结构来调整数据加载和UI渲染的逻辑。

回到顶部