Flutter:获取 ListView/GridView 中的当前滚动偏移量

本文将向您展示如何在 ListView 或 Flutter 中的其他可滚动小部件(如 GridView、SingleSchildScrollView、CustomScrollView 等)中获取当前滚动偏移量(表示到顶部的距离,以像素为单位)。我们将发现机制然后研究在实践中应用该机制的完整示例。

重点是什么?

要在 Flutter 中计算可滚动小部件中的滚动偏移量,首先要做的是初始化一个ScrollController实例:

final ScrollController _controller = ScrollController();

然后将此控制器连接到可滚动小部件:

ListView.builder(
        // attact the controller
        controller: _controller,
        /* ... */
),

现在您可以添加一个侦听器并获得如下结果:

@override
void initState() {
    _controller.addListener(() {
        // print out the scroll offset
        print(_controller.offset);
    });
    super.initState();
}

如果列表视图滚动到其末尾,则滚动偏移量将等于或大于以下值:

controller.position.maxScrollExtent

为了更好地理解,请参阅下面的完整示例。

例子

应用预览

我们要制作的演示应用程序包含一个应用程序栏、一个列表视图(呈现很多项目)和一个底栏。滚动偏移量将显示在底部栏中。底栏的背景颜色取决于滚动偏移量:

  • 如果滚动偏移量等于或小于零,则背景为绿色(滚动偏移量可能为负数)
  • 如果滚动到列表视图的末尾,背景颜色将为红色。此外,将出现一个带有“您已到达列表视图末尾”消息的快餐栏
  • 在其余情况下,底栏的背景为蓝色

以下是它的工作原理:

Flutter:获取 ListView/GridView 中的当前滚动偏移量

此示例旨在演示如何检索可滚动小部件中的滚动位置并检测它是否滚动到底部/顶部。

编码

main.dart中的完整源代码(在注释中有解释):

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

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // Remove the debug banner
      debugShowCheckedModeBanner: false,
      title: 'KindaCode.com',
      theme: ThemeData(
        primarySwatch: Colors.amber,
      ),
      home: const HomeScreen(),
    );
  }
}

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

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  // Generate dummy data to fill the list view
  final List _items = List.generate(50, (index) => 'Item $index');

  // The controller that is assigned to the list view
  final ScrollController _controller = ScrollController();

  // The scroll offset
  double _scrollOffset = 0;

  // The maximum scroll offset
  // In other words, this means the user has reached the bottom of the list view
  double? _maxOffset;

  @override
  void initState() {
    _controller.addListener(() {
      _maxOffset = _controller.position.maxScrollExtent;
      setState(() {
        _scrollOffset = _controller.offset;
        if (_maxOffset != null && _scrollOffset >= _maxOffset!) {
          ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
              content: Text('You have reached the end of the list view')));
        } else {
          ScaffoldMessenger.of(context).removeCurrentSnackBar();
        }
      });
    });

    super.initState();
  }

  // Discards any resources used by the scroll controller object
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('KindaCode.com')),
      // implement the list view
      body: ListView.builder(
        // attact the controller
        controller: _controller,
        itemBuilder: ((context, index) => Card(
              key: ValueKey(_items[index]),
              margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
              elevation: 6,
              color: Colors.amber.shade100,
              child: ListTile(
                title: Text(_items[index]),
              ),
            )),
        itemCount: _items.length,
      ),
      // display the scroll offset
      bottomNavigationBar: BottomAppBar(
        elevation: 6,
        // set the background color of the bottom bar based on the the current offset position
        // if at the top: green
        // if at the bottom: red
        // otherwise: blue
        color: _scrollOffset <= 0
            ? Colors.green
            : _maxOffset != null && _scrollOffset >= _maxOffset!
                ? Colors.red
                : Colors.blue,
        child: Padding(
          padding: const EdgeInsets.only(top: 20, left: 20),
          child: Text(
            "Offset: ${_scrollOffset.toString()}",
            style: const TextStyle(
                fontSize: 21, color: Colors.white, fontWeight: FontWeight.bold),
          ),
        ),
      ),
    );
  }
}

结论

您已经学习了如何在 Flutter 的可滚动小部件中找到滚动偏移位置。