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 Cards:如何在 Flutter 中创建自定义卡片小部件