当前位置:   article > 正文

flutter 弹窗之系列二

flutter 弹窗之系列二

自定义弹窗(含底部抽屉)Dialog

  1. class MyHomePage extends StatefulWidget {
  2. const MyHomePage({super.key, required this.title});
  3. final String title;
  4. @override
  5. State<MyHomePage> createState() => _MyHomePageState();
  6. }
  7. class _MyHomePageState extends State<MyHomePage> {
  8. @override
  9. Widget build(BuildContext context) {
  10. return Scaffold(
  11. body: Column(
  12. mainAxisAlignment: MainAxisAlignment.spaceAround,
  13. crossAxisAlignment: CrossAxisAlignment.center,
  14. children: [
  15. const SizedBox(
  16. height: 0,
  17. width: double.infinity,
  18. ),
  19. TextButton(
  20. child: const Text("show dialog"),
  21. onPressed: () => showCustomDialog(),
  22. ),
  23. TextButton(
  24. child: const Text("show delay dialog"),
  25. onPressed: () => showDelayDialog(),
  26. ),
  27. TextButton(
  28. child: const Text("show sheet"),
  29. onPressed: () => showSheetDialog(),
  30. ),
  31. ],
  32. ),
  33. );
  34. }
  35. void showCustomDialog() {
  36. CustomDialog.show(context, (context, dismiss) {
  37. return Container(
  38. width: 200,
  39. height: 100,
  40. color: Colors.white,
  41. child: Center(
  42. child:
  43. TextButton(onPressed: () => dismiss(), child: const Text("POP")),
  44. ),
  45. );
  46. });
  47. }
  48. void showSheetDialog() {
  49. CustomDialog.showBottomSheet(context, (ctx, dismiss) {
  50. return Container(
  51. height: 300,
  52. color: Colors.white,
  53. margin: const EdgeInsets.all(20),
  54. child: Center(
  55. child:
  56. TextButton(onPressed: () => dismiss(), child: const Text("POP")),
  57. ),
  58. );
  59. });
  60. }
  61. void showDelayDialog() {
  62. CustomDialog.show(context, (context, dismiss) {
  63. //延时关闭
  64. Timer(const Duration(seconds: 2), () => dismiss());
  65. return Container(
  66. width: 200,
  67. height: 100,
  68. color: Colors.yellow,
  69. child: const Center(
  70. child: Text("等待"),
  71. ),
  72. );
  73. }, cancellable: true);
  74. }
  75. }
  76. class CustomDialog {
  77. static void show(BuildContext context,
  78. Widget Function(BuildContext ctx, void Function() dismiss) builder,
  79. {bool cancellable = false}) {
  80. showDialog(
  81. context: context,
  82. barrierDismissible: cancellable,
  83. builder: (ctx) {
  84. return WillPopScope(
  85. child: Dialog(
  86. child: builder(ctx, () => Navigator.of(ctx).pop()),
  87. backgroundColor: Colors.transparent,
  88. shape: const RoundedRectangleBorder(
  89. borderRadius: BorderRadius.all(Radius.circular(0.0))),
  90. elevation: 0,
  91. alignment: Alignment.center,
  92. ),
  93. onWillPop: () async => cancellable,
  94. );
  95. },
  96. );
  97. }
  98. static void showBottomSheet(BuildContext context,
  99. Widget Function(BuildContext ctx, void Function() dismiss) builder,
  100. {bool cancellable = true}) {
  101. showModalBottomSheet(
  102. context: context,
  103. isDismissible: cancellable,
  104. enableDrag: cancellable,
  105. isScrollControlled: true,
  106. builder: (BuildContext ctx) {
  107. return WillPopScope(
  108. child: builder(ctx, () => Navigator.of(ctx).pop()),
  109. onWillPop: () async => cancellable,
  110. );
  111. },
  112. //不设置会默认使用屏幕最大宽度而不是子组件宽度
  113. constraints: const BoxConstraints(
  114. minWidth: 0,
  115. minHeight: 0,
  116. maxWidth: double.infinity,
  117. maxHeight: double.infinity),
  118. backgroundColor: Colors.transparent,
  119. );
  120. }
  121. }

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依赖包

 

  1. class MyHomePage extends StatefulWidget {
  2. const MyHomePage({super.key, required this.title});
  3. final String title;
  4. @override
  5. State<MyHomePage> createState() => _MyHomePageState();
  6. }
  7. class _MyHomePageState extends State<MyHomePage> {
  8. void _showLoadingDialog() {
  9. showDialog(
  10. context: context,
  11. builder: (context) {
  12. // 用Scaffold返回显示的内容,能跟随主题
  13. return Scaffold(
  14. backgroundColor: Colors.transparent, // 设置透明背影
  15. body: Center( // 居中显示
  16. child: Column( // 定义垂直布局
  17. mainAxisAlignment: MainAxisAlignment.center, // 主轴居中布局,相关介绍可以搜下flutter-ui的内容
  18. children: <Widget>[
  19. // CircularProgressIndicator自带loading效果,需要宽高设置可在外加一层sizedbox,设置宽高即可
  20. CircularProgressIndicator(),
  21. SizedBox(
  22. height: 10,
  23. ),
  24. Text('loading'), // 文字
  25. // 触发关闭窗口
  26. GestureDetector(
  27. child: Text('close dialog'),
  28. onTap: () {
  29. print('close');
  30. },
  31. ),
  32. ],
  33. ), // 自带loading效果,需要宽高设置可在外加一层sizedbox,设置宽高即可
  34. ),
  35. );
  36. },
  37. );
  38. }
  39. @override
  40. Widget build(BuildContext context) {
  41. return Scaffold(
  42. appBar: AppBar(
  43. backgroundColor: Theme.of(context).colorScheme.inversePrimary,
  44. title: Text(widget.title),
  45. ),
  46. body: Center(
  47. child: GestureDetector(
  48. onTap: () {
  49. _showLoadingDialog();
  50. },
  51. child: const Text(
  52. '\n点击显示弹窗一\n',
  53. ),
  54. ),
  55. ),
  56. );
  57. }
  58. }

