简体   繁体   English

在屏幕点击上显示(滑入)或隐藏(滑出)flutter AppBar

[英]Show (slide in) or hide (slide out) flutter AppBar on screen tap

Please I am trying to create this effect where the AppBar slides out when the screen is tapped and slides in when it is tapped again.请我尝试创建这种效果,当点击屏幕时 AppBar 滑出并在再次点击时滑入。

示例图像

I am able to create something similar in SliverAppBar by setting floating and snap to true.通过将浮动和捕捉设置为 true,我可以在 SliverAppBar 中创建类似的东西。 The difference is that the appBar shows when scrolling down and hides when screen is tapped or scrolled up.不同之处在于 appBar 在向下滚动时显示,在屏幕被点击或向上滚动时隐藏。

Here is the sample code for the SliverAppBar:这是 SliverAppBar 的示例代码:

 @override
  Widget build(BuildContext context) {

    return Scaffold(
          body: CustomScrollView(
            controller: _ctrlr,

                slivers: <Widget>[
                  SliverAppBar(
                    floating: true,
                    snap: true,

                  ),
                  SliverList(
                    delegate: SliverChildListDelegate([
                      Text('1', style: TextStyle(fontSize: 160.0),),
                      Text('2', style: TextStyle(fontSize: 160.0),),
                      Text('3', style: TextStyle(fontSize: 160.0),),
                      Text('4', style: TextStyle(fontSize: 160.0),),
                    ]),
                  )
                ],
        ),
    );
  }  

How can I be able to achieve this?我怎样才能做到这一点? I also considered placing the AppBar in a Stack but i don't think that is the best approach.我还考虑将 AppBar 放在 Stack 中,但我认为这不是最好的方法。 Your help will be greatly appreciated!对你的帮助表示感谢!

I came upon a similar need, and discovered your question.我遇到了类似的需求,并发现了您的问题。 Since there were no answers, I took it upon myself to try and solve the problem.由于没有答案,我自己尝试解决问题。 I know you asked this 6 months ago, but I'm putting in an (nearly complete) answer in case anybody else happens upon it.我知道你在 6 个月前问过这个问题,但我正在提供一个(几乎完整的)答案,以防其他人遇到它。

