【问题标题】:Flutter: How to open Drawer programmaticallyFlutter:如何以编程方式打开抽屉
【发布时间】:2020-01-04 23:21:15
【问题描述】:

我想以编程方式打开Drawer,而不是通过滑动它,如何禁用该滑动功能(抽屉的触摸功能)

【问题讨论】:

  • 您希望它可以通过滑动访问吗?
  • 不,我想禁用触摸滑动,我只想以编程方式打开它

标签: flutter dart flutter-layout


【解决方案1】:

空安全码

  • 使用GlobalKey

    final GlobalKey<ScaffoldState> _key = GlobalKey(); // Create a key
    
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        key: _key, // Assign the key to Scaffold.
        drawer: Drawer(),
        floatingActionButton: FloatingActionButton(
          onPressed: () => _key.currentState!.openDrawer(), // <-- Opens drawer
        ),
      );
    }
    
  • 使用Builder

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        drawer: Drawer(),
        floatingActionButton: Builder(builder: (context) {
          return FloatingActionButton(
            onPressed: () => Scaffold.of(context).openDrawer(), // <-- Opens drawer.
          );
        }),
      );
    }
    

如果你想禁止使用拖动手势打开Drawer,你可以设置

Scaffold(
  drawerEnableOpenDragGesture: false
  // above code ...
)

【讨论】:

  • 感谢您的回答,对不起,它没有点击它正在滑动,如何禁用该滑动,我只想以编程方式打开它
  • 你很聪明,你只是改变了问题,它最初是点击现在滑动。
  • AFAIK,没有办法通过滑动来阻止抽屉打开,这违反了 Material Guidelines,您可以做的是您可以创建自己的自定义抽屉来实现该效果。
  • 是的,我改了问题,因为我问错了,抱歉,你能提供一个源来构建自定义抽屉
  • 这应该是接受的答案,因为接受的答案会禁用滑动打开,这实际上是不受欢迎的行为。
【解决方案2】:
  1. 要禁用滑动打开功能,您可以将 Scaffold 上的属性 drawerEnableOpenDragGesture 设置为 false。

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(
      home: Scaffold(
        // this to prevent the default sliding behaviour
        drawerEnableOpenDragGesture: false,
        drawer: Drawer(),
        appBar: AppBar(
          leading: Builder(builder: (context) => // Ensure Scaffold is in context
            IconButton(
              icon: Icon(Icons.menu),
              onPressed: () => Scaffold.of(context).openDrawer()
            ),
          ),
        )
      )
    );
  }
}

  1. 要使用Scaffold.of(context) 以编程方式打开抽屉,您必须确保(感谢Krolaw!)调用所在的上下文知道脚手架。

    一种简洁的方法是将按钮包装在构建器中。 我已经编辑了答案以包含一个最小的完整工作示例。

    Scaffold 是一个实现材料设计原则的小部件,因此请注意,要能够调用此方法,您需要 import 'package:flutter/material.dart'; 并且您的小部件需要有一个 MaterialApp 作为祖先。

Codepen demo


与许多 Flutter 事物一样,还有其他解决方案可以确保 Scaffold 在上下文中。

错误消息是 IMO 中 Flutter 框架的最佳功能之一,请允许我谦虚地建议始终通读它们并探索它们指向的文档。

例如,如果在适当的上下文之外调用 openDrawer,这是错误消息的一部分:

使用不包含 Scaffold 的上下文调用 Scaffold.of()。

从传递给 Scaffold.of() 的上下文开始,找不到任何 Scaffold 祖先。这通常发生在提供的上下文来自与其构建函数实际创建正在寻找的 Scaffold 小部件相同的 StatefulWidget 时。

有几种方法可以避免这个问题。最简单的方法是使用 Builder 来获取位于 Scaffold 下的上下文。有关这方面的示例,请参阅 Scaffold.of() 的文档: https://api.flutter.dev/flutter/material/Scaffold/of.html

更有效的解决方案是将构建功能拆分为多个小部件。这引入了一个新的上下文,您可以从中获取 Scaffold。在此解决方案中,您将拥有一个外部小部件,该小部件创建由新内部小部件的实例填充的 Scaffold,然后在这些内部小部件中,您将使用 Scaffold.of()。

一个不太优雅但更方便的解决方案是将 GlobalKey 分配给 Scaffold,然后使用 key.currentState 属性来获取 ScaffoldState 而不是使用 Scaffold.of() 函数。

【讨论】:

  • 代码本身不起作用,这只是一个建议,我已经用一个自包含的完整工作演示再次更新了答案,在 OSX 和 android studio 的模拟像素 2 上进行了测试: )
