使用 ScrollNotification 滚动时获取 SliverList 子项的索引

Tai*_*aio 7 flutter

我有这个应用程序,它有一个 sliverlist 和一个 sliverAppbar。我需要获取 sliverlist 中每个项目的当前滚动位置onscroll,并确定它是否越过 sliverAppbar 并使用该项目的标题更新 sliverappbar。假设从 Item1 开始,一旦它穿过 SliverAppBar,这意味着我正在查看 Item1 内容,当 Item2 穿过 SliverAppBar 时,用标题更新 SliverAppBarItem2意味着用户正在查看Item2内容

我正在尝试使用 a 来实现这一点,NotificationListener<ScrollEndNotification>但我被困在第二个NotificationListener上,它应该在这一行向父级顶部发出通知,ScrollEndNotification(metrics: , context:context).dispatch(context);它会抛出一个错误,我应该提供一个metrics我不知道要提供什么的参数。

            SliverList(
              delegate: SliverChildBuilderDelegate(
                (BuildContext context, int index) {
                  ScrollEndNotification(metrics: , context:context).dispatch(context);
                  return  AutoScrollTag(
                        key: ValueKey(index),
                        controller: controller,
                        index: index,
                        child: Padding(
                     padding: const EdgeInsets.only(
                      top: 30.0, left: 20.0, right: 20.0),
                      child: Container(
                       color: Colors.red,
                     //height: 120.0
                     //height: A varying height

                        ),),},); ),
Run Code Online (Sandbox Code Playgroud)

完整的代码是

           Widget build(BuildContext context) {
           return Scaffold(
           backgroundColor: Colors.grey[100],
           body: NotificationListener<ScrollEndNotification>(
                    onNotification: (notification) {
                      if (notification is ScrollEndNotification) {
                     ///Here I need to know what widget index bubbled the notification, its position 
                     ///on the screen and its index 
                     //in the list, in order to do further implementation like update the 
                      //SliverAppBar
                      print('$notification');          
                      return true;
                    },
              child: CustomScrollView(
                 controller: controller
             slivers: <Widget>[
              SliverAppBar(
            title: Text(title),
             ), 
         SliverList(
              delegate: SliverChildBuilderDelegate(
                (BuildContext context, int index) {
                  ScrollEndNotification(metrics: , context:context).dispatch(context);
                  return  AutoScrollTag(
                        key: ValueKey(index),
                        controller: controller,
                        index: index,
                        child: Padding(
                     padding: const EdgeInsets.only(
                      top: 30.0, left: 20.0, right: 20.0),
                      child: Container(
                       color: Colors.red,
                     //height: 120.0
                     //height: A varying height
                        ),),},); ),
Run Code Online (Sandbox Code Playgroud)

另外,如果您对如何实现这一目标有更好的实现,请帮助我。简而言之,我需要跟踪某个项目何时从屏幕上滚出,并index在 Sliverlist 中了解它的情况。请记住,它item有一个可变大小的容器,可以根据其中的子对象数量进行扩展。这是电子商务应用程序中常见的用户体验模式。例如,当用户向下滚动时查看菜单,并在标题穿过屏幕时更新用户正在查看的菜单。

提供要点链接,以便您了解完整的实现

KuK*_*uKu 5

我为不同身高的孩子实现了你想要的。
要制作不同的孩子身高,请随机给出身高。

  1. 获取每个索引儿童的身高并保存距顶部的距离。
  2. 添加滚动侦听器以获取当前滚动位置
  3. 获取带有第 n 个子节点的距离和当前位置的隐藏子节点索引
  4. 仅使用隐藏的子索引更改标题

在此输入图像描述

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:scroll_to_index/scroll_to_index.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String title = 'Viewing item 0';
  Random random = Random();

  Map<int, double> itemHeight = {};
  int currentHideIndex = 0;

  final _mainScrollController = ScrollController();
  @override
  void initState() {
    super.initState();
    _mainScrollController.addListener(_onMainScroll);
  }

  void _onMainScroll() {
    int justHiddenIndex =
        findIndexJustHidden(_mainScrollController.position.pixels);
    print('justHiddenIndex: $justHiddenIndex');
    if (currentHideIndex != justHiddenIndex) {
      setState(() {
        title = 'Viewing item ${justHiddenIndex + 1}';
      });
      currentHideIndex = justHiddenIndex;
    }
  }

  int findIndexJustHidden(currentPosition) {
    int index = -1;
    for (var item in itemHeight.entries) {
      if (currentPosition > item.value) {
        index = item.key;
      } else {
        if (index != 0) {
          return index;
        }
      }
    }
    return index;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey[100],
      body: CustomScrollView(
        controller: _mainScrollController,
        slivers: <Widget>[
          SliverAppBar(
            title: Text(title),
            pinned: true,
          ),
          SliverList(
            delegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                double randomHeight;
                if (!itemHeight.containsKey(index)) {
                  randomHeight = (random.nextInt(100) + 40) * 1.0;
                  print('index: $index, randomHeight: $randomHeight');
                  double beforeSumHeight =
                      index == 0 ? 0 : itemHeight[index - 1];
                  print({
                    'index': index,
                    'beforeSumHeight': beforeSumHeight,
                    'height': randomHeight
                  });
                  itemHeight[index] = beforeSumHeight + randomHeight;
                } else {
                  randomHeight = index == 0
                      ? itemHeight[index]
                      : itemHeight[index] - itemHeight[index - 1];
                }

                return AutoScrollTag(
                  key: ValueKey(index),
                  controller: AutoScrollController(),
                  index: index,
                  child: Container(
                    height: randomHeight,
                    decoration: BoxDecoration(
                        color: Colors.red.withOpacity(0.5),
                        border: Border(
                          bottom: BorderSide(
                            color: Color(0XFF000000).withOpacity(0.08),
                            width: 1.0,
                            style: BorderStyle.solid,
                          ),
                        )),
                    padding: EdgeInsets.only(top: 10, left: 20.0, right: 20.0),
                    child: Text('$index'),
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildBody() {
    return Container();
  }
}

class WidgetSize extends StatefulWidget {
  final Widget child;
  final Function onChange;

  const WidgetSize({
    Key key,
    @required this.onChange,
    @required this.child,
  }) : super(key: key);

  @override
  _WidgetSizeState createState() => _WidgetSizeState();
}

class _WidgetSizeState extends State<WidgetSize> {
  @override
  Widget build(BuildContext context) {
    SchedulerBinding.instance.addPostFrameCallback(postFrameCallback);
    return Container(
      key: widgetKey,
      child: widget.child,
    );
  }

  var widgetKey = GlobalKey();
  var oldSize;

  void postFrameCallback(_) {
    var context = widgetKey.currentContext;
    if (context == null) return;

    var newSize = context.size;
    if (oldSize == newSize) return;

    oldSize = newSize;
    widget.onChange(newSize);
  }
}

Run Code Online (Sandbox Code Playgroud)