Flutter可滚动定位列表插件scrollable_positioned_list_nic的使用

Flutter可滚动定位列表插件scrollable_positioned_list_nic的使用

ScrollablePositionedList 是一个 Flutter 列表控件,它允许你将列表滚动到特定的项目,并且可以确定哪些项目当前在屏幕上可见。

使用方法

ScrollablePositionedList 的使用方式类似于 ListView 的构建器版本,但具有滚动或跳转到特定项目的功能。

示例

首先,我们需要创建一些控制器来管理滚动和项目位置:

final ItemScrollController itemScrollController = ItemScrollController();
final ScrollOffsetController scrollOffsetController = ScrollOffsetController();
final ItemPositionsListener itemPositionsListener = ItemPositionsListener.create();
final ScrollOffsetListener scrollOffsetListener = ScrollOffsetListener.create();

然后,我们可以创建一个 ScrollablePositionedList

ScrollablePositionedList.builder(
  itemCount: numberOfItems,
  itemBuilder: (context, index) => item(index, orientation),
  itemScrollController: itemScrollController,
  itemPositionsListener: itemPositionsListener,
  scrollOffsetController: scrollOffsetController,
  reverse: reversed,
  scrollDirection: orientation == Orientation.portrait ? Axis.vertical : Axis.horizontal,
);

我们可以通过以下方式滚动到特定的项目:

itemScrollController.scrollTo(
  index: 150,
  duration: Duration(seconds: 2),
  curve: Curves.easeInOutCubic,
);

或者直接跳转到特定的项目:

itemScrollController.jumpTo(index: 150);

我们还可以监听哪些项目当前在屏幕上可见:

itemPositionsListener.itemPositions.addListener(() {
  // 处理逻辑
});

完整示例代码

import 'dart:math';
import 'package:flutter/material.dart';
import 'package:scrollable_positioned_list_nic/scrollable_positioned_list_nic.dart';

const numberOfItems = 5001;
const minItemHeight = 20.0;
const maxItemHeight = 150.0;
const scrollDuration = Duration(seconds: 2);

const randomMax = 1 << 32;

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

class ScrollablePositionedListExample extends StatelessWidget {
  const ScrollablePositionedListExample({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ScrollablePositionedList Example',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const ScrollablePositionedListPage(),
    );
  }
}

class ScrollablePositionedListPage extends StatefulWidget {
  const ScrollablePositionedListPage({Key? key}) : super(key: key);

  [@override](/user/override)
  _ScrollablePositionedListPageState createState() => _ScrollablePositionedListPageState();
}

class _ScrollablePositionedListPageState extends State<ScrollablePositionedListPage> {
  final ItemScrollController itemScrollController = ItemScrollController();
  final ScrollOffsetController scrollOffsetController = ScrollOffsetController();
  final ItemPositionsListener itemPositionsListener = ItemPositionsListener.create();
  late List<double> itemHeights;
  late List<Color> itemColors;
  bool reversed = false;

  double alignment = 0;

  [@override](/user/override)
  void initState() {
    super.initState();
    final heightGenerator = Random(328902348);
    final colorGenerator = Random(42490823);
    itemHeights = List<double>.generate(numberOfItems, (_) => heightGenerator.nextDouble() * (maxItemHeight - minItemHeight) + minItemHeight);
    itemColors = List<Color>.generate(numberOfItems, (_) => Color(colorGenerator.nextInt(randomMax)).withOpacity(1));
  }

