Flutter - 如何在弹出窗口中创建表单

60

我想在Flutter中创建一个类似下面图片的弹出窗口内嵌表单:

popup.

如何使用Flutter实现此功能?


使用 showDialog()showGeneralDialog() - CopsOnRoad
显示对话框(context: context,builder: (BuildContext context) { return Container(height: 10.0, width: 10.0, color: Colors.white,);}) 我已经这样做了,但是只显示了一个白色页面。我希望弹出窗口的大小是相对于我的页面而不是整个页面。 - geek man
1
你需要使用WrapColumn,并设置mainAxisSize: MainAxisSize.min - CopsOnRoad
5个回答

122

给你!showDialog的参数是一个WidgetBuilder,因此您可以返回任何小部件。

   import 'package:flutter/material.dart';

void main() {
  runApp(new MaterialApp(home: new MyApp()));
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Flutter"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            showDialog(
                context: context,
                builder: (BuildContext context) {
                  return AlertDialog(
                    content: Stack(
                      overflow: Overflow.visible,
                      children: <Widget>[
                        Positioned(
                          right: -40.0,
                          top: -40.0,
                          child: InkResponse(
                            onTap: () {
                              Navigator.of(context).pop();
                            },
                            child: CircleAvatar(
                              child: Icon(Icons.close),
                              backgroundColor: Colors.red,
                            ),
                          ),
                        ),
                        Form(
                          key: _formKey,
                          child: Column(
                            mainAxisSize: MainAxisSize.min,
                            children: <Widget>[
                              Padding(
                                padding: EdgeInsets.all(8.0),
                                child: TextFormField(),
                              ),
                              Padding(
                                padding: EdgeInsets.all(8.0),
                                child: TextFormField(),
                              ),
                              Padding(
                                padding: const EdgeInsets.all(8.0),
                                child: RaisedButton(
                                  child: Text("Submitß"),
                                  onPressed: () {
                                    if (_formKey.currentState.validate()) {
                                      _formKey.currentState.save();
                                    }
                                  },
                                ),
                              )
                            ],
                          ),
                        ),
                      ],
                    ),
                  );
                });
          },
          child: Text("Open Popup"),
        ),
      ),
    );
  }
}

希望这有所帮助!

2
从文档中得知:当前在树中具有此全局键的小部件的状态。 如果(1)树中没有与此全局键匹配的小部件,(2)该小部件不是StatefulWidget或关联的State对象不是T的子类型,则当前状态为null。 - Richie Permana
@AjayKumar 你能否请添加关闭按钮? - Mikel Tawfik
如果我们正在使用showDialog,那么仍然需要AlertDialog吗? - NullByte08
showDialog文档中可以看到:“_此函数接受一个builder,通常用于构建Dialog widget_”。换句话说,showDialog用于显示像AlertDialog这样的对话框小部件。 - Boaz
1
对于那些需要“overflow”参数的人,最新的更新中必须将其替换为clipBehavior: Clip.none 来源 - Filippo Mazza
这在当前版本的Flutter中已经无法使用了。 - Benny Bottema

16
这是一个示例代码,可以让你创建一个可以产生这种弹出窗口的按钮。
代码:
RaisedButton(
          child: Text("Open Popup"),
          onPressed: () {
            showDialog(
                context: context,
                builder: (BuildContext context) {
                  return AlertDialog(
                    scrollable: true,
                    title: Text('Login'),
                    content: Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: Form(
                        child: Column(
                          children: <Widget>[
                            TextFormField(
                              decoration: InputDecoration(
                                labelText: 'Name',
                                icon: Icon(Icons.account_box),
                              ),
                            ),
                            TextFormField(
                              decoration: InputDecoration(
                                labelText: 'Email',
                                icon: Icon(Icons.email),
                              ),
                            ),
                            TextFormField(
                              decoration: InputDecoration(
                                labelText: 'Message',
                                icon: Icon(Icons.message ),
                              ),
                            ),
                          ],
                        ),
                      ),
                    ),
                     actions: [
                      ElevatedButton(
                          child: Text("Submit"),
                          onPressed: () {
                            // your code
                          })
                    ],
                  );
                });
          },
        ),

输出:

enter image description here

要获得更多选项,您需要操作Form小部件、TextField小部件或RaisedButton小部件的属性,例如autovalidation、decoration、color等等... 如果这还不够,您可以使用Dialog小部件代替AlertDialog小部件。但在这种情况下,您需要将content属性替换为child,并进行必要的修改。

你会如何在actions中使用RaisedButton内的_formKey.validate? - angelcervera

9

截图(不使用任何第三方包):

enter image description here


代码:只需调用此方法:

