用相同的动画隐藏bottomAppBar和FloatingActionButton

时间:2019-06-26 05:00:57

标签: flutter flutter-layout

在滚动列表中,我在SOF中发现的这个已实现的应用程序中,在隐藏floating action button时会调整大小CircularNotchedRectangleBottomAppBar的原因

enter image description here

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: WorkoutDetailsPage(Workout()),
    );
  }
}

class Exercise {
  String name;

  Exercise({@required name}) {
    this.name = name;
  }
}

class Workout {
  String name = "my name";
}

class WorkoutDetailsPage extends StatefulWidget {
  Workout _workout = Workout();

  WorkoutDetailsPage(this._workout);

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

class _WorkoutDetailsPageState extends State<WorkoutDetailsPage> {
  final List<Exercise> exercises = [
    Exercise(name: "Push Ups"),
    Exercise(name: "Bench press"),
    Exercise(name: "Pull ups"),
    Exercise(name: "Press ups"),
    Exercise(name: "Crunches"),
    Exercise(name: "Sit ups"),
    Exercise(name: "BIceps curl"),
    Exercise(name: "Something else"),
    Exercise(name: "Push Ups"),
    Exercise(name: "Bench press"),
    Exercise(name: "Pull ups"),
    Exercise(name: "Press ups"),
    Exercise(name: "Crunches"),
    Exercise(name: "Sit ups"),
    Exercise(name: "BIceps curl"),
    Exercise(name: "Something else"),
    Exercise(name: "Push Ups"),
    Exercise(name: "Bench press"),
    Exercise(name: "Pull ups"),
  ];

  ScrollController _hideButtonController;

  bool _isVisible = true;

