簡體   English   中英

在屏幕點擊上顯示(滑入)或隱藏(滑出)flutter AppBar

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

請我嘗試創建這種效果,當點擊屏幕時 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 中,但我認為這不是最好的方法。 對你的幫助表示感謝!

我遇到了類似的需求,並發現了您的問題。 由於沒有答案,我自己嘗試解決問題。 我知道你在 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 縮回,再次點擊會使它重新出現。 但是,出於某種原因,我實際上無法讓它平滑地前后動畫,但效果就在那里。

在實踐中,它看起來像這樣:

動畫 AppBar 可見性

如果有人(在我做之前)弄清楚我錯過了什么以使其順利動畫,請隨時提供幫助。

只需將 Bob H. 解決方案中的 SlideTransition 替換為 AnimatedBuilder 和 Transform.translate 小部件:

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

這是我的回答:

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

我在這里遇到了CopsOnRoad建議的更好方法: Flutter - 如何在頁面上動態顯示或隱藏應用欄

只是重新分享給其他人。

我們可以在另一個小部件中抽象動畫部分,例如:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM