Flutter中如何实现自定义对话框?

Posted

技术标签:

【中文标题】Flutter中如何实现自定义对话框?【英文标题】:How to implement a Custom dialog box in flutter? 【发布时间】:2019-03-31 19:46:59 【问题描述】:

我是 Flutter 新手,需要创建一个图库应用,该应用需要自定义对话框来显示所选图像。我该如何实现?

【问题讨论】:

您需要粘贴您尝试过的代码。?对于初学者 - github.com/flutter/flutter/blob/master/examples/flutter_gallery/… 您可以使用 showDialog 小部件并在对话框中显示您的图像。 【参考方案1】:

一个通用的例子

showDialog(context: context,builder: (context) => _onTapImage(context)); // Call the Dialog.

_onTapImage(BuildContext context) 
    return Stack(
      alignment: Alignment.center,
      children: <Widget>[
        Image.network('https://via.placeholder.com/150',fit: BoxFit.contain,), // Show your Image
        Align(
          alignment: Alignment.topRight,
          child: RaisedButton.icon(
              color: Theme.of(context).accentColor,
              textColor: Colors.white,
              onPressed: () => Navigator.pop(context),
              icon: Icon(
                Icons.close,
                color: Colors.white,
              ),
              label: Text('Close')),
        ),
      ],
    );
  

【讨论】:

【参考方案2】:

使用 Dialog 类,它是 Flutter 中 AlertDialog 类的父类。对话框小部件有一个参数“shape”,您可以使用它来塑造对话框的边缘。

这是一个代码示例:

 Dialog errorDialog = Dialog(
  shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)), //this right here
  child: Container(
    height: 300.0,
    width: 300.0,
   
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Padding(
          padding:  EdgeInsets.all(15.0),
          child: Text('Cool', style: TextStyle(color: Colors.red),),
        ),
        Padding(
          padding: EdgeInsets.all(15.0),
          child: Text('Awesome', style: TextStyle(color: Colors.red),),
        ),
        Padding(padding: EdgeInsets.only(top: 50.0)),
        TextButton(onPressed: () 
          Navigator.of(context).pop();
        ,
            child: Text('Got It!', style: TextStyle(color: Colors.purple, fontSize: 18.0),))
      ],
    ),
  ),
);
showDialog(context: context, builder: (BuildContext context) => errorDialog);

【讨论】:

假设我们有自定义对话框类,我们在另一个 Statefulwidget onTap 中使用它: () showDialog( context: context, builder: (context) return CustomDialog(); ); ,我们是否必须将上下文传递给 customDialog。 (我认为如果对话框对该 statefulwidget 进行一些更改,这是否会使洞小部件再次重建)【参考方案3】:

在按钮上单击显示对话框为 -

showDialog(
        context: context,
        builder: (_) => LogoutOverlay(),
      );

带有两个按钮的对话框设计 -

