自定义弹窗(含底部抽屉)Dialog
class MyHomePage extends StatefulWidget {const MyHomePage({super.key, required this.title});final String title;@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {@overrideWidget 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依赖包
dio: any # dio依赖包
class MyHomePage extends StatefulWidget {const MyHomePage({super.key, required this.title});final String title;@overrideState<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,设置宽高即可),);},);}@overrideWidget 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 网络请求显示弹窗
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);}
}
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);@overrideWidget build(BuildContext context) {return Text('$text');}
}
实现全局存储context
@override
Widget build(BuildContext context) {......Loading.ctx = context; // 注入context......}
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
class MyHomePage extends StatefulWidget {const MyHomePage({super.key, required this.title});final String title;@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {@overrideWidget build(BuildContext context) {Loading.ctx = context; // 注入contextreturn 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入参
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