Flutter show and hide container on scrolling ListView

倾然丶 夕夏残阳落幕 提交于 2020-01-16 09:02:13

问题


How can I show a Container with animation on scrolling ListView down and hide it when scrolling up.

The video I am attaching is not exact implementation I want but it is just to give you an idea.

Sample video

https://imgur.com/a/auEzJQk


Edit:

Every time I scroll down, I need to show the Container and every time I scroll up, I want to hide it. It shouldn't depend on the index of the ListView.


回答1:


Not sure if I got your question properly, is this what you are trying to achieve?

Screenshot:


Code:

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:


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,
          ),
        );
      },
    );
  }
}




回答3:


I got the solution of your problem. Here is the demo code

 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")),
                  )),
            ],
          ),
        );
      }
    }


来源:https://stackoverflow.com/questions/58254569/flutter-show-and-hide-container-on-scrolling-listview

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!