class LogoutOverlay extends StatefulWidget 
      @override
      State<StatefulWidget> createState() => LogoutOverlayState();
    

    class LogoutOverlayState extends State<LogoutOverlay>
        with SingleTickerProviderStateMixin 
      AnimationController controller;
      Animation<double> scaleAnimation;

      @override
      void initState() 
        super.initState();

        controller =
            AnimationController(vsync: this, duration: Duration(milliseconds: 450));
        scaleAnimation =
            CurvedAnimation(parent: controller, curve: Curves.elasticInOut);

        controller.addListener(() 
          setState(() );
        );

        controller.forward();
      

      @override
      Widget build(BuildContext context) 
        return Center(
          child: Material(
            color: Colors.transparent,
            child: ScaleTransition(
              scale: scaleAnimation,
              child: Container(
                margin: EdgeInsets.all(20.0),
                  padding: EdgeInsets.all(15.0),
                  height: 180.0,

                  decoration: ShapeDecoration(
                      color: Color.fromRGBO(41, 167, 77, 10),
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(15.0))),
                  child: Column(
                    children: <Widget>[
                      Expanded(
                          child: Padding(
                        padding: const EdgeInsets.only(
                            top: 30.0, left: 20.0, right: 20.0),
                        child: Text(
                          "Are you sure, you want to logout?",
                          style: TextStyle(color: Colors.white, fontSize: 16.0),
                        ),
                      )),
                      Expanded(
                          child: Row(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: <Widget>[
                          Padding(
                            padding: const EdgeInsets.all(10.0),
                            child: ButtonTheme(
                                height: 35.0,
                                minWidth: 110.0,
                                child: RaisedButton(
                                  color: Colors.white,
                                  shape: RoundedRectangleBorder(
                                      borderRadius: BorderRadius.circular(5.0)),
                                  splashColor: Colors.white.withAlpha(40),
                                  child: Text(
                                    'Logout',
                                    textAlign: TextAlign.center,
                                    style: TextStyle(
                                        color: Colors.green,
                                        fontWeight: FontWeight.bold,
                                        fontSize: 13.0),
                                  ),
                                  onPressed: () 
                                    setState(() 
                                      Route route = MaterialPageRoute(
                                          builder: (context) => LoginScreen());
                                      Navigator.pushReplacement(context, route);
                                    );
                                  ,
                                )),
                          ),
                          Padding(
                            padding: const EdgeInsets.only(
                                left: 20.0, right: 10.0, top: 10.0, bottom: 10.0),
                            child:  ButtonTheme(
                                height: 35.0,
                                minWidth: 110.0,
                                child: RaisedButton(
                                  color: Colors.white,
                                  shape: RoundedRectangleBorder(
                                      borderRadius: BorderRadius.circular(5.0)),
                                  splashColor: Colors.white.withAlpha(40),
                                  child: Text(
                                    'Cancel',
                                    textAlign: TextAlign.center,
                                    style: TextStyle(
                                        color: Colors.green,
                                        fontWeight: FontWeight.bold,
                                        fontSize: 13.0),
                                  ),
                                  onPressed: () 
                                    setState(() 
                                      /* Route route = MaterialPageRoute(
                                          builder: (context) => LoginScreen());
                                      Navigator.pushReplacement(context, route);
                                   */ );
                                  ,
                                ))
                          ),
                        ],
                      ))
                    ],
                  )),
            ),
          ),
        );
      
    

【讨论】:

我收到错误“没有为类 LogoutOverlayState”定义方法 'LoginScreen'。...尝试将名称更正为现有方法的名称,或定义一个名为“LoginScreen”的方法。 " LoginScreen 是一个页面名称,它将在点击“注销”时重定向。【参考方案4】:

你只需将这个类放到你的项目中并调用它的方法来显示对话框。使用这个类你不需要到处写对话框代码

class DialogUtils 
  static DialogUtils _instance = new DialogUtils.internal();

  DialogUtils.internal();

  factory DialogUtils() => _instance;

  static void showCustomDialog(BuildContext context,
      @required String title, 
      String okBtnText = "Ok",
      String cancelBtnText = "Cancel",
      @required Function okBtnFunction) 
    showDialog(
        context: context,
        builder: (_) 
          return AlertDialog(
            title: Text(title),
            content: /* Here add your custom widget  */,
            actions: <Widget>[
              FlatButton(
                child: Text(okBtnText),
                onPressed: okBtnFunction,
              ),
              FlatButton(
                  child: Text(cancelBtnText),
                  onPressed: () => Navigator.pop(context))
            ],
          );
        );
  
 

你可以像这样调用这个方法:

GestureDetector(
      onTap: () =>
              DialogUtils.showCustomDialog(context,
          title: "Gallary",
          okBtnText: "Save",
          cancelBtnText: "Cancel",
          okBtnFunction: () => /* call method in which you have write your logic and save process  */),
      child: Container(),
)

【讨论】:

此解决方案完美运行。您能否详细说明为什么使用.internal?我想了解内部和工厂语义的背景 仅用于创建单例类@JulianOtto 为什么还需要持久化这个实例?那是 100% 无状态对话框,不需要保存它的实例,您可以在这里使用简单的静态方法 我尝试了这个解决方案,但我得到了这个错误Error: The argument type 'Function' can't be assigned to the parameter type 'void Function()?'.【参考方案5】:

屏幕截图(Null Safe):


代码:

只要调用这个方法:

void showCustomDialog(BuildContext context) 
  showGeneralDialog(
    context: context,
    barrierLabel: "Barrier",
    barrierDismissible: true,
    barrierColor: Colors.black.withOpacity(0.5),
    transitionDuration: Duration(milliseconds: 700),
    pageBuilder: (_, __, ___) 
      return Center(
        child: Container(
          height: 240,
          child: SizedBox.expand(child: FlutterLogo()),
          margin: EdgeInsets.symmetric(horizontal: 20),
          decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(40)),
        ),
      );
    ,
    transitionBuilder: (_, anim, __, child) 
      Tween<Offset> tween;
      if (anim.status == AnimationStatus.reverse) 
        tween = Tween(begin: Offset(-1, 0), end: Offset.zero);
       else 
        tween = Tween(begin: Offset(1, 0), end: Offset.zero);
      
  
      return SlideTransition(
        position: tween.animate(anim),
        child: FadeTransition(
          opacity: anim,
          child: child,
        ),
      );
    ,
  );

【讨论】:

【参考方案6】:

您现在可以使用 AlertDialog 并在内容中构建您的小部件。

showDialog(
context: context,
builder: (BuildContext context) 
   return AlertDialog(
     shape: RoundedRectangleBorder(
     borderRadius: BorderRadius.all(Radius.circular(20.0))),
     backgroundColor: Colors.green,
     content: Container(
         height:200,
         width:200,
         decoration: BoxDecoration(
                    image: DecorationImage(
                          image: FileImage(filepath),
                          fit: BoxFit.cover))),),

【讨论】:

【参考方案7】:
    警报对话框 自定义对话框 全屏对话框

参考:Flutter Alert Dialog to Custom Dialog | by Ishan Fernando | CodeChai | Medium

警报对话框

showDialog(
              context: context,
              builder: (BuildContext context)
                  return AlertDialog(
                    title: Text("Alert Dialog"),
                    content: Text("Dialog Content"),
                  );
              ,
              actions:[
                      FlatButton(
                        child: Text("Close"),
                        onPressed: ()
                          Navigator.of(context).pop();
                        ,
                      )
                    ],
            );

自定义对话框

showDialog(
        context: context,
        builder: (BuildContext context) 
          return Dialog(
            shape: RoundedRectangleBorder(
                borderRadius:
                    BorderRadius.circular(20.0)), //this right here
            child: Container(
              height: 200,
              child: Padding(
                padding: const EdgeInsets.all(12.0),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    TextField(
                      decoration: InputDecoration(
                          border: InputBorder.none,
                          hintText: 'What do you want to remember?'),
                    ),
                    SizedBox(
                      width: 320.0,
                      child: RaisedButton(
                        onPressed: () ,
                        child: Text(
                          "Save",
                          style: TextStyle(color: Colors.white),
                        ),
                        color: const Color(0xFF1BC0C5),
                      ),
                    )
                  ],
                ),
              ),
            ),
          );
        );

全屏对话框

showGeneralDialog(
      context: context,
      barrierDismissible: true,
      barrierLabel: MaterialLocalizations.of(context)
          .modalBarrierDismissLabel,
      barrierColor: Colors.black45,
      transitionDuration: const Duration(milliseconds: 200),
      pageBuilder: (BuildContext buildContext,
          Animation animation,
          Animation secondaryAnimation) 
        return Center(
          child: Container(
            width: MediaQuery.of(context).size.width - 10,
            height: MediaQuery.of(context).size.height -  80,
            padding: EdgeInsets.all(20),
            color: Colors.white,
            child: Column(
              children: [
                RaisedButton(
                  onPressed: () 
                    Navigator.of(context).pop();
                  ,
                  child: Text(
                    "Save",
                    style: TextStyle(color: Colors.white),
                  ),
                  color: const Color(0xFF1BC0C5),
                )
              ],
            ),
          ),
        );
      );

【讨论】:

