溢出容器顶部的可扩展按钮

Expandable button overflowing top of container

我正在尝试制作一个可扩展按钮,有点像可扩展 fab,只是它不是 fab,因为它不是浮动的。这是透视图的可扩展工厂:

不过,我想要实现的是拥有一个 独立 按钮,该按钮在其上方展开并带有一个菜单。自包含以粗体显示,因为我希望无需修改父结构即可轻松使用小部件。

因此,如果您将下面的代码复制粘贴到 dartpad 中,您会在底部看到一个黄色条。但是,如果您取消注释被注释的行,表示菜单展开,您会看到底部栏被推到顶部。

import 'package:flutter/material.dart';


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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Column(
            children: [
              Expanded(child: Container(color: Colors.purple)),
              MyWidget(),
            ]
          ),
        ),
      ),
    );
  }
}

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    

    return SizedOverflowBox(
      size: Size(double.infinity, 100),
      child: Stack(
        children: [
          Container(color: Colors.amber, height: 100),
//           Transform.translate(
//               offset: Offset(0, -400),
//               child: Container(color: Colors.lightBlue, height: 400, width: 80),
//             ),  
        ]
      )
    );
  }
}

所以我的问题是:

试试这个,运行 dartpad 中的这段代码。 它包含一个父项,三个子项,可以使用菜单按钮调用,

这段代码中使用的FloatingActionButton.extended可以替换成任何自定义的Widget,可以给onTap点击的方法,

我使用了简单的 widgets,让我知道您是在寻找类似的东西还是不同的东西。

import 'package:flutter/material.dart';

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',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'I am Parent'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool showButtons = false;
  var index = 0;
  List<Widget> childList = [Child1(), Child2(), Child3()];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Center(
          child: childList[index],
        ),
        floatingActionButton: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Visibility(
              visible: showButtons,
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  FloatingActionButton.extended(
                    heroTag: 'btn1',
                    onPressed: () {
                      setState(() {
                        index = 0;
                      });
                    },
                    label: Text(
                      "Sub Btn1",
                      style: TextStyle(color: Colors.black),
                    ),
                    elevation: 3,
                    backgroundColor: Colors.yellowAccent,
                  ),
                  Padding(
                      padding: EdgeInsets.only(top: 3),
                      child: FloatingActionButton.extended(
                        heroTag: 'btn1',
                        onPressed: () {
                          setState(() {
                            index = 1;
                          });
                        },
                        label: Text(
                          "Sub Btn2",
                          style: TextStyle(color: Colors.black),
                        ),
                        elevation: 3,
                        backgroundColor: Colors.yellowAccent,
                      )),
                  Padding(
                      padding: EdgeInsets.only(top: 3),
                      child: FloatingActionButton.extended(
                        heroTag: 'btn3',
                        onPressed: () {
                          setState(() {
                            index = 2;
                          });
                        },
                        label: Text(
                          "Sub Btn3",
                          style: TextStyle(color: Colors.black),
                        ),
                        elevation: 3,
                        backgroundColor: Colors.yellowAccent,
                      ))
                ],
              ),
            ),
            RaisedButton(
              onPressed: () {
                setState(() {
                  showButtons = !showButtons;
                });
              },
              child: Text("Self Contained"),
              shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(16)),
              color: Colors.yellow,
            ),
          ],
        ) // This trailing comma makes auto-formatting nicer for build methods.
        );
  }
}

class Child1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text("I am Child 1"),
    );
  }
}

class Child2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text("I am Child 2"),
    );
  }
}

class Child3 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text("I am Child 3"),
    );
  }
}

Overlay 和 OverlayEntry 可以帮助实现这一点:

import 'package:flutter/material.dart';


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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Column(
            children: [
              Expanded(child: Container(color: Colors.purple)),
              MyWidget(),
            ]
          ),
        ),
      ),
    );
  }
}

class MyWidget extends StatelessWidget {
  OverlayEntry? _overlayEntry;
  
  _hideMenu() {
    _overlayEntry?.remove();
  }
  
  _showMenu(BuildContext context) {
     final overlay = Overlay.of(context);
     _overlayEntry = OverlayEntry(
       builder: (ctx) => Stack(
         children: [
           GestureDetector(
             onTap: () => _hideMenu(),
             child: Container(color: Colors.grey.withAlpha(100)),
           ),
           Positioned(
             bottom: 100,
             left: 50,
             child: Container(color: Colors.pink, height: 200, width: 50,),
           ),
           
         ],
       )
       
     );
    overlay?.insert(_overlayEntry!);
  }
  
  @override
  Widget build(BuildContext context) {

    return GestureDetector(
      onTap: () => _showMenu(context),
      child: Container(color: Colors.amber, height: 100)
    );
  }
}