Flutter双侧滑动菜单插件both_side_sheet的使用

Flutter双侧滑动菜单插件both_side_sheet的使用

both_side_sheet是一个可以从底部弹出的菜单,也可以根据设备的方向从侧面弹出。它结合了底部菜单(Bottom Sheet)和侧面菜单(Side Sheet)的功能。

示例展示

A example

开始使用

添加依赖

pubspec.yaml 文件中添加以下依赖:

dependencies:
  both_side_sheet:

或者执行以下命令来添加依赖:

flutter pub add both_side_sheet

使用方法

显示双侧滑动菜单

在你的项目中导入 both_side_sheet 包,并使用 BothSideSheet.show 方法来显示菜单。以下是一个简单的示例:

import 'package:both_side_sheet/both_side_sheet.dart';

// 在需要显示菜单的地方调用该方法
BothSideSheet.show(
  context: context, // BuildContext of the context.
  title: "Both Side Sheet Example", // 菜单的标题。
  child: // 你可以在这里放置任何你想显示的Widget。
  const SingleChildScrollView(
    child: Text('''
    这里可以放置任何你想显示的内容...
    '''),
  ),
);

完整示例代码

以下是一个完整的示例代码,展示了如何在应用中使用 both_side_sheet 插件。

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

void main() {
  runApp(const MaterialApp(
    home: MainApp(),
  ));
}

class MainApp extends StatefulWidget {
  const MainApp({super.key});

  @override
  State<MainApp> createState() => _MainAppState();
}

class _MainAppState extends State<MainApp> {
  (String, List<String>)? searchTags;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            TextButton(
              onPressed: () => BothSideSheet.show(
                context: context,
                title: "Both Side Sheet Example",
                child: const SingleChildScrollView(
                  child: Text('''
                  Ripples... / Genesis

                  Credit: Mike Rutherford & Tony Banks, 1976
                  With: Steve Hackett & Phil Collins
                  From: A Trick of the Tail

                  Blue girls come in every size
                  Some are wise and some otherwise

                  They got pretty blue eyes
                  For an hour a man may change
                  For an hour her face looks strange
                  Looks strange, looks strange

                  Marching to the promised land
                  Where the honey flows
                  And takes you by the hand

                  Pulls you down on your knees
                  While you're down a pool appears
                  The face in the water looks up

                  And she shakes her head as if to say
                  That it's the last time you'll look like today

                  [Chorus]
                  Sail away, away
                  Ripples never come back
                  Gone to the other side
                  Sail away, away

                  [Verse 2]
                  The face that launched a thousand ships
                  Is sinking fast, that happens you know

                  The water gets below, seems not very long ago
                  Lovelier she was than any that I know

                  Angels never know it's time
                  To close the book
                  And gracefully decline

                  The song has found a tale
                  My, what a jealous pool is she

                  The face in the water looks up
                  She shakes her head as if to say
                  That the blue girls have all gone away

                  [Extended Chorus]
                  Sail away, away
                  Ripples never come back
                  They've gone to the other side

                  Look into the pool, ripples never come back
                  Dive to the bottom and go to the top
                  To see where they have gone
                  They've gone to the other side
                  '''),
                ),
              ),
              child: const Text('Sad ballad'),
            ),
            TextButton(
              onPressed: () async {
                searchTags = await BothSideSheet.show<(String, List<String>)>(
                  context: context,
                  child: CategoryChoiceView(
                    data: searchTags ?? ("", []),
                  ),
                  title: "Type of position",
                );
                if (mounted) {
                  setState(() {});
                }
              },
              child: const Text("Change the type of position"),
            ),
            Text("Current type choice: $searchTags.")
          ],
        ),
      ),
    );
  }
}

其他信息

与Traintime PDA代码库不同的是,这个插件增加了分隔符。如果有任何改进建议或想法,欢迎提出来。

如何工作

如你所见,在竖屏时,它是从底部弹出的,类似于底部菜单(Bottom Sheet)。而在横屏时,它是从右侧弹出的,类似于侧面菜单(Side Sheet)。

在Flutter的Material框架中,本身没有实现侧面菜单(Side Sheet),而对于横屏来说,底部菜单(Bottom Sheet)会占用大量屏幕空间,而且不太美观。因此,从左侧弹出侧面菜单更为合适。由于很多开发者已经实现了侧面菜单插件,我们可以直接使用这些插件的概念,但我想将这两个功能结合起来。

