官方没有提供加载中的组件, 我们可以自己封装一个:

  1. import 'dart:async';
  2. import 'package:flutter/material.dart';
  3. import 'package:fluttertoast/fluttertoast.dart';
  4. // ignore: must_be_immutable
  5. class NetLoadingDialog extends StatefulWidget {
  6. String loadingText;
  7. bool outsideDismiss; // 是否可以点击消失, 默认false
  8. Function dismissCallback; // 消失后的回调
  9. Future<dynamic> requestCallBack; // 请求回调, 一定要传, 否则loading不会消失
  10. NetLoadingDialog(
  11. {Key key,
  12. this.loadingText = "loading...",
  13. this.outsideDismiss = false,
  14. this.dismissCallback,
  15. this.requestCallBack})
  16. : super(key: key);
  17. @override
  18. State<NetLoadingDialog> createState() => _LoadingDialog();
  19. }
  20. class _LoadingDialog extends State<NetLoadingDialog> {
  21. _dismissDialog() {
  22. if (widget.dismissCallback != null) {
  23. widget.dismissCallback();
  24. }
  25. // 隐藏Loading
  26. Navigator.of(context).pop();
  27. }
  28. @override
  29. void initState() {
  30. super.initState();
  31. if (widget.requestCallBack != null) {
  32. widget.requestCallBack.then((_) {
  33. // 请求成功后隐藏Loading
  34. Navigator.pop(context);
  35. }).catchError((err) {
  36. // 请求失败后隐藏Loading
  37. Navigator.pop(context);
  38. // 吐司提示错误原因
  39. Fluttertoast.showToast(
  40. msg: "请求失败: $err",
  41. toastLength: Toast.LENGTH_SHORT,
  42. gravity: ToastGravity.CENTER,
  43. timeInSecForIos: 1,
  44. backgroundColor: Colors.grey,
  45. textColor: Colors.white,
  46. fontSize: 16.0
  47. );
  48. });
  49. }
  50. }
  51. @override
  52. Widget build(BuildContext context) {
  53. return new GestureDetector(
  54. onTap: widget.outsideDismiss ? _dismissDialog : null, // 是否可以通过点击让loading消失
  55. child: Material(
  56. type: MaterialType.transparency,
  57. child: new Center(
  58. child: new SizedBox(
  59. width: 120.0,
  60. height: 120.0,
  61. child: new Container(
  62. decoration: ShapeDecoration(
  63. color: Color(0xffffffff),
  64. shape: RoundedRectangleBorder(
  65. borderRadius: BorderRadius.all(
  66. Radius.circular(8.0),
  67. ),
  68. ),
  69. ),
  70. child: new Column(
  71. mainAxisAlignment: MainAxisAlignment.center,
  72. crossAxisAlignment: CrossAxisAlignment.center,
  73. children: <Widget>[
  74. new CircularProgressIndicator(), // 加载动画
  75. new Padding(
  76. padding: const EdgeInsets.only(
  77. top: 20.0,
  78. ),
  79. child: new Text(
  80. widget.loadingText,
  81. style: new TextStyle(fontSize: 12.0),
  82. ),
  83. ),
  84. ],
  85. ),
  86. ),
  87. ),
  88. ),
  89. ),
  90. );
  91. }
  92. }

使用方式:

  1. // 重点就是实现返回Future的方法, 无论成功与否, 都要返回Future, 否则loading不会消失
  2. Future<dynamic> _register() {
  3. Future<dynamic> future = HttpUtil().resolve(Api.code);
  4. future.then((res) {
  5. print('成功啦');
  6. }).catchError((err) {
  7. print('失败啦');
  8. });
  9. return future;
  10. }
  11. // 通过showDialog调用显示Loading
  12. showDialog(
  13. context: context,
  14. barrierDismissible: false,
  15. builder: (_) {
  16. return new NetLoadingDialog(
  17. loadingText: '加载中...',
  18. requestCallBack: _register(), // 传入一个返回Future的方法
  19. );
  20. });

当然, loading图不一定要使用 CircularProgressIndicator, 也可以使用第三方库, 有一些很不错的库:

参考资料