接入dio 网络请求显示弹窗

  1. import 'package:dio/dio.dart' show Dio, InterceptorsWrapper;
  2. import 'package:flutter_custom_widget/http/Loading.dart';
  3. Dio? dio;
  4. class Http {
  5. static Dio? instance() {
  6. if (dio != null) {
  7. return dio;// 实例化dio
  8. }
  9. dio = Dio();
  10. // 增加拦截器
  11. dio?.interceptors.add(
  12. InterceptorsWrapper(
  13. // 接口请求前数据处理
  14. onRequest: (options,handler) {
  15. Loading.before("onRequest");
  16. },
  17. // 接口成功返回时处理
  18. onResponse: (resp,handler) {
  19. Loading.complete();
  20. },
  21. // 接口报错时处理
  22. onError: ( error,handler) {
  23. Loading.complete();
  24. },
  25. ),
  26. );
  27. return dio;
  28. }
  29. /// get接口请求
  30. /// path: 接口地址
  31. static get(path) {
  32. return instance()?.get(path);
  33. }
  34. }
  1. import 'package:flutter/material.dart';
  2. class Loading {
  3. static dynamic ctx;
  4. static void before(text) {
  5. // 请求前显示弹窗
  6. showDialog(
  7. context: ctx,
  8. builder: (context) {
  9. return Index(text: text);
  10. },
  11. );
  12. }
  13. static void complete() {
  14. // 完成后关闭loading窗口
  15. Navigator.of(ctx, rootNavigator: true).pop();
  16. }
  17. }
  18. // 弹窗内容
  19. class Index extends StatelessWidget {
  20. final String? text;
  21. Index({Key? key, @required this.text}):super(key: key);
  22. @override
  23. Widget build(BuildContext context) {
  24. return Text('$text');
  25. }
  26. }

实现全局存储context

  1. @override
  2. Widget build(BuildContext context) {
  3. ......
  4. Loading.ctx = context; // 注入context
  5. ......
  6. }
  1. class Loading {
  2. static dynamic ctx;
  3. static void before(text) {
  4. // 请求前显示弹窗
  5. // showDialog(context: ctx, builder: (context) {
  6. // return Index(text:text);
  7. // );
  8. }
  9. static void complete() {
  10. // 完成后关闭loading窗口
  11. // Navigator.of(ctx, rootNavigator: true).pop();
  12. }
  13. }

实现dio请求时loading

  1. class MyHomePage extends StatefulWidget {
  2. const MyHomePage({super.key, required this.title});
  3. final String title;
  4. @override
  5. State<MyHomePage> createState() => _MyHomePageState();
  6. }
  7. class _MyHomePageState extends State<MyHomePage> {
  8. @override
  9. Widget build(BuildContext context) {
  10. Loading.ctx = context; // 注入context
  11. return Scaffold(
  12. appBar: AppBar(
  13. backgroundColor: Theme.of(context).colorScheme.inversePrimary,
  14. title: Text(widget.title),
  15. ),
  16. body: Center(
  17. child: GestureDetector(
  18. onTap: () {
  19. Http.get('https://blog.csdn.net/u013491829/article/details/137032263');
  20. },
  21. child: const Text(
  22. '\n点击进行网络加载\n',
  23. ),
  24. ),
  25. ),
  26. );
  27. }
  28. }

并发请求时loading处理

并发请求,loading只需要保证有一个在当前运行。接口返回结束,只需要保证最后一个完成时,关闭loading。 

  • 使用Set有排重作用,比较使用用来管理并发请求地址。通过Set.length控制弹窗与关闭窗口。
  • 增加LoadingStatus判断是否已经有弹窗存在
  • 修改onRequest/onResponse/onError入参
  1. import 'package:flutter/material.dart';
  2. Set dict = Set();
  3. bool loadingStatus = false;
  4. class Loading {
  5. static dynamic ctx;
  6. static void before(uri, text) {
  7. dict.add(uri); // 放入set变量中
  8. // 已有弹窗,则不再显示弹窗, dict.length >= 2 保证了有一个执行弹窗即可,
  9. if (loadingStatus == true || dict.length >= 2) {
  10. return ;
  11. }
  12. loadingStatus = true; // 修改状态
  13. // 请求前显示弹窗
  14. showDialog(
  15. context: ctx,
  16. builder: (context) {
  17. return Index(text: text);
  18. },
  19. );
  20. }
  21. static void complete(uri) {
  22. dict.remove(uri);
  23. // 所有接口接口返回并有弹窗
  24. if (dict.length == 0 && loadingStatus == true) {
  25. loadingStatus = false; // 修改状态
  26. // 完成后关闭loading窗口
  27. Navigator.of(ctx, rootNavigator: true).pop();
  28. }
  29. }
  30. }

 案例 切换到分支flutter_custom_widget

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/341775
推荐阅读
相关标签
  

闽ICP备14008679号