void showDialogWithFields() {
  showDialog(
    context: context,
    builder: (_) {
      var emailController = TextEditingController();
      var messageController = TextEditingController();
      return AlertDialog(
        title: Text('Contact Us'),
        content: ListView(
          shrinkWrap: true,
          children: [
            TextFormField(
              controller: emailController,
              decoration: InputDecoration(hintText: 'Email'),
            ),
            TextFormField(
              controller: messageController,
              decoration: InputDecoration(hintText: 'Message'),
            ),
          ],
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: Text('Cancel'),
          ),
          TextButton(
            onPressed: () {
              // Send them to your email maybe?
              var email = emailController.text;
              var message = messageController.text;
              Navigator.pop(context);
            },
            child: Text('Send'),
          ),
        ],
      );
    },
  );
}

1
如果有人遇到“hasSize”错误:请使用以下内容替换ListView -> content: SingleChildScrollView(child: Column(....)) - Hardik
AlertDialog接受一个可滚动的参数(默认为false)。 - Naym

8

enter image description here

showDialog(
        context: context,
        builder: (BuildContext context) {
          return AlertDialog(
            contentPadding: EdgeInsets.zero,
            content: Stack(
              overflow: Overflow.visible,
              children: <Widget>[
                Positioned(
                  right: -15.0,
                  top: -15.0,
                  child: InkResponse(
                    onTap: () {
                      Navigator.of(context).pop();
                    },
                    child: CircleAvatar(
                      radius: 12,
                      child: Icon(Icons.close, size: 18,),
                      backgroundColor: Colors.red,
                    ),
                  ),
                ),
                Form(
                  key: _formKey,
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    children: <Widget>[
                      Container(
                        height: 60,
                        width: MediaQuery.of(context).size.width,
                        decoration: BoxDecoration(
                          color:Colors.yellow.withOpacity(0.2),
                          border: Border(
                            bottom: BorderSide(color: Colors.grey.withOpacity(0.3))
                          )
                        ),
                        child: Center(child: Text("Contact Me", style:TextStyle(color: Colors.black54, fontWeight: FontWeight.w700, fontSize: 20, fontStyle: FontStyle.italic, fontFamily: "Helvetica"))),
                      ),
                      Padding(
                        padding: EdgeInsets.all(20.0),
                        child: Container(
                          height: 50,
                          decoration: BoxDecoration(
                            border: Border.all(color: Colors.grey.withOpacity(0.2) )
                          ),
                            child: Row(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: [
                                Expanded(
                                  flex:1,
                                  child: Container(
                                    width: 30,
                                    child: Center(child: Icon(Icons.person, size: 35,color:Colors.grey.withOpacity(0.4))),
                                    decoration: BoxDecoration(
                                        border: Border(
                                            right: BorderSide(color: Colors.grey.withOpacity(0.2))
                                        )
                                    ),
                                  ),
                                ),
                                Expanded(
                                  flex: 4,
                                  child: TextFormField(
                                    decoration: InputDecoration(
                                        hintText: "Name",
                                        contentPadding: EdgeInsets.only(left:20),
                                        border: InputBorder.none,
                                        focusedBorder: InputBorder.none,
                                        errorBorder: InputBorder.none,
                                        hintStyle: TextStyle(color:Colors.black26, fontSize: 18, fontWeight: FontWeight.w500 )
                                    ),

                                  ),
                                )
                              ],
                            )
                        ),
                      ),
                      Padding(
                        padding: const EdgeInsets.all(20.0),
                        child: RaisedButton(
                          padding: EdgeInsets.zero,
                          child: Container(
                              width:MediaQuery.of(context).size.width,
                              height: 60,
                            decoration: BoxDecoration(
                               gradient: LinearGradient(
                                 begin: Alignment.topCenter,
                                 end: Alignment.bottomCenter,
                                 colors: [
                                   Color(0xffc9880b),
                                   Color(0xfff77f00),
                                 ]
                               )
                            ),
                            child: Center(child: Text("Submit", style: TextStyle(color:Colors.white70, fontSize: 20, fontWeight: FontWeight.w800),)),
                          ),
                          onPressed: () {
                            if (_formKey.currentState.validate()) {
                              _formKey.currentState.save();
                            }
                          },
                        ),
                      )
                    ],
                  ),
                ),
              ],
            ),
          );
        });

5
我尝试了以上所有答案,但是出现“没有material Widget错误”的提示。 我尝试在IconButton的位置使用任何小部件,但您必须将Scaffold的背景颜色设置为透明,并且也需要使用返回或取消按钮。
onTap: () {
                            showDialog(
                              context: context,
                              builder: (BuildContext context) {
                                return Scaffold(
                                  backgroundColor: Colors.transparent,
                                  body: IconButton(
                                    icon: Icon(Icons.ac_unit),
                                    onPressed: () {
                                      Navigator.pop(context);
                                    },
                                  ),
                                );
                              },
                            );
                          },

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