【问题标题】:Show (slide in) or hide (slide out) flutter AppBar on screen tap在屏幕点击时显示(滑入)或隐藏(滑出)flutter AppBar
【发布时间】:2019-04-28 15:06:39
【问题描述】:

请我尝试创建这种效果,即当点击屏幕时 AppBar 滑出,再次点击屏幕时滑入。

我可以通过将浮动和捕捉设置为 true 在 SliverAppBar 中创建类似的东西。不同之处在于 appBar 在向下滚动时显示,在屏幕被点击或向上滚动时隐藏。

这里是 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),),
                    ]),
                  )
                ],
        ),
    );
  }  

我怎样才能做到这一点?我还考虑将 AppBar 放在 Stack 中,但我认为这不是最好的方法。非常感谢您的帮助!

【问题讨论】:

    标签: android flutter dart flutter-layout


    【解决方案1】:

    我遇到了类似的需求,并发现了您的问题。由于没有答案,我自己尝试解决问题。我知道你在 6 个月前问过这个问题,但我会提供一个(几乎完整的)答案,以防其他人遇到这种情况。

    (如果我的方法不够优雅,我深表歉意,但在撰写本文时,我只使用 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),
                  ),
                ),
              ),
            ],
          )
        );
      }
    }
    

    本质上,AppBar 作为 Scaffold 的直接部分被移除,而是被添加到 Stack,在那里它实际上可以被动画化。为了确保图像在其背后可见,将其放入容器中,以便控制其高度(否则,您将无法看到图像)。

    在我上面的代码中,点击图像会使 AppBar 缩回,再次点击会使它重新出现。不过,出于某种原因,我实际上无法让它顺利地前后移动,但 效果 是存在的。

    实际上,它看起来像这样:

    如果有人(在我做之前)弄清楚我错过了什么以使其动画流畅,请随时提供帮助。

    【讨论】:

      【解决方案2】:

      只需将 Bob H. 解决方案中的 SlideTransition 替换为 AnimatedBuilder 和 Transform.translate 小部件:

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

      【讨论】:

        【解决方案3】:

        这是我的回答:

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

        【讨论】:

          【解决方案4】:

          我在这里遇到了CopsOnRoad 建议的更好方法:Flutter - How can I dynamically show or hide App Bars on pages

          只是为他人重新分享。

          我们可以将动画部分抽象到另一个小部件中,例如:

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

          现在这个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...'),
                  )
                )
              );
            }
          }
          

          【讨论】:

            猜你喜欢
            • 2021-05-29
            • 2014-01-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-05-13
            • 2019-06-14
            • 1970-01-01
            相关资源
            最近更新 更多