(I apologize if my approach is less than elegant, but as of this writing, I've only been using Flutter for about a week. :) (如果我的方法不够优雅,我深表歉意,但在撰写本文时,我只使用 Flutter 大约一个星期。:)

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

class FramePage extends StatefulWidget {
  final String title;
  final String imageUrl;

  FramePage({Key key, this.title, this.imageUrl}) : super(key: key);

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

class _FramePageState extends State<FramePage> with SingleTickerProviderStateMixin {
  AnimationController _controller;
  bool _appBarVisible;

  @override
  void initState() {
    super.initState();

    _appBarVisible = true;
    _controller = AnimationController(
      duration: const Duration(milliseconds: 700),
      value: 1.0,
      vsync: this,
    );
  }

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

  void _toggleAppBarVisibility() {
    _appBarVisible = !_appBarVisible;
    _appBarVisible ? _controller.forward() : _controller.reverse();
  }

  Widget get _imageWidget {
    return Center(
      child: GestureDetector(
      onTap: () => setState(() { _toggleAppBarVisibility(); } ),
      child: Container(
          foregroundDecoration: new BoxDecoration(color: Color.fromRGBO(155, 85, 250, 0.0)),
          child: FadeInImage.memoryNetwork(
            placeholder: kTransparentImage,
            image: widget.imageUrl,
            fit: BoxFit.cover,
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context)
  {
    Animation<Offset> offsetAnimation = new Tween<Offset>(
      begin: Offset(0.0, -70),
      end: Offset(0.0, 0.0),
    ).animate(_controller);

    return Scaffold(
      body: Stack(
        children: <Widget>[
          _imageWidget,
          SlideTransition(
            position: offsetAnimation,
            child: Container(
              height: 75,
              child: AppBar(
                title: Text(widget.title),
              ),
            ),
          ),
        ],
      )
    );
  }
}

Essentially, the AppBar is removed as a direct part of the Scaffold, and instead is added to the Stack, where it can actually be animated.本质上,AppBar 作为 Scaffold 的直接部分被移除,而是被添加到 Stack 中,在那里它实际上可以被动画化。 In order to make sure the image is visible behind it, it is placed into a Container so it's height can be controlled (otherwise, you would not be able to see the image).为了确保图像在其后面可见,它被放置在一个容器中,以便可以控制它的高度(否则,您将无法看到图像)。

In my code above, tapping on the image makes the AppBar retract, and tapping again makes it re-appear.在我上面的代码中,点击图像会使 AppBar 缩回,再次点击会使它重新出现。 For some reason, though, I haven't been able to actually get it to smoothly animate forward and back, but the effect is there.但是,出于某种原因,我实际上无法让它平滑地前后动画,但效果就在那里。

In practice, it looks like this:在实践中,它看起来像这样:

动画 AppBar 可见性

If somebody figures out (before I do) what I've missed to make it animate smoothly, please feel free to help out.如果有人(在我做之前)弄清楚我错过了什么以使其顺利动画,请随时提供帮助。

Just replace the SlideTransition in Bob H.'s solution with an AnimatedBuilder and a Transform.translate widget:只需将 Bob H. 解决方案中的 SlideTransition 替换为 AnimatedBuilder 和 Transform.translate 小部件:

                          animation: offsetAnimation,
                          builder: (context, child) {
                            return Transform.translate(
                                offset: offsetAnimation.value,
                                child: Container( ....```

This is my answer:这是我的回答:

import 'package:flutter/material.dart';

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

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

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

class _HomePageState extends State<HomePage> {
  bool show = true;
  @override
  Widget build(BuildContext context) {
    var appbar = AppBar();
    var maxHeight = appbar.preferredSize.height;
    return SafeArea(
      child: Scaffold(
        body: Stack(
          children: <Widget>[
            Positioned.fill(
                child: GestureDetector(
                    onTap: () {
                      setState(() {
                        show = !show;
                      });
                    },
                    child: Container(
                        color: Colors.black,
                        child: Center(child: FlutterLogo())))),
            AnimatedAlign(
              duration: kThemeAnimationDuration,
              alignment: Alignment(0, show ? -1 : -2),
              child: ConstrainedBox(
                constraints: BoxConstraints(maxHeight: maxHeight),
                child: FlexibleSpaceBar.createSettings(
                  currentExtent: maxHeight,
                  child: appbar,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

I came across a better approach suggested by CopsOnRoad here: Flutter - How can I dynamically show or hide App Bars on pages我在这里遇到了CopsOnRoad建议的更好方法: Flutter - 如何在页面上动态显示或隐藏应用栏

Just re-sharing for others.只是重新分享给其他人。

We can abstract the animation part in another widget like:我们可以在另一个小部件中抽象动画部分,例如:

class SlidingAppBar extends PreferredSize {
  SlidingAppBar({
    @required this.child,
    @required this.controller,
    @required this.visible,
  });

  @override
  final PreferredSizeWidget child;

  @override
  Size get preferredSize => child.preferredSize;

  final AnimationController controller;
  final bool visible;

  @override
  Widget build(BuildContext context) {
    visible ? controller.reverse() : controller.forward();
    return SlideTransition(
      position: Tween<Offset>(begin: Offset.zero, end: Offset(0, -1)).animate(
        CurvedAnimation(parent: controller, curve: Curves.fastOutSlowIn),
      ),
      child: child,
    );
  }
}

Now this SlidingAppBar widget can be used with Scaffold 's appBar field instead of using Stack widget like:现在这个SlidingAppBar小部件可以与ScaffoldappBar字段一起使用,而不是使用Stack小部件,例如:

class _MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
  bool _visible = true;
  AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 400),
    );
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // extendBodyBehindAppBar: !_visible, // Uses entire screen after hiding AppBar
      appBar: SlidingAppBar(
        controller: _controller,
        visible: _visible,
        child: AppBar(title: Text('AppBar')),
      ),
      body: GestureDetector(
        onTap: () => setState(() => _visible = !_visible),
        child: Container(
          height: double.infinity,
          child: Text('App content...'),
        )
      )
    );
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM