如何在showModalBottomSheet中创建内部导航?

4
在我的应用程序中,我正在尝试实现类似Badoo的排序/筛选showBottomModalSheet功能。我成功创建了2个单独的页面,可以在它们之间来回导航。然而,我面临的问题是showBottomModalSheet中的第二个页面。返回按钮正常工作,直到我尝试触摸模态框以外的区域,这会将我带回第一个页面。相反,它应该关闭模态框。 用户导航到排序用户模态框,显示showBottomModalSheet的第一页。当用户点击“显示性别”时,它会导航到第二个页面(具有不同的性别)。当按下返回按钮时,它会导航到第一页,直到完全关闭模态框。触摸模态框外部也会关闭模态框 我尝试过的最佳stackoverflow答案:

https://dev59.com/Fbzpa4cB1Zd3GeqPUfHy#63603685

我也尝试使用modal_bottom_sheet包,但没成功。https://pub.dev/packages/modal_bottom_sheet/example 大部分我的代码都在showBottomModalSheet函数中:
class Page1 extends StatefulWidget {
  const Page1({
    Key? key
  }) : super(key: key);
  @override
  _Page1State createState() => _Page1State();
}

class _Page1State extends State<Page1> {
  final GlobalKey<NavigatorState> navigatorKey = GlobalKey();
  int _currentView = 0;
  late List<Widget> pages;

  @override
  void initState() {
    pages = [
      page1(),
      page2(),
    ];
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
print("LOG build _currentView ${_currentView}");
    return pages[_currentView];


  }
  Widget page1() {
    return WillPopScope(
        onWillPop: () async {
          return true;
        },
        child: Container(
          decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.only(
                  topRight: Radius.circular(60), topLeft: Radius.circular(60))),
          height: 400,
          width: double.maxFinite,
          child: Center(
              child: Column(
            children: [
              Text("First page"),
              ElevatedButton(
                onPressed: () {
                  setState(() {
                    _currentView = 1;
                    print("LOG page1 _currentView ${_currentView}");
                  });
                },
                child: Text("tap to navigate to 2nd page"),
              ),
            ],
          )),
        ));
  }

  Widget page2() {
    return WillPopScope(
        child: Container(
          decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.only(
                  topRight: Radius.circular(60), topLeft: Radius.circular(60))),
          height: 400,
          width: double.maxFinite,
      child: Center(
            child: InkWell(
              onTap: () {
                setState(() {
                  _currentView = 0;
                  print("LOG page2 _currentView ${_currentView}");
                });
              },
              child: Text("tap to navigate to 1st screen"),
            ),
          ),
        ),
        onWillPop: () async {
          print("LOG currentView jot $_currentView");
          if (_currentView == 0) {
            return true;
          }

          setState(() {
            _currentView = 0;
          });

          return false;
        });
  }
}
1个回答

2

解决方案 1

使用标准的DraggableScrollableSheet或第三方小部件来完成,有很多选择。以下是一些来自https://pub.dev/的例子:

  1. awesome_select
  2. backdrop_modal_route
  3. bottom_sheet_expandable_bar
  4. bottom_sheet
  5. cupertino_modal_sheet
  6. modal_bottom_sheet
  7. just_bottom_sheet
  8. sheet

解决方案 2

如果您想手动完成,我建议使用Navigator.push/Navigator.pop而不是PageRouteBuilder,并设置barrierDismissible=true

效果如下图所示。请查看DartPad上的演示

Screenshot

以下是代码:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

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

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  void _show() async {
    await Navigator.of(context).push(
      PageRouteBuilder(
        opaque: false,
        barrierDismissible: true,
        pageBuilder: (_, __, ___) => const Page1(),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: _show,
        tooltip: 'Settings',
        child: const Icon(Icons.settings),
      ),
    );
  }
}

class Page1 extends StatefulWidget {
  const Page1({Key? key}) : super(key: key);

  @override
  State<Page1> createState() => _Page1State();
}

class _Page1State extends State<Page1> {
  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Align(
          alignment: Alignment.bottomCenter,
          child: Container(
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: const BorderRadius.only(
                  topRight: Radius.circular(60), topLeft: Radius.circular(60)),
              boxShadow: [
                BoxShadow(
                  color: Colors.grey.withOpacity(0.5),
                  spreadRadius: 5,
                  blurRadius: 7,
                  offset: const Offset(0, 3), // changes position of shadow
                ),
              ],
            ),
            height: 400,
            width: double.maxFinite,
            child: Center(
              child: Material(
                type: MaterialType.transparency,
                child: Column(
                  children: [
                    const Text("First page"),
                    ElevatedButton(
                      onPressed: () async {
                        final backButton =
                            await Navigator.of(context).push<bool?>(
                          PageRouteBuilder(
                            opaque: false,
                            barrierDismissible: true,
                            pageBuilder: (_, __, ___) => const Page2(),
                          ),
                        );

                        if (backButton == null || backButton == false) {
                          if (mounted) Navigator.of(context).pop();
                        }
                      },
                      child: const Text("tap to navigate to 2nd page"),
                    ),
                  ],
                ),
              ),
            ),
          ),
        ),
      ],
    );
  }
}

class Page2 extends StatelessWidget {
  const Page2({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Align(
      alignment: Alignment.bottomCenter,
      child: Container(
        decoration: const BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.only(
              topRight: Radius.circular(60), topLeft: Radius.circular(60)),
        ),
        height: 400,
        width: double.maxFinite,
        child: Center(
          child: Material(
            type: MaterialType.transparency,
            child: InkWell(
              onTap: () => Navigator.of(context).pop(true),
              child: const Text("tap to navigate to 1st screen"),
            ),
          ),
        ),
      ),
    );
  }
}

谢谢你的回答。可能听起来很挑剔,但是有没有一种方法可以保持bottomModalSheet的行为(例如'enableDrag'属性),并让手机的后退按钮从第二个屏幕返回到第一个屏幕? - Michael Lock
返回按钮应该正常工作。现在,拖动手势可以使用GestureDetector实现。 - lepsch
我还在答案中添加了一个第三方小部件列表,请查看。 - lepsch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接