本文实例为大家分享了Flutter自定义弹窗Dialog效果的具体代码,供大家参考,具体内容如下
主要是基于系统的dialog机制做一些使用限制和自定义UI的优化
核心代码:
class CustomDialog {
static void show(BuildContext context, Widget Function(BuildContext ctx, void Function() dismiss)builder, {bool cancellable = false}) {
showDialog(
context: context,
barrierDismissible: cancellable,
builder: (ctx) {
return WillPopScope(
child: Dialog(
child: builder(ctx, () => Navigator.of(ctx).pop()),
backgroundColor: Colors.transparent,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(0.0))),
elevation: 0,
alignment: Alignment.center,
),
onWillPop: () async => cancellable,
);
},
);
}
static void showBottomSheet(BuildContext context, Widget Function(BuildContext ctx, void Function() dismiss)builder, {bool cancellable = true}) {
showModalBottomSheet(
context: context,
isDismissible: cancellable,
enableDrag: cancellable,
isScrollControlled: true,
builder: (BuildContext ctx) {
return WillPopScope(
child: builder(ctx, () => Navigator.of(ctx).pop()),
onWillPop: () async => cancellable,
);
},
//不设置会默认使用屏幕最大宽度而不是子组件宽度
constraints: const BoxConstraints(minWidth: 0, minHeight: 0, maxWidth: double.infinity, maxHeight: double.infinity),
backgroundColor: Colors.transparent,
);
}
}
使用:
import 'dart:async';
import 'package:flutter/material.dart';
class DialogTestPage extends StatefulWidget {
const DialogTestPage({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => _DialogTestState();
}
class _DialogTestState extends State<DialogTestPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: [
const SizedBox(height: 0, width: double.infinity,),
TextButton(
child: const Text("show dialog"),
onPressed: () => showCustomDialog(),
),
TextButton(
child: const Text("show delay dialog"),
onPressed: () => showDelayDialog(),
),
TextButton(
child: const Text("show sheet"),
onPressed: () => showSheetDialog(),
),
], mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.center,),
);
}
void showCustomDialog() {
CustomDialog.show(context, (context, dismiss) {
return Container(
width: 200,
height: 100,
color: Colors.white,
child: Center(child: TextButton(onPressed: () => dismiss(), child: const Text("POP")),),
);
});
}
void showSheetDialog() {
CustomDialog.showBottomSheet(context, (ctx, dismiss) {
return Container(
height: 300,
color: Colors.white,
child: Center(child: TextButton(onPressed: () => dismiss(), child: const Text("POP")),),
margin: const EdgeInsets.all(20),
);
});
}
void showDelayDialog() {
CustomDialog.show(context, (context, dismiss) {
//延时关闭
Timer(const Duration(seconds: 2), () => dismiss());
return Container(
width: 200,
height: 100,
color: Colors.yellow,
child: const Center(child: Text("等待"),),
);
}, cancellable: true);
}
}
其中show方法中使用到的Dialog默认使用material组件库的,如果觉得有不合理的尺寸约束,可替换我修改过的Dialog组件,有其他需求也可在此定制,为什么不直接放弃使用此组件?主要涉及到其路由跳转中的一些动画布局之类的。
class Dialog extends StatelessWidget {
const Dialog({
Key? key,
this.backgroundColor,
this.elevation,
this.insetAnimationDuration = const Duration(milliseconds: 100),
this.insetAnimationCurve = Curves.decelerate,
this.insetPadding = const EdgeInsets.symmetric(horizontal: 40.0, vertical: 24.0),
this.clipBehavior = Clip.none,
this.shape,
this.alignment,
this.child,
}) : super(key: key);
final Color? backgroundColor;
final double? elevation;
final Duration insetAnimationDuration;
final Curve insetAnimationCurve;
final EdgeInsets? insetPadding;
final Clip clipBehavior;
final ShapeBorder? shape;
final AlignmentGeometry? alignment;
final Widget? child;
static const RoundedRectangleBorder _defaultDialogShape =
RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0)));
static const double _defaultElevation = 24.0;
@override
Widget build(BuildContext context) {
final DialogTheme dialogTheme = DialogTheme.of(context);
final EdgeInsets effectivePadding = MediaQuery.of(context).viewInsets + (insetPadding ?? EdgeInsets.zero);
return AnimatedPadding(
padding: effectivePadding,
duration: insetAnimationDuration,
curve: insetAnimationCurve,
child: MediaQuery.removeViewInsets(
removeLeft: true,
removeTop: true,
removeRight: true,
removeBottom: true,
context: context,
child: Align(
alignment: alignment ?? dialogTheme.alignment ?? Alignment.center,
child: Material(
color: backgroundColor ?? dialogTheme.backgroundColor ?? Theme.of(context).dialogBackgroundColor,
elevation: elevation ?? dialogTheme.elevation ?? _defaultElevation,
shape: shape ?? dialogTheme.shape ?? _defaultDialogShape,
type: MaterialType.card,
clipBehavior: clipBehavior,
child: child,
),
),
),
);
}
}