我正在尝试创建一个带按钮的小部件,每当按下该按钮时,会在其下方打开一个列表,填充该按钮下的所有空间。我用简单的Column
实现了它,类似这样:
class _MyCoolWidgetState extends State<MyCoolWidget> {
...
@override
Widget build(BuildContext context) {
return new Column(
children: <Widget>[
new MyButton(...),
isPressed ? new Expanded(
child: new SizedBox(
width: MediaQuery.of(context).size.width,
child: new MyList()
)
) : new Container()
]
)
}
}
这在很多情况下完全正常,但不是全部。
问题我创建此窗口小部件时,如果MyCoolWidget
放在Row
内,例如其他窗口小部件,请说其他窗口小部件{ {1}} s,列表受MyCoolWidget
暗示的宽度约束
我尝试使用Row
进行修复,但遗憾的是没有运气。
这个小部件与标签的不同之处在于它们可以放在小部件树的任何位置,当按下按钮时,列表将填满按钮下的所有空间,即使这意味着忽略了约束。
下图是我想要实现的目标,其中&#34; BUTTON1&#34;和&#34; BUTTON2&#34;或OverflowBox
中的MyCoolWidget
个:
修改:实际代码的片段
Row
我按如下方式使用它:
class _MyCoolWidgetState extends State<MyCoolWidget> {
bool isTapped = false;
@override
Widget build(BuildContext context) {
return new Column(
children: <Widget>[
new SizedBox(
height: 20.0,
width: 55.0,
child: new Material(
color: Colors.red,
child: new InkWell(
onTap: () => setState(() => isTapped = !isTapped),
child: new Text("Surprise"),
),
),
),
bottomList()
],
);
}
Widget comboList() {
if (isTapped) {
return new Expanded(
child: new OverflowBox(
child: new Container(
color: Colors.orange,
width: MediaQuery.of(context).size.width,
child: new ListView( // Random list
children: <Widget>[
new Text("ok"),
new Text("ok"),
new Text("ok"),
new Text("ok"),
new Text("ok"),
new Text("ok"),
new Text("ok"),
new Text("ok"),
new Text("ok"),
new Text("ok"),
new Text("ok"),
new Text("ok"),
new Text("ok"),
],
)
)
),
);
} else {
return new Container();
}
}
}
答案 0 :(得分:2)
从评论中可以看出,OP想要的是:
制作一个覆盖所有内容的弹出窗口,从屏幕上的按钮到屏幕底部,同时也可以水平填充,无论按钮在屏幕上的哪个位置。当按下按钮时,它也会切换打开/关闭。
如何做到这一点有几个选择;最基本的将使用Dialog&amp; showDialog,除了它有一些围绕SafeArea的问题,这使得困难。此外,OP要求按钮切换而不是按下对话框的任何地方(这是对话框的作用 - 或者对话框后面的块或触摸)。
这是如何做这样的事情的一个工作示例。完全免责声明 - 我并不是说这是一件好事,甚至不是一个好方法......但它是一种方式。
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
// We're extending PopupRoute as it (and ModalRoute) do a lot of things
// that we don't want to have to re-create. Unfortunately ModalRoute also
// adds a modal barrier which we don't want, so we have to do a slightly messy
// workaround for that. And this has a few properties we don't really care about.
class NoBarrierPopupRoute<T> extends PopupRoute<T> {
NoBarrierPopupRoute({@required this.builder});
final WidgetBuilder builder;
@override
Color barrierColor;
@override
bool barrierDismissible = true;
@override
String barrierLabel;
@override
Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
return new Builder(builder: builder);
}
@override
Duration get transitionDuration => const Duration(milliseconds: 100);
@override
Iterable<OverlayEntry> createOverlayEntries() sync* {
// modalRoute creates two overlays - the modal barrier, then the
// actual one we want that displays our page. We simply don't
// return the modal barrier.
// Note that if you want a tap anywhere that isn't the dialog (list)
// to close it, then you could delete this override.
yield super.createOverlayEntries().last;
}
@override
Widget buildTransitions(
BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
// if you don't want a transition, remove this and set transitionDuration to 0.
return new FadeTransition(opacity: new CurvedAnimation(parent: animation, curve: Curves.easeOut), child: child);
}
}
class PopupButton extends StatefulWidget {
final String text;
final WidgetBuilder popupBuilder;
PopupButton({@required this.text, @required this.popupBuilder});
@override
State<StatefulWidget> createState() => PopupButtonState();
}
class PopupButtonState extends State<PopupButton> {
bool _active = false;
@override
Widget build(BuildContext context) {
return new FlatButton(
onPressed: () {
if (_active) {
Navigator.of(context).pop();
} else {
RenderBox renderbox = context.findRenderObject();
Offset globalCoord = renderbox.localToGlobal(new Offset(0.0, context.size.height));
setState(() => _active = true);
Navigator
.of(context, rootNavigator: true)
.push(
new NoBarrierPopupRoute(
builder: (context) => new Padding(
padding: new EdgeInsets.only(top: globalCoord.dy),
child: new Builder(builder: widget.popupBuilder),
),
),
)
.then((val) => setState(() => _active = false));
}
},
child: new Text(widget.text),
);
}
}
class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new SafeArea(
child: new Container(
color: Colors.white,
child: new Column(children: [
new PopupButton(
text: "one",
popupBuilder: (context) => new Container(
color: Colors.blue,
),
),
new PopupButton(
text: "two",
popupBuilder: (context) => new Container(color: Colors.red),
)
]),
),
),
);
}
}
对于更多古怪的建议,您可以查找位置部分并查看this answer which describes how to create a child that isn't constrained by it's parent's position。
然而,你最终会这样做,最好不要将列表作为按钮的直接子项,因为很多东西都会依赖于孩子的大小并使其能够扩展到全屏大小很容易引起问题。