【解决方案3】:

这是另一个以编程方式从汉堡包图标打开抽屉的示例,并且没有 Appbar:-

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => MyAppState();
}

class MyAppState extends State<MyApp> {
  var scaffoldKey = GlobalKey<ScaffoldState>();

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        key: scaffoldKey,
        drawer: new Drawer(
          child: new ListView(
            padding: EdgeInsets.zero,
            children: <Widget>[
              DrawerHeader(
                child: Text('Drawer Header'),
                decoration: BoxDecoration(
                  color: Colors.blue,
                ),
              ),
              ListTile(
                title: Text('Item 1'),
                onTap: () {
                  //Do some stuff here
                  //Closing programmatically - very less practical use
                  scaffoldKey.currentState.openEndDrawer();
                },
              )
            ],
          ),
        ),
        body: Stack(
          children: <Widget>[
            new Center(
                child: new Column(
              children: <Widget>[],
            )),
            Positioned(
              left: 10,
              top: 20,
              child: IconButton(
                icon: Icon(Icons.menu),
                onPressed: () => scaffoldKey.currentState.openDrawer(),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

【讨论】:

    【解决方案4】:

    调用 Scaffold.of 不起作用,因为上下文不包含 Scaffold。上面的一些解决方案忽略了这一点,其他解决方案使用了 GlobalKey。我相信最干净的解决方案是将按钮包装在 Builder 中:

    Scaffold(
       drawerEnableOpenDragGesture: false, // Prevent user sliding open
       appBar: AppBar(
          automaticallyImplyLeading: false,
          title: Text("Some Title"),
          actions: [
             Builder(builder: (context) => // Ensure Scaffold is in context
                IconButton(
                   icon: Icon(Icons.settings),
                   onPressed: () => Scaffold.of(context).openDrawer()
             )),
          ],
       ),
       // TODO ...
    )
    

    【讨论】:

    • 在某些情况下(例如我们的情况),如果您正在处理全局快捷方式,您可能需要使用 GlobalKey() 但如果您在脚手架内,您建议的是首选方法.
    【解决方案5】:
    appBar: AppBar(
    
          automaticallyImplyLeading: false,
          title: Text(
            "Infilon Technologies",
            style:
                TextStyle(fontFamily: "Poppins", fontWeight: FontWeight.w600),
          ),
          actions: <Widget>[
            IconButton(
              icon: Icon(Icons.menu),
              onPressed: () {
                if (_scaffoldKey.currentState.isEndDrawerOpen) {
                  _scaffoldKey.currentState.openDrawer();
                } else {
                  _scaffoldKey.currentState.openEndDrawer();
                }
              },
            ),
          ],
        ),
    

    【讨论】:

    • 我知道这是一个很老的问题,但这确实激发了我的一个“最佳实践”问题——大多数人开发单页应用程序模型,整个应用程序都有一个脚手架,还是为每个页面提供自己的完整构建和自己的脚手架,并在它们之间进行页面->路由?
    • @ChrisH:这值得一个单独的问题,你不觉得吗?
    【解决方案6】:

    您必须将小部件包装在新的 BuildContext 中,因为您使用的上下文很可能是外部上下文,并且不知道 Scaffold。

    drawer: Builder(
      builder: (BuildContext internalContext) {
        return _drawer(internalContext);
      },
    ),
    

    查看完整代码

    class SampleAppPage extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          home: new Scaffold(
            appBar: _appBar(context),
            drawer: new Builder(
              builder: (BuildContext internalContext) {
                return _drawer(internalContext);
              },
            ),
            body: new Builder(
              builder: (BuildContext internalContext) {
                return _body(internalContext);
              },
            ),
          ),
        );
      }
    
      Widget _appBar(BuildContext context) {
        return new AppBar(
          title: new Text('Drawer example'),
        );
      }
    
      Widget _drawer(BuildContext context) {
        return new Center(
          child: new RaisedButton(
            child: new Text('Close drawer'),
            onPressed: () => Navigator.of(context).pop(),
          ),
        );
      }
    
      Widget _body(BuildContext context) {
        return new Column(
          children: <Widget>[
            new RaisedButton(
              child: new Text('Open via Scaffold context'),
              onPressed: () => Scaffold.of(context).openDrawer(),
            ),
          ],
        );
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2021-04-10
      • 2019-09-24
      • 2018-10-02
      • 1970-01-01
      • 1970-01-01
      • 2020-12-15
      • 2013-08-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多