自定义弹窗(含底部抽屉)Dialog
bash
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
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(),
),
],
),
);
}
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,
margin: const EdgeInsets.all(20),
child: Center(
child:
TextButton(onPressed: () => dismiss(), child: const Text("POP")),
),
);
});
}
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);
}
}
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,
);
}
}
AlertDialog的属性
- title:标题
- titlePadding:标题内边距
- titleTextStyle:标题样式
- content:内容,推荐用SingleChildScrollView包裹
- contentPadding:EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0),内容内边距
- contentTextStyle:内容样式
- actions:按钮集合,可以放多个
- actionsPadding:EdgeInsets.zero,actions内边距
- buttonPadding:按钮内边距
- backgroundColor:背景色
- elevation:阴影
- shape:形状
- scrollable = false:
SimpleDialog
- title:标题
- titlePadding:EdgeInsets.fromLTRB(24.0, 24.0, 24.0, 0.0),标题内边距
- titleTextStyle:标题样式
- children:子节点
- contentPadding:EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 16.0),内容内边距
- backgroundColor:背景色
- elevation:阴影
- shape:形状
全局弹窗
- pubspec.yaml增加dio依赖包
bash
dio: any # dio依赖包
bash
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void _showLoadingDialog() {
showDialog(
context: context,
builder: (context) {
// 用Scaffold返回显示的内容,能跟随主题
return Scaffold(
backgroundColor: Colors.transparent, // 设置透明背影
body: Center( // 居中显示
child: Column( // 定义垂直布局
mainAxisAlignment: MainAxisAlignment.center, // 主轴居中布局,相关介绍可以搜下flutter-ui的内容
children: <Widget>[
// CircularProgressIndicator自带loading效果,需要宽高设置可在外加一层sizedbox,设置宽高即可
CircularProgressIndicator(),
SizedBox(
height: 10,
),
Text('loading'), // 文字
// 触发关闭窗口
GestureDetector(
child: Text('close dialog'),
onTap: () {
print('close');
},
),
],
), // 自带loading效果,需要宽高设置可在外加一层sizedbox,设置宽高即可
),
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: GestureDetector(
onTap: () {
_showLoadingDialog();
},
child: const Text(
'\n点击显示弹窗一\n',
),
),
),
);
}
}
接入dio 网络请求显示弹窗
bash
import 'package:dio/dio.dart' show Dio, InterceptorsWrapper;
import 'package:flutter_custom_widget/http/Loading.dart';
Dio? dio;
class Http {
static Dio? instance() {
if (dio != null) {
return dio;// 实例化dio
}
dio = Dio();
// 增加拦截器
dio?.interceptors.add(
InterceptorsWrapper(
// 接口请求前数据处理
onRequest: (options,handler) {
Loading.before("onRequest");
},
// 接口成功返回时处理
onResponse: (resp,handler) {
Loading.complete();
},
// 接口报错时处理
onError: ( error,handler) {
Loading.complete();
},
),
);
return dio;
}
/// get接口请求
/// path: 接口地址
static get(path) {
return instance()?.get(path);
}
}
bash
import 'package:flutter/material.dart';
class Loading {
static dynamic ctx;
static void before(text) {
// 请求前显示弹窗
showDialog(
context: ctx,
builder: (context) {
return Index(text: text);
},
);
}
static void complete() {
// 完成后关闭loading窗口
Navigator.of(ctx, rootNavigator: true).pop();
}
}
// 弹窗内容
class Index extends StatelessWidget {
final String? text;
Index({Key? key, @required this.text}):super(key: key);
@override
Widget build(BuildContext context) {
return Text('$text');
}
}
实现全局存储context
bash
@override
Widget build(BuildContext context) {
......
Loading.ctx = context; // 注入context
......
}
bash
class Loading {
static dynamic ctx;
static void before(text) {
// 请求前显示弹窗
// showDialog(context: ctx, builder: (context) {
// return Index(text:text);
// );
}
static void complete() {
// 完成后关闭loading窗口
// Navigator.of(ctx, rootNavigator: true).pop();
}
}
实现dio请求时loading
bash
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
Loading.ctx = context; // 注入context
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: GestureDetector(
onTap: () {
Http.get('https://blog.csdn.net/u013491829/article/details/137032263');
},
child: const Text(
'\n点击进行网络加载\n',
),
),
),
);
}
}
并发请求时loading处理
并发请求,loading只需要保证有一个在当前运行。接口返回结束,只需要保证最后一个完成时,关闭loading。
- 使用Set有排重作用,比较使用用来管理并发请求地址。通过Set.length控制弹窗与关闭窗口。
- 增加LoadingStatus判断是否已经有弹窗存在
- 修改onRequest/onResponse/onError入参
bash
import 'package:flutter/material.dart';
Set dict = Set();
bool loadingStatus = false;
class Loading {
static dynamic ctx;
static void before(uri, text) {
dict.add(uri); // 放入set变量中
// 已有弹窗,则不再显示弹窗, dict.length >= 2 保证了有一个执行弹窗即可,
if (loadingStatus == true || dict.length >= 2) {
return ;
}
loadingStatus = true; // 修改状态
// 请求前显示弹窗
showDialog(
context: ctx,
builder: (context) {
return Index(text: text);
},
);
}
static void complete(uri) {
dict.remove(uri);
// 所有接口接口返回并有弹窗
if (dict.length == 0 && loadingStatus == true) {
loadingStatus = false; // 修改状态
// 完成后关闭loading窗口
Navigator.of(ctx, rootNavigator: true).pop();
}
}
}
案例 切换到分支flutter_custom_widget