  @override
  void initState() {
    super.initState();
    _isVisible = true;
    _hideButtonController = new ScrollController();
    _hideButtonController.addListener(() {
      print("listener");
      if (_hideButtonController.position.userScrollDirection == ScrollDirection.reverse) {
        setState(() {
          _isVisible = false;
          print("**** $_isVisible up");
        });
      }
      if (_hideButtonController.position.userScrollDirection == ScrollDirection.forward) {
        setState(() {
          _isVisible = true;
          print("**** $_isVisible down");
        });
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButtonAnimator: FloatingActionButtonAnimator.scaling,
      floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
      floatingActionButton: _isVisible
          ? FloatingActionButton(
              backgroundColor: Colors.blue,
              elevation: 12,
              onPressed: () {},
            )
          : null,
      bottomNavigationBar: AnimatedContainer(
        duration: Duration(milliseconds: 200),
        height: _isVisible ? 60 : 0.0,
        child: BottomAppBar(
          elevation: 8,
          shape: CircularNotchedRectangle(),
          color: Colors.blue,
          child: Container(
            height: 60,
            child: Row(
              children: <Widget>[Text("data")],
            ),
          ),
        ),
      ),
      body: CustomScrollView(
        controller: _hideButtonController,
        slivers: <Widget>[
          SliverAppBar(
            expandedHeight: kToolbarHeight,
            pinned: true,
            floating: true,
            snap: true,
            flexibleSpace: FlexibleSpaceBar(
              title: Text(widget._workout.name),
            ),
          ),
          SliverList(
            delegate: SliverChildBuilderDelegate(
                buildSliverListItem, childCount: exercises.length
                ),
          ),
        ],
      ),
    );
  }

  Widget buildSliverListItem(BuildContext context, int index) {
    return Center(
      child: ListTile(
        title: Text(exercises[index].name),
      ),
    );
  }
}

我该如何解决?我测试过将BottomAppBar放在Stack上,但这没用

2 个答案:

答案 0 :(得分:3)

如果您不想调整FloatingActionButton的大小,只需创建自己的FloatingActionButtonAnimator(对代码进行了一些更改),请检查以下内容:

@override
  void initState() {
    super.initState();
    _isVisible = true;
    _hideButtonController = new ScrollController();
    _hideButtonController.addListener(() {
      //print("listener");
      if (_hideButtonController.position.userScrollDirection ==
          ScrollDirection.reverse) {
        if (_isVisible)
          setState(() {
            _isVisible = false;
            //print("**** $_isVisible up");
          });
      }
      if (_hideButtonController.position.userScrollDirection ==
          ScrollDirection.forward) {
        if (!_isVisible)
          setState(() {
            _isVisible = true;
            //print("**** $_isVisible down");
          });
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButtonAnimator: MyFabAnimation(),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
      floatingActionButton: FloatingActionButton(
        backgroundColor: Colors.red,
        elevation: 12,
        onPressed: () {},
      ),
      bottomNavigationBar: AnimatedContainer(
        duration: Duration(milliseconds: 200),
        height: _isVisible ? 60 : 0.0,
        child: BottomAppBar(
          elevation: 8,
          shape: CircularNotchedRectangle(),
          color: Colors.blue,
          child: Container(
            height: 60,
            child: Row(
              children: <Widget>[Text("data")],
            ),
          ),
        ),
      ),
      body: CustomScrollView(
        controller: _hideButtonController,
        slivers: <Widget>[
          SliverAppBar(
            expandedHeight: kToolbarHeight,
            pinned: true,
            floating: true,
            snap: true,
            flexibleSpace: FlexibleSpaceBar(
              title: Text(widget._workout.name),
            ),
          ),
          SliverList(
            delegate: SliverChildBuilderDelegate(buildSliverListItem,
                childCount: exercises.length),
          ),
        ],
      ),
    );
  }

这是您的FloatingActionButton的自定义动画:

double minValue = double.maxFinite;

class MyFabAnimation extends FloatingActionButtonAnimator {
  @override
  Offset getOffset({Offset begin, Offset end, double progress}) {
    if (minValue == double.maxFinite) {
      minValue = end.dy;
    } else {
      if (begin.dy < minValue) minValue = begin.dy;
    }
    double difference = end.dy - minValue;
    return Offset(end.dx, end.dy + (difference * 1.8));
  }

  @override
  Animation<double> getRotationAnimation({Animation<double> parent}) {
    return Tween<double>(begin: 1.0, end: 1.0).animate(parent);
  }

  @override
  Animation<double> getScaleAnimation({Animation<double> parent}) {
    const Curve curve = Interval(0.5, 1.0, curve: Curves.ease);
    return _AnimationSwap<double>(
      ReverseAnimation(parent.drive(CurveTween(curve: curve.flipped))),
      parent.drive(CurveTween(curve: curve)),
      parent,
      0.5,
    );
  }
}

class _AnimationSwap<T> extends CompoundAnimation<T> {
  _AnimationSwap(
      Animation<T> first, Animation<T> next, this.parent, this.swapThreshold)
      : super(first: first, next: next);

  final Animation<double> parent;
  final double swapThreshold;

  @override
  T get value => parent.value < swapThreshold ? first.value : next.value;
}

Result

答案 1 :(得分:1)

输出:

enter image description here


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

class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
  AnimationController _controller;
  Tween<Offset> _tween = Tween(begin: Offset(0, 0), end: Offset(0, 1.5));
  bool _visible = true;
  Duration _duration = Duration(milliseconds: 500);
  Curve _curve = Curves.linear;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: _duration,
    );

    _controller.addListener(() {
      value = _tween.evaluate(_controller).dy * 100;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      floatingActionButton: FloatingActionButton(onPressed: _animate,),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
      floatingActionButtonAnimator: MyAnimator(),
      bottomNavigationBar: SlideTransition(
        position: _tween.animate(_controller),
        child: AnimatedContainer(
          duration: _duration,
          curve: _curve,
          height: _visible ? 100 : 0,
          child: BottomAppBar(color: Colors.blue, shape: CircularNotchedRectangle()),
        ),
      ),
    );
  }

  void _animate() async {
    if (_controller.isDismissed)
      _controller.forward();
    else if (_controller.isCompleted) _controller.reverse();

    _visible = !_visible;
    setState(() {});
  }
}

double value = 0;

class MyAnimator extends FloatingActionButtonAnimator {
  @override
  Offset getOffset({Offset begin, Offset end, double progress}) {
    Offset offset = Offset(begin.dx, begin.dy + value/2);
    return offset;
  }

  @override
  Animation<double> getRotationAnimation({Animation<double> parent}) {
    return parent;
  }

  @override
  Animation<double> getScaleAnimation({Animation<double> parent}) {
    return parent;
  }
}