为什么要将这两个功能结合在一起呢?这涉及到实际使用中,我们是如何呼出底部菜单(Bottom Sheet)的。

呼出底部菜单(Bottom Sheet)就像呼出对话框(Dialog)一样,使用了一个函数,在这里称为 showBottomSheet。这个问题在于,本质上它是往路由栈里面压入一个底部菜单页面栈,也就是说,无论横屏还是竖屏,它永远是底部菜单,而不会变化。我最初使用侧面菜单(Side Sheet),结果发现在横屏时打开了侧面菜单,但在竖屏时仍然是侧面菜单,它们之间不会互相转化。

所以我决定缝合这两个功能。侧面菜单(Side Sheet)比较好处理,可以直接复制前辈的代码,并且我也顺带复制了使用 showGeneralDialog 来显示弹窗的方法。但底部菜单(Bottom Sheet)本身没有任何代码资料,我只能自己编写。我使用了 StatefulWidget 来保存 heightForVertical 变量,这是一个高度变量,默认为页面高度的80%。然后我使用了一个 GestureDetector 手势侦测器,在拖拽最上面的小横杠时进行当前高度检测,并更新高度。这里我将收起的高度定为页面高度的40%。

这里我说明一下底部菜单(Bottom Sheet)和侧面菜单(Side Sheet)的特点,它们都可以分成两个部分,上面的和下面的。下面的部分是通过参数传递进来的部件,上面的部分则是属于部件的东西。

最后再说一句,原来的侧面菜单(Side Sheet)的最上面是使用 AppBar 实现的,但 AppBar 会侦测手机的状态栏,最终导致在某些情况下,上面的高度过高。我被迫自行实现了这部分,使得效果变得有些难看。


更多关于Flutter双侧滑动菜单插件both_side_sheet的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter双侧滑动菜单插件both_side_sheet的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用both_side_sheet插件来实现双侧滑动菜单的示例代码。这个插件允许你在Flutter应用中轻松实现左右两侧的滑动菜单。

首先,确保你已经在pubspec.yaml文件中添加了both_side_sheet依赖:

dependencies:
  flutter:
    sdk: flutter
  both_side_sheet: ^最新版本号  # 请替换为最新的版本号

然后,运行flutter pub get来安装依赖。

接下来,是一个简单的示例代码,展示如何使用BothSideSheet组件:

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final GlobalKey<BothSideSheetState> _bothSideSheetKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('双侧滑动菜单示例'),
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.menu),
            onPressed: () {
              _bothSideSheetKey.currentState?.openLeftSheet();
            },
          ),
          IconButton(
            icon: Icon(Icons.more_horiz),
            onPressed: () {
              _bothSideSheetKey.currentState?.openRightSheet();
            },
          ),
        ],
      ),
      body: Center(
        child: Text('滑动左右两侧菜单'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        tooltip: '焦点按钮',
        child: Icon(Icons.add),
      ),
      bottomSheet: BothSideSheet(
        key: _bothSideSheetKey,
        leftSheet: Container(
          color: Colors.blue,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              ListTile(
                leading: Icon(Icons.star),
                title: Text('左侧菜单项 1'),
                onTap: () {
                  _bothSideSheetKey.currentState?.closeSheet();
                },
              ),
              ListTile(
                leading: Icon(Icons.star_border),
                title: Text('左侧菜单项 2'),
                onTap: () {
                  _bothSideSheetKey.currentState?.closeSheet();
                },
              ),
            ],
          ),
        ),
        rightSheet: Container(
          color: Colors.green,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              ListTile(
                leading: Icon(Icons.settings),
                title: Text('右侧菜单项 1'),
                onTap: () {
                  _bothSideSheetKey.currentState?.closeSheet();
                },
              ),
              ListTile(
                leading: Icon(Icons.info),
                title: Text('右侧菜单项 2'),
                onTap: () {
                  _bothSideSheetKey.currentState?.closeSheet();
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个包含双侧滑动菜单的Flutter应用。BothSideSheet组件被放置在ScaffoldbottomSheet属性中。通过点击应用栏上的菜单按钮(左侧是汉堡菜单图标,右侧是更多选项图标),可以打开相应的左侧或右侧菜单。每个菜单项都是一个ListTile,点击后会关闭菜单。

注意,这个插件的具体用法可能会随着版本的更新而有所变化,因此请参考官方文档和示例代码以确保最佳实践。

回到顶部