Flutter在滚动ListView时展示和隐藏Container

6

如何在向下滚动ListView时以动画的方式显示Container,并在向上滚动时隐藏它。

我附加的视频并不是我想要的精确实现,但它只是为了给你一个想法。

enter image description here

示例视频

https://imgur.com/a/auEzJQk


编辑:

每次我向下滚动时,需要显示Container,每次我向上滚动时,希望隐藏它。它不应该依赖于ListView的索引。


https://www.youtube.com/watch?v=ORiTTaVY6mM - Doc
@pskink 显示和隐藏ListView顶部的容器 - DolDurma
向下滚动会隐藏容器,向上滚动会显示容器。 - DolDurma
@pskink不好意思,我的想法比你的链接更简单。https://imgur.com/a/auEzJQk - DolDurma
我现在不在工作场所,等我回去后,我会发布答案。你不需要使用SliverPersistentHeader,我认为我的朋友(pskink)误解了你的问题,我只是编辑了一下以使其更清晰。 - CopsOnRoad
显示剩余6条评论
3个回答

15

不确定我是否正确理解了您的问题,这是您想要实现的吗?

截图:

在此输入图片描述


代码:

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  // Height of your Container
  static final _containerHeight = 100.0;

  // You don't need to change any of these variables
  var _fromTop = -_containerHeight;
  var _controller = ScrollController();
  var _allowReverse = true, _allowForward = true;
  var _prevOffset = 0.0;
  var _prevForwardOffset = -_containerHeight;
  var _prevReverseOffset = 0.0;

  @override
  void initState() {
    super.initState();
    _controller.addListener(_listener);
  }

  // entire logic is inside this listener for ListView
  void _listener() {
    double offset = _controller.offset;
    var direction = _controller.position.userScrollDirection;

    if (direction == ScrollDirection.reverse) {
      _allowForward = true;
      if (_allowReverse) {
        _allowReverse = false;
        _prevOffset = offset;
        _prevForwardOffset = _fromTop;
      }

      var difference = offset - _prevOffset;
      _fromTop = _prevForwardOffset + difference;
      if (_fromTop > 0) _fromTop = 0;
    } else if (direction == ScrollDirection.forward) {
      _allowReverse = true;
      if (_allowForward) {
        _allowForward = false;
        _prevOffset = offset;
        _prevReverseOffset = _fromTop;
      }

      var difference = offset - _prevOffset;
      _fromTop = _prevReverseOffset + difference;
      if (_fromTop < -_containerHeight) _fromTop = -_containerHeight;
    }
    setState(() {}); // for simplicity I'm calling setState here, you can put bool values to only call setState when there is a genuine change in _fromTop
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("ListView")),
      body: Stack(
        children: <Widget>[
          _yourListView(),
          Positioned(
            top: _fromTop,
            left: 0,
            right: 0,
            child: _yourContainer(),
          )
        ],
      ),
    );
  }

  Widget _yourListView() {
    return ListView.builder(
      itemCount: 100,
      controller: _controller,
      itemBuilder: (_, index) => ListTile(title: Text("Item $index")),
    );
  }

  Widget _yourContainer() {
    return Opacity(
      opacity: 1 - (-_fromTop / _containerHeight),
      child: Container(
        height: _containerHeight,
        color: Colors.red,
        alignment: Alignment.center,
        child: Text("Your Container", style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold, color: Colors.white)),
      ),
    );
  }
}

2
像往常一样过于复杂化。 - user10539074
@Eugene 哈哈,你的解决方案对于旧帖子很好,但是 OP 编辑了问题,说他想追踪每次滚动发生的时间,所以我不得不使用这种方法。如果你有更简单的方法,很高兴看到你的贡献。 - CopsOnRoad
我该如何做到同样的事情,只是相反的方式?当"FORWARD"时,容器会出现,当"REVERSE"时,容器会消失。 - Oséias Ribeiro
@OséiasRibeiro 我还没到电脑前,等我到了会回复你的。不过应该很简单。 - CopsOnRoad
@CopsOnRoad,使用以下修改可以实现: 如果(direction == ScrollDirection.reverse){     ...     var difference = _prevOffset - offset;     _fromTop = _prevForwardOffset + difference;     if (_fromTop < -_containerHeight){         _fromTop = -_containerHeight;       } } else if (direction == ScrollDirection.forward) {     ...     var difference = offset - _prevOffset;     _fromTop = _prevReverseOffset - difference;     if (_fromTop > 0){         _fromTop = 0;       } } ... 谢谢! - Oséias Ribeiro

11
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    const double HEIGHT = 96;
    final ValueNotifier<double> notifier = ValueNotifier(0);
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(title: const Text('Test')),
        body: Stack(
          children: [
            NotificationListener<ScrollNotification>(
              onNotification: (n) {
                if (n.metrics.pixels <= HEIGHT) {
                  notifier.value = n.metrics.pixels;
                }
                return false;
              },
              child: ListView.builder(
                itemCount: 42,
                itemBuilder: (context, index) {
                  return Container(
                    height: 64,
                    padding: const EdgeInsets.all(16),
                    alignment: Alignment.centerLeft,
                    child: Text('Item $index'),
                  );
                },
              ),
            ),
            HideableWidget(height: HEIGHT, notifier: notifier),
          ],
        ),
      ),
    );
  }
}

class HideableWidget extends StatelessWidget {
  final double height;
  final ValueNotifier<double> notifier;

  HideableWidget({@required this.height, @required this.notifier});

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<double>(
      valueListenable: notifier,
      builder: (context, value, child) {
        return Transform.translate(
          offset: Offset(0, value - height),
          child: Container(
            height: 80,
            color: Colors.red,
          ),
        );
      },
    );
  }
}

enter image description here


嗨,我怎样才能实现相反的效果呢?(我想在滚动时隐藏容器)。 - ARUN BALAJI
@ARUNBALAJI 只需要将条件反转 -> 如果 (n.metrics.pixels >= HEIGHT) { notifier.value = n.metrics.pixels; } - Nicks
反转功能无法正常工作。 - PAGE BUNNII

4
我已经找到了解决方案。以下是示例代码
 class _DemoState extends State<WidgetDemo> {
      ScrollController scrollController = new ScrollController();
      bool isVisible = true;

      @override
      initState() {
        super.initState();
        scrollController.addListener(() {
          if (scrollController.position.userScrollDirection ==
              ScrollDirection.reverse) {
            if (isVisible)
              setState(() {
                isVisible = false;
              });
          }
          if (scrollController.position.userScrollDirection ==
              ScrollDirection.forward) {
            if (!isVisible)
              setState(() {
                isVisible = true;
              });
          }
        });
      }

      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Stack(
            children: <Widget>[
              new CustomScrollView(
                controller: scrollController,
                shrinkWrap: true,
                slivers: <Widget>[
                  new SliverPadding(
                    padding: const EdgeInsets.all(20.0),
                    sliver: new SliverList(
                      delegate: new SliverChildListDelegate(
                        <Widget>[
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                          const Text('My temp data'),
                          const Text('Wow its working'),
                        ],
                      ),
                    ),
                  ),
                ],
              ),
              AnimatedContainer(
                  duration: Duration(milliseconds: 400),
                  height: isVisible ? 60.0 : 0.0,
                  child: new Container(
                    color: Colors.green,
                    width: MediaQuery.of(context).size.width,
                    child: Center(child: Text("Container")),
                  )),
            ],
          ),
        );
      }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接