Flutter无限滚动插件infinite的使用

Flutter无限滚动插件infinite的使用

概述

此插件提供了列表和网格的无限视图功能。它经过优化,可以处理大量数据,特别适合渲染大量的媒体文件(如图片和视频)。

源代码

源代码可在以下地址获取: GitLab

特性

  • 可以选择 <code>InfiniteViewMode.grid</code><code>InfiniteViewMode.list</code> 模式。
  • 可以切换当前模式。
  • 切换模式时会将已获取的数据传递到新模式。
  • 不需要手动编写 <code>Image</code><code>Video</code> 小部件。
  • 高性能处理大量媒体。

使用步骤

添加依赖

pubspec.yaml 文件中添加以下依赖:

dependencies:
  infinite: ^1.0.0

然后在项目目录的控制台运行以下命令:

flutter pub get

环境要求

确保您的环境满足以下条件:

environment:
  sdk: ">=2.12.0 <3.0.0"

使用示例

基本用法

InfiniteView 是一个状态管理小部件,用于处理列表或网格的无限滚动。您可以为其指定一个 key 并调用以下方法:

reloadMode() {
  key.currentState.onReloadMode(InfiniteViewMode.list); 
}
clear() {
  key.currentState.clear(); 
}

获取数据

您需要实现 _onGetData 方法来获取数据。以下是一个示例:

Future<List<InfiniteItemViewData<MyListData>>> _onGetData(
  startIndex,
  nextItemsCount,
) async {
  if (startIndex < 0) return [];

  int endIndex = startIndex + nextItemsCount + 1;
  endIndex = endIndex < items.length ? endIndex : items.length;

  final items = await getItems();
  return _onInitItems(items);
}

必要属性

itemsCount 是必需的,因为列表需要知道何时到达最后一页。当滚动到最后一个元素时,所有项目将在最后一页之前被获取。

如果使用 <code>InfiniteViewMode.grid</code>,则需要提供 <code>InfiniteGridDelegate</code>。如果使用 <code>InfiniteViewMode.list</code>,则需要提供 <code>InfiniteListDelegate</code>

Delegate 示例

以下是 <code>InfiniteGridDelegate</code> 的示例:

delegateGrid: InfiniteGridDelegate(
  maxWidth: size.width,
  builder: (context, value, index) => Container(
    color: value.color,
    child: Text(value.text),
  ),
  itemExtent: size.width / 2,
  columnsCount: 3,
  pageSize: 20,
),
getData: _onGetData,
onRefresh: _onRefresh,

自定义构建器

您可以在 delegate 中提供 <code>builder</code><code>builderWithMedia</code>。当使用 <code>builderWithMedia</code> 时,会返回一个 <code>InfiniteViewMedia</code> 列表。在启动时提供 <code>InfiniteItemViewData</code> 时,您需要提供适当的 <code>InfiniteMediaData</code>

示例代码:

builderWithMedia: (context, value, index, media) => Container(
  color: value.color,
  child: Stack(
    children: [
      media.isNotEmpty ? media.first : Container(),
      Align(
        alignment: Alignment.bottomCenter,
        child: Text(value.text),
      ),
    ],
  ),
),

回调函数

  • <code>onRefresh</code>:用户拉动列表时触发自定义回调。
  • <code>colorRefreshIndicator</code>:设置下拉刷新指示器的颜色。
  • <code>noMoreItemsIndicatorBuilder</code>:用户滚动到底部时显示自定义视图。
  • <code>noFoundItemsIndicatorBuilder</code>:未找到任何项目时显示自定义视图。

示例代码

以下是一个完整的示例代码,展示了如何使用 <code>Infinite</code> 插件实现无限滚动:

import 'dart:math' as math;

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

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

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Infinite Demo',
      theme: ThemeData.dark(),
      themeMode: ThemeMode.dark,
      home: const MyHomePage(title: 'Infinite widget'),
    );
  }
}

/// 数据模型
class MyListData {
  MyListData({
    required this.color,
    required this.text,
  });

  final String text;
  final Color color;
}

/// 主页面
class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

/// 页面状态
class _MyHomePageState extends State<MyHomePage> {
  final _keyInfinite = GlobalKey<InfiniteViewState>();

  List<InfiniteItemViewData<MyListData>> items = [];

  @override
  void initState() {
    items = _onCreateItems();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Infinite.infiniteView<MyListData, InfiniteItemViewData<MyListData>>(
        key: _keyInfinite,
        itemsCount: items.length,
        mode: InfiniteViewMode.list,
        delegateList: InfiniteListDelegate(
          maxWidth: size.width,
          builder: (context, value, index) => Container(),
          builderWithMedia: (context, value, index, media) => Container(
            constraints: const BoxConstraints(minHeight: 100),
            color: value.color,
            child: Column(
              children: [
                media.isNotEmpty ? media.first : Container(),
                SizedBox(height: 100, child: Center(child: Text(value.text))),
              ],
            ),
          ),
          itemExtent: 100,
          pageSize: 7,
        ),
        delegateGrid: InfiniteGridDelegate(
          maxWidth: size.width,
          builderWithMedia: (context, value, index, media) => Container(
            color: value.color,
            child: Stack(
              children: [
                media.isNotEmpty ? media.first : Container(),
                Align(
                  alignment: Alignment.bottomCenter,
                  child: Text(value.text),
                ),
              ],
            ),
          ),
          itemExtent: size.width / 2,
          columnsCount: 3,
          pageSize: 20,
        ),
        getData: _onGetData,
        onRefresh: _onRefresh,
      ),
      floatingActionButton: FloatingActionButton(onPressed: _onSwitchMode),
    );
  }

  List<InfiniteItemViewData<MyListData>> _onCreateItems() {
    return List.generate(
      20000,
      (index) => InfiniteItemViewData(
        value: MyListData(
          text: 'Hi$index',
          color: _randomColor(),
        ),
        media: [
          InfiniteMediaData(
            id: index.toString(),
            url: 'https://storage.googleapis.com/cms-storage-bucket/6f183a9db312d0e1b535.png',
            isVideo: false,
          ),
        ],
      ),
    );
  }

  Future<void> _onRefresh() async {
    await Future(() => items = _onCreateItems());

    _keyInfinite.currentState?.clear();

    setState(() {});
  }

  Future<List<InfiniteItemViewData<MyListData>>> _onGetData(
    startIndex,
    nextItemsCount,
  ) async {
    if (startIndex < 0) return [];

    int endIndex = startIndex + nextItemsCount + 1;
    endIndex = endIndex < items.length ? endIndex : items.length;

    return await Future(() => items.sublist(startIndex, endIndex));
  }

  _onSwitchMode() {
    final selectedMode = _keyInfinite.currentState?.mode;

    if (selectedMode != null) {
      switch (selectedMode) {
        case InfiniteViewMode.list:
          _keyInfinite.currentState?.onReloadMode(InfiniteViewMode.grid);
          break;
        case InfiniteViewMode.grid:
          _keyInfinite.currentState?.onReloadMode(InfiniteViewMode.list);
          break;
      }
    }
  }

  Color _randomColor() {
    return Color((math.Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0);
  }
}

其他信息

此插件针对长列表和大量媒体进行了优化。特别是视频播放需要设备的内存支持。如果仍然遇到问题,请在您的 <code>Info.plist</code> 文件中添加以下配置:

<key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
        <key>NSAllowsArbitraryLoadsForMedia</key>
        <true/>
        <key>NSAllowsLocalNetworking</key>
        <true/>
    </dict>

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

1 回复

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


在 Flutter 中,实现无限滚动(Infinite Scroll)通常可以通过 ListView.builderGridView.builder 结合分页逻辑来实现。不过,如果你想使用一个现成的插件来简化这个过程,infinite_scroll_pagination 是一个非常流行的选择。

1. 安装 infinite_scroll_pagination 插件

首先,在 pubspec.yaml 文件中添加依赖:

dependencies:
  flutter:
    sdk: flutter
  infinite_scroll_pagination: ^3.2.0

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

2. 使用 PagedListView 实现无限滚动

以下是一个简单的例子,展示如何使用 PagedListView 实现无限滚动:

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

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Infinite Scroll Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: InfiniteScrollExample(),
    );
  }
}

class InfiniteScrollExample extends StatefulWidget {
  [@override](/user/override)
  _InfiniteScrollExampleState createState() => _InfiniteScrollExampleState();
}

class _InfiniteScrollExampleState extends State<InfiniteScrollExample> {
  static const _pageSize = 20;

  final PagingController<int, String> _pagingController =
      PagingController(firstPageKey: 0);

  [@override](/user/override)
  void initState() {
    super.initState();
    _pagingController.addPageRequestListener((pageKey) {
      _fetchPage(pageKey);
    });
  }

  Future<void> _fetchPage(int pageKey) async {
    try {
      final newItems = await _fetchItems(pageKey);
      final isLastPage = newItems.length < _pageSize;
      if (isLastPage) {
        _pagingController.appendLastPage(newItems);
      } else {
        final nextPageKey = pageKey + newItems.length;
        _pagingController.appendPage(newItems, nextPageKey);
      }
    } catch (error) {
      _pagingController.error = error;
    }
  }

  Future<List<String>> _fetchItems(int startIndex) async {
    // 模拟网络请求,返回数据
    await Future.delayed(Duration(seconds: 1));
    return List.generate(
        _pageSize, (index) => 'Item ${startIndex + index + 1}');
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Infinite Scroll Example'),
      ),
      body: PagedListView<int, String>(
        pagingController: _pagingController,
        builderDelegate: PagedChildBuilderDelegate<String>(
          itemBuilder: (context, item, index) => ListTile(
            title: Text(item),
          ),
        ),
      ),
    );
  }

  [@override](/user/override)
  void dispose() {
    _pagingController.dispose();
    super.dispose();
  }
}
回到顶部