【参考方案8】:

在我的情况下有一个可行的解决方案:

  Future<void> _showMyDialog() async 
  return showDialog<void>(
  context: context,
  barrierDismissible: false, // user must tap button!
  builder: (BuildContext context) 
    return AlertDialog(
      title: Text('AlertDialog Title'),
      content: SingleChildScrollView(
        child: Column(
          children: <Widget>[
            Text('This is a demo alert dialog.'),
            Text('Would you like to confirm this message?'),
          ],
        ),
      ),
      actions: <Widget>[
        TextButton(
          child: Text('Confirm'),
          onPressed: () 
            print('Confirmed');
            Navigator.of(context).pop();
          ,
        ),
        TextButton(
          child: Text('Cancel'),
          onPressed: () 
            Navigator.of(context).pop();
          ,
        ),
      ],
    );
  ,
);

【讨论】:

【参考方案9】:

希望这会有所帮助: 我在一个单独的类中创建了静态函数:

import 'package:flutter/material.dart';
import 'package:flutter_application_1/TGColors.dart';

class TGDialog 

  static doNothing()     // stub needed for Function     parameters

/// Returns an AlertDialog with most optional parameters 

 static AlertDialog dlg(  BuildContext context,
                      String      txtTitle      = 'WHAT? no title?'  ,
                       String      txtMsg        = 'WHAT? no content?',
                       String      txtBtn1       = 'CANCEL'           ,
                       String      txtBtn2       = 'OK'               , 
                       Function    funcBtn1      = doNothing          ,
                       Function    funcBtn2      = doNothing          ,
                       Color       colBackground = TGColors.Orange    ,
                       Color       colText       = TGColors.Indigo      ) 
 
  return
    AlertDialog(
        backgroundColor : colBackground,
        title           : Text(txtTitle),
        content         : Text(txtMsg),
        
        actions : <Widget>
        [
          TextButton(
            onPressed : () =>  funcBtn1(), Navigator.pop(context,'Cancel'),
            child     : Text(txtBtn1, style: TextStyle(color: colText)),
          ),

          TextButton(
            onPressed :() =>  funcBtn2(),Navigator.pop(context)  ,
            child     : Text(txtBtn2, style: TextStyle(color: colText)),
          ),
        ],
    );
  
 

一个例子:

Positioned( bottom: 1, left: (screenW / 5.6), 
            child    : FloatingActionButton(
            heroTag  : 'clear',

            onPressed :() => showDialog<String>
            (
              context : context,
             
              builder : (BuildContext context) => 
                ///////////////////////////////////////////////////////
                TGDialog.dlg( context, 
                              txtTitle : 'Clear Order?',
                              txtMsg   : 'This resets all item counts' , 
                              funcBtn2 : resetOrder) 
                ///////////////////////////////////////////////////////
            ),
           
            child     : const Text('clear\nall', textAlign: TextAlign.center),
            shape     : RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(40),
                        ),
          ),
        ), 

等等。 顺便说一句:我喜欢 CSS webcolors,所以我在一个单独的类中定义了它们 像这样:

import 'dart:ui';
/// Contains mainly web colors  (based on CSS)
/// 
/// Usage e.g:  ... = TGcolors.CornFlowerBlue
class TGColors

  static const  PrimaryColor =  Color(0xFF808080);
  static const  AliceBlue =  Color(0xFFF0F8FF);
  static const  AntiqueWhite = Color(0xFFFAEBD7);
  static const  Aqua = Color(0xFF00FFFF);
  static const  Aquamarine = Color(0xFF7FFFD4);  
  // etc.   

【讨论】:

以上是关于Flutter中如何实现自定义对话框?的主要内容,如果未能解决你的问题,请参考以下文章

Flutter 如何构建自定义 Widgets

Flutter Cards:如何在 Flutter 中创建自定义卡片小部件

Flutter BottomAppBar 自定义路径 + 贝塞尔曲线实现闲鱼底部导航

WINCC-如何使用自定义的对话框实现用户登录

Flutter - 自定义警报对话框未显示

Flutter-自定义警报对话框未显示