反弹的可拖动滚动-颤动

时间:2018-11-28 08:12:36

标签: flutter

当前正在创建可反弹的可拖动滚动。使用动画控制器,动画和补间。有效...第一次拖动。随后的拖动从第一个/初始位置向后拖动。

enter image description here

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text('Draggable Scroll Bar Demo'),
        ),
        //DraggableScrollbar builds Stack with provided Scrollable List of Grid
        body: new DraggableScrollbar(
          child: _buildGrid(),
        ),
      ),
    );
  }

  Widget _buildGrid() {
    return GridView.builder(
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 5,
      ),
      padding: EdgeInsets.zero,
      itemCount: 1000,
      itemBuilder: (context, index) {
        return Container(
          alignment: Alignment.center,
          margin: EdgeInsets.all(2.0),
          color: Colors.grey[300],
        );
      },
    );
  }
}

class DraggableScrollbar extends StatefulWidget {
  const DraggableScrollbar({this.child});

  final Widget child;

  @override
  _DraggableScrollbarState createState() => new _DraggableScrollbarState();
}

class _DraggableScrollbarState extends State<DraggableScrollbar>
    with TickerProviderStateMixin {
  //this counts offset for scroll thumb for Vertical axis
  double _barOffset;
  // controller for the thing
  AnimationController animationController;
  Animation<double> animation;

  @override
  void initState() {
    super.initState();
    animationController =
        AnimationController(vsync: this, duration: Duration(seconds: 1));
    _barOffset = 300.0;
  }

  void _onVerticalDragUpdate(DragUpdateDetails details) {
    setState(() {
      _barOffset += details.delta.dy;
    });
  }

  void _animateSelectorBack() {
    if (mounted) {
      setState(() {
        _barOffset = animation.value;
      });
    }
  }

  void _verticalGoesBack(DragEndDetails details) {
    animationController.reset();

    animation = Tween<double>(begin: _barOffset, end: 300.0)
        .animate(animationController)
          ..addListener(_animateSelectorBack);

    animationController.forward();
  }

  @override
  Widget build(BuildContext context) {
    return new Stack(children: <Widget>[
      widget.child,
      GestureDetector(
          onVerticalDragUpdate: _onVerticalDragUpdate,
          onVerticalDragEnd: _verticalGoesBack,
          child: Container(
              alignment: Alignment.topRight,
              margin: EdgeInsets.only(top: _barOffset),
              child: _buildScrollThumb())),
    ]);
  }

  Widget _buildScrollThumb() {
    return new Container(
      height: 40.0,
      width: 20.0,
      color: Colors.blue,
    );
  }

  @override
  void dispose() {
    super.dispose();
    animationController.dispose();
  }
}

我认为它保存了初始位置,但由于将状态设置为初始位置而无法发生。我对动画/状态管理如何协同工作感到困惑

2 个答案:

答案 0 :(得分:0)

animationController.reset()正在重置_barOffset的值,所以我刚刚创建了一个_previousPosition,动画将其用作起始值:

class _DraggableScrollbarState extends State<DraggableScrollbar>
    with TickerProviderStateMixin {
  //this counts offset for scroll thumb for Vertical axis
  double _barOffset;
  double _previousPosition;
  // controller for the thing
  AnimationController animationController;
  Animation<double> animation;

  @override
  void initState() {
    super.initState();
    _barOffset = 300.0;
    _previousPosition = 0.0;
    animationController =
        AnimationController(vsync: this, duration: Duration(seconds: 1));
  }

  void _onVerticalDragUpdate(DragUpdateDetails details) {
    setState(() {
      _barOffset += details.delta.dy;
      _previousPosition = _barOffset;
    });
  }

  void _animateSelectorBack() {
    if (mounted) {
      setState(() {
        _barOffset = animation.value;
      });
    }
  }

  void _verticalGoesBack(DragEndDetails details) {
    animationController.reset();

    animation = Tween<double>(begin: _previousPosition, end: 300.0)
        .animate(animationController)
        ..addListener(_animateSelectorBack);

    animationController.forward();
  }

  @override
  Widget build(BuildContext context) {
    return new Stack(children: <Widget>[
      widget.child,
      GestureDetector(
          onVerticalDragUpdate: _onVerticalDragUpdate,
          onVerticalDragEnd: _verticalGoesBack,
          child: Container(
              alignment: Alignment.topRight,
              margin: EdgeInsets.only(top: _barOffset),
              child: _buildScrollThumb())),
    ]);
  }

  Widget _buildScrollThumb() {
    return new Container(
      height: 40.0,
      width: 20.0,
      color: Colors.blue,
    );
  }

  @override
  void dispose() {
    super.dispose();
    animationController.dispose();
  }
}

可能有一种更优雅的方法可以实现这一目标,但是可以正常使用!

demo

答案 1 :(得分:0)

我找到了某种可行的方法。

void _verticalGoesBack(DragEndDetails details) {
    animationController.reset();

    animation = Tween<double>(begin: _barOffset, end: 300.0)
        .animate(animationController)
          ..addListener(_animateSelectorBack)
          ..addStatusListener((status) {
            if (status == AnimationStatus.completed) {
              animationController.removeListener(_animateSelectorBack);
            }
          });

    animationController.forward();
  }

认为_animateSelectorBack在某种程度上存储了初始滚动条的位置。这可能是错误的。我猜它更干净了,而无需添加变量和额外的初始化。