  [@override](/user/override)
  Widget build(BuildContext context) => Material(
        child: OrientationBuilder(
          builder: (context, orientation) => Column(
            children: [
              Expanded(
                child: list(orientation),
              ),
              positionsView,
              Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Column(
                    children: [
                      scrollControlButtons,
                      scrollOffsetControlButtons,
                      const SizedBox(height: 10),
                      jumpControlButtons,
                      alignmentControl,
                    ],
                  ),
                ],
              )
            ],
          ),
        ),
      );

  Widget get alignmentControl => Row(
        mainAxisSize: MainAxisSize.max,
        children: [
          const Text('Alignment: '),
          SizedBox(
            width: 200,
            child: SliderTheme(
              data: SliderThemeData(showValueIndicator: ShowValueIndicator.always),
              child: Slider(
                value: alignment,
                label: alignment.toStringAsFixed(2),
                onChanged: (double value) => setState(() => alignment = value),
              ),
            ),
          ),
        ],
      );

  Widget list(Orientation orientation) => ScrollablePositionedList.builder(
        itemCount: numberOfItems,
        itemBuilder: (context, index) => item(index, orientation),
        itemScrollController: itemScrollController,
        itemPositionsListener: itemPositionsListener,
        scrollOffsetController: scrollOffsetController,
        reverse: reversed,
        scrollDirection: orientation == Orientation.portrait ? Axis.vertical : Axis.horizontal,
      );

  Widget get positionsView => ValueListenableBuilder<Iterable<ItemPosition>>(
        valueListenable: itemPositionsListener.itemPositions,
        builder: (context, positions, child) {
          int? min;
          int? max;
          if (positions.isNotEmpty) {
            min = positions
                .where((ItemPosition position) => position.itemTrailingEdge > 0)
                .reduce((ItemPosition min, ItemPosition position) => position.itemTrailingEdge < min.itemTrailingEdge ? position : min)
                .index;
            max = positions
                .where((ItemPosition position) => position.itemLeadingEdge < 1)
                .reduce((ItemPosition max, ItemPosition position) => position.itemLeadingEdge > max.itemLeadingEdge ? position : max)
                .index;
          }
          return Row(
            children: [
              Expanded(child: Text('First Item: ${min ?? ''}')),
              Expanded(child: Text('Last Item: ${max ?? ''}')),
              const Text('Reversed: '),
              Checkbox(
                  value: reversed,
                  onChanged: (bool? value) => setState(() {
                        reversed = value!;
                      })),
            ],
          );
        },
      );

  Widget get scrollControlButtons => Row(
        children: [
          const Text('scroll to'),
          scrollItemButton(0),
          scrollItemButton(5),
          scrollItemButton(10),
          scrollItemButton(100),
          scrollItemButton(1000),
          scrollItemButton(5000),
        ],
      );

  Widget get scrollOffsetControlButtons => Row(
        children: [
          const Text('scroll by'),
          scrollOffsetButton(-1000),
          scrollOffsetButton(-100),
          scrollOffsetButton(-10),
          scrollOffsetButton(10),
          scrollOffsetButton(100),
          scrollOffsetButton(1000),
        ],
      );

  Widget get jumpControlButtons => Row(
        children: [
          const Text('jump to'),
          jumpButton(0),
          jumpButton(5),
          jumpButton(10),
          jumpButton(100),
          jumpButton(1000),
          jumpButton(5000),
        ],
      );

  ButtonStyle _scrollButtonStyle({required double horizonalPadding}) => ButtonStyle(
        padding: MaterialStateProperty.all(
          EdgeInsets.symmetric(horizontal: horizonalPadding, vertical: 0),
        ),
        minimumSize: MaterialStateProperty.all(Size.zero),
        tapTargetSize: MaterialTapTargetSize.shrinkWrap,
      );

  Widget scrollItemButton(int value) => TextButton(
        key: ValueKey<String>('Scroll$value'),
        onPressed: () => scrollTo(value),
        child: Text('$value'),
        style: _scrollButtonStyle(horizonalPadding: 20),
      );

  Widget scrollOffsetButton(int value) => TextButton(
        key: ValueKey<String>('Scroll$value'),
        onPressed: () => scrollBy(value.toDouble()),
        child: Text('$value'),
        style: _scrollButtonStyle(horizonalPadding: 10),
      );

  Widget scrollPixelButton(int value) => TextButton(
        key: ValueKey<String>('Scroll$value'),
        onPressed: () => scrollTo(value),
        child: Text('$value'),
        style: _scrollButtonStyle(horizonalPadding: 20),
      );

  Widget jumpButton(int value) => TextButton(
        key: ValueKey<String>('Jump$value'),
        onPressed: () => jumpTo(value),
        child: Text('$value'),
        style: _scrollButtonStyle(horizonalPadding: 20),
      );

  void scrollTo(int index) => itemScrollController.scrollTo(index: index, duration: scrollDuration, curve: Curves.easeInOutCubic, alignment: alignment);

  void scrollBy(double offset) => scrollOffsetController.animateScroll(offset: offset, duration: scrollDuration, curve: Curves.easeInOutCubic);

  void jumpTo(int index) => itemScrollController.jumpTo(index: index, alignment: alignment);

  Widget item(int i, Orientation orientation) {
    return SizedBox(
      height: orientation == Orientation.portrait ? itemHeights[i] : null,
      width: orientation == Orientation.landscape ? itemHeights[i] : null,
      child: Container(
        color: itemColors[i],
        child: Center(
          child: Text('Item $i'),
        ),
      ),
    );
  }
}

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

