如何让抽屉推动内容而不是放在上面?

Posted

技术标签:

【中文标题】如何让抽屉推动内容而不是放在上面?【英文标题】:How to have the drawer push the content instead of going on top of it? 【发布时间】:2021-06-11 19:37:24 【问题描述】:

我正在尝试构建类似于 slack 应用程序(见下面的屏幕截图)的东西,其中导航抽屉将屏幕推开而不是放在顶部。

我一直在尝试使用 Drawer 组件,但没有成功。我也看过PageView,但似乎孩子们需要占用 100% 的宽度。

有人知道如何实现它吗?

【问题讨论】:

scaffolds openDrawer 使用导航器,您可以使用带有Scrollable.ensureVisible(context) 的单子水平滚动视图(禁用滚动物理)来显示菜单 【参考方案1】:

编辑

使用StackAnimatedPositioned 可以获得类似的结果

class SlidingDrawer extends StatefulWidget 
  final Widget drawer;
  final Widget child;
  final int swipeSensitivity;
  final double drawerRatio;
  final Color overlayColor;
  final double overlayOpacity;
  final int animationDuration;
  final Curve animationCurve;

  SlidingDrawer(
    Key key,
    @required this.drawer,
    @required this.child,
    this.swipeSensitivity = 25,
    this.drawerRatio = 0.8,
    this.overlayColor = Colors.black,
    this.overlayOpacity = 0.5,
    this.animationDuration = 500,
    this.animationCurve = Curves.ease,
  ) : super(key: key);
  @override
  _SlidingDrawerState createState() => _SlidingDrawerState();


class _SlidingDrawerState extends State<SlidingDrawer> 
  bool _opened = false;

  void open() 
    setState(() 
      _opened = true;
    );
  

  void close() 
    setState(() 
      _opened = false;
    );
  

  @override
  Widget build(BuildContext context) 
    final width = MediaQuery.of(context).size.width;
    final height = MediaQuery.of(context).size.height;
    final drawerWidth = width * widget.drawerRatio;

    return GestureDetector(
      onHorizontalDragUpdate: (details) 
        if (details.delta.dx > widget.swipeSensitivity) 
          open();
         else if (details.delta.dx < -widget.swipeSensitivity) 
          close();
        
      ,
      child: SizedBox(
        width: width,
        height: height,
        child: Stack(
          children: [
            AnimatedPositioned(
              width: drawerWidth,
              height: height,
              left: _opened ? 0 : -drawerWidth,
              duration: Duration(milliseconds: widget.animationDuration),
              curve: widget.animationCurve,
              child: Container(
                color: Colors.amber,
                child: widget.drawer,
              ),
            ),
            AnimatedPositioned(
              height: height,
              width: width,
              left: _opened ? drawerWidth : 0,
              duration: Duration(milliseconds: widget.animationDuration),
              curve: widget.animationCurve,
              child: Stack(
                fit: StackFit.expand,
                children: [
                  widget.child,
                  AnimatedSwitcher(
                    duration: Duration(milliseconds: widget.animationDuration),
                    switchInCurve: widget.animationCurve,
                    switchOutCurve: widget.animationCurve,
                    child: _opened
                        ? GestureDetector(
                            onTap: () 
                              setState(() 
                                _opened = false;
                              );
                            ,
                            child: Container(
                              color: widget.overlayColor.withOpacity(
                                widget.overlayOpacity,
                              ),
                            ),
                          )
                        : null,
                  )
                ],
              ),
            ),
          ],
        ),
      ),
    );
  

原始答案

正如@Yadu 在评论中指出的那样

您可以使用带有 Scrollable.ensureVisible(context) 的单子水平滚动视图(禁用滚动物理)来显示菜单

使用水平滚动视图是有效的。

import 'package:flutter/material.dart';

void main() 
  runApp(MyApp());


class MyApp extends StatelessWidget 
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  


class MyHomePage extends StatefulWidget 
  MyHomePage(Key key) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();


class _MyHomePageState extends State<MyHomePage> 
  bool _drawerOpened = false;

  final drawerKey = new GlobalKey();
  final mainKey = new GlobalKey();

  @override
  Widget build(BuildContext context) 
    return SingleChildScrollView(
      physics: NeverScrollableScrollPhysics(),
      scrollDirection: Axis.horizontal,
      child: Row(
        children: [
          Container(
            key: drawerKey,
            color: Colors.green,
            width: MediaQuery.of(context).size.width * 0.8,
          ),
          SizedBox(
            key: mainKey,
            width: MediaQuery.of(context).size.width,
            height: MediaQuery.of(context).size.height,
            child: Scaffold(
              appBar: AppBar(
                title: Text("My Page"),
                leading: IconButton(
                  icon: Icon(Icons.menu),
                  onPressed: _toggleDrawer,
                ),
              ),
              body: Container(
                color: Colors.yellow,
                width: MediaQuery.of(context).size.width,
              ),
            ),
          ),
        ],
      ),
    );
  

  void _toggleDrawer() 
    setState(() 
      _drawerOpened = !_drawerOpened;
    );
    if (_drawerOpened) 
      Scrollable.ensureVisible(drawerKey.currentContext);
     else 
      Scrollable.ensureVisible(mainKey.currentContext);
    
  

【讨论】:

以上是关于如何让抽屉推动内容而不是放在上面?的主要内容,如果未能解决你的问题,请参考以下文章

如何导航到另一个页面而不会弹出抽屉?

单击抽屉的任何项目而不在android中关闭时更改导航抽屉内的视图和内容

Android:更改导航抽屉应用程序中的当前内容,而不在抽屉菜单中显示项目

导航抽屉开启新活动

如何将抽屉连接到颤动的动画图标按钮?

在 react native 中构建自定义抽屉时,如何在抽屉项目上使用按钮/图标?