1 回复

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


scrollable_positioned_list_nic 是 Flutter 中的一个插件,用于实现可滚动且支持精确定位的列表。它是对 Flutter 自带的 ListView 的增强版本,允许你通过索引来精确地滚动到列表中的特定位置,甚至可以在列表中实现诸如“跳转到第 N 项”或“滚动到指定位置并保持对齐”等高级功能。

安装

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

dependencies:
  flutter:
    sdk: flutter
  scrollable_positioned_list_nic: ^0.1.0

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

基本用法

以下是一个简单的示例,展示如何使用 scrollable_positioned_list_nic

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('ScrollablePositionedList Example')),
        body: ScrollablePositionedListExample(),
      ),
    );
  }
}

class ScrollablePositionedListExample extends StatefulWidget {
  @override
  _ScrollablePositionedListExampleState createState() =>
      _ScrollablePositionedListExampleState();
}

class _ScrollablePositionedListExampleState
    extends State<ScrollablePositionedListExample> {
  final ItemScrollController itemScrollController = ItemScrollController();
  final ItemPositionsListener itemPositionsListener =
      ItemPositionsListener.create();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ElevatedButton(
          onPressed: () {
            itemScrollController.scrollTo(
              index: 50,
              duration: Duration(seconds: 1),
              curve: Curves.easeInOut,
            );
          },
          child: Text('Scroll to Item 50'),
        ),
        Expanded(
          child: ScrollablePositionedList.builder(
            itemCount: 100,
            itemScrollController: itemScrollController,
            itemPositionsListener: itemPositionsListener,
            itemBuilder: (context, index) {
              return ListTile(
                title: Text('Item $index'),
              );
            },
          ),
        ),
      ],
    );
  }
}

主要组件和功能

  1. ScrollablePositionedList.builder:

    • itemCount: 列表中的项目总数。
    • itemBuilder: 构建列表项的构建器函数。
    • itemScrollController: 用于控制列表滚动的控制器。
    • itemPositionsListener: 用于监听列表项位置的监听器。
  2. ItemScrollController:

    • scrollTo: 滚动到指定索引的项目。
    • jumpTo: 立即跳转到指定索引的项目。
  3. ItemPositionsListener:

    • 用于监听当前可见的列表项的位置。

高级用法

  • 初始滚动位置: 你可以通过 initialScrollIndex 参数来设置列表的初始滚动位置。
  • 对齐方式: 在 scrollTo 方法中,你可以通过 alignment 参数来指定滚动后的对齐方式,例如 0.0 表示顶部对齐,0.5 表示居中,1.0 表示底部对齐。
itemScrollController.scrollTo(
  index: 50,
  duration: Duration(seconds: 1),
  curve: Curves.easeInOut,
  alignment: 0.5, // 居中
);
回到顶部