load_state_layout.dart 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import 'package:cs_resources/generated/assets.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:get/get.dart';
  4. import 'package:cs_resources/constants/color_constants.dart';
  5. import 'my_load_image.dart';
  6. import 'my_text_view.dart';
  7. ///四种视图状态
  8. enum LoadState { State_Success, State_Error, State_Loading, State_Empty }
  9. ///根据不同状态来展示不同的视图
  10. class LoadStateLayout extends StatefulWidget {
  11. final LoadState state; //页面状态
  12. final Widget? successWidget; //成功视图
  13. final List<SliverMultiBoxAdaptorWidget>? successSliverWidget; //成功的滚动视图
  14. final VoidCallback? errorRetry; //错误事件处理
  15. String? errorMessage;
  16. LoadStateLayout({
  17. Key? key,
  18. this.state = LoadState.State_Loading, //默认为加载状态
  19. this.successWidget, //成功的布局 (二选一)
  20. this.successSliverWidget, //成功的滚动布局(二选一)
  21. this.errorMessage, //错误的信息展示
  22. this.errorRetry, //错误重试的事件
  23. }) : super(key: key);
  24. @override
  25. _LoadStateLayoutState createState() => _LoadStateLayoutState();
  26. }
  27. class _LoadStateLayoutState extends State<LoadStateLayout> {
  28. @override
  29. Widget build(BuildContext context) {
  30. if (widget.successSliverWidget != null) {
  31. //如果有 successSliverWidget 就使用 Slivers 的方式布局
  32. return CustomScrollView(
  33. slivers: _buildSlivers(),
  34. );
  35. } else {
  36. //如果没有有 successSliverWidget 就使用默认布局的方式布局
  37. return SizedBox(
  38. width: double.infinity,
  39. height: double.infinity,
  40. child: _buildWidget,
  41. );
  42. }
  43. }
  44. //Slivers的布局
  45. List<Widget> _buildSlivers() {
  46. return _buildListWidget;
  47. }
  48. ///根据不同状态来显示不同的视图 (默认布局)
  49. Widget get _buildWidget {
  50. switch (widget.state) {
  51. case LoadState.State_Success:
  52. return widget.successWidget ?? const SizedBox();
  53. case LoadState.State_Error:
  54. return _errorView;
  55. case LoadState.State_Loading:
  56. return _loadingView;
  57. case LoadState.State_Empty:
  58. return _emptyView;
  59. default:
  60. return _loadingView;
  61. }
  62. }
  63. ///根据不同状态来显示不同的视图 (CustomScrollView)
  64. List<Widget> get _buildListWidget {
  65. switch (widget.state) {
  66. case LoadState.State_Success:
  67. return widget.successSliverWidget != null
  68. ? widget.successSliverWidget!
  69. : widget.successWidget != null
  70. ? [widget.successWidget!]
  71. : [const SizedBox()];
  72. case LoadState.State_Error:
  73. return [widget.successSliverWidget != null ? _warpStateLayout(_errorView) : _errorView];
  74. case LoadState.State_Loading:
  75. return [widget.successSliverWidget != null ? _warpStateLayout(_loadingView) : _loadingView];
  76. case LoadState.State_Empty:
  77. return [widget.successSliverWidget != null ? _warpStateLayout(_emptyView) : _emptyView];
  78. default:
  79. return [widget.successSliverWidget != null ? _warpStateLayout(_loadingView) : _loadingView];
  80. }
  81. }
  82. //如果父布局是 CustomScrollView 则使用 SliverFillViewport 包裹状态布局
  83. Widget _warpStateLayout(Widget widget) {
  84. return SliverFillViewport(
  85. delegate: SliverChildBuilderDelegate(
  86. (context, index) {
  87. return widget;
  88. },
  89. childCount: 1,
  90. ),
  91. );
  92. }
  93. // ===================================== 真正的状态布局 ↓ =====================================
  94. ///加载中视图
  95. Widget get _loadingView {
  96. return Container(
  97. width: double.infinity,
  98. height: double.infinity,
  99. alignment: Alignment.center,
  100. child: Column(
  101. mainAxisSize: MainAxisSize.max,
  102. mainAxisAlignment: MainAxisAlignment.center,
  103. crossAxisAlignment: CrossAxisAlignment.center,
  104. children: [
  105. CircularProgressIndicator(
  106. strokeWidth: 3,
  107. valueColor: AlwaysStoppedAnimation(Color(0XFFD6E9F1)),
  108. ),
  109. MyTextView(
  110. 'Loading...'.tr,
  111. marginTop: 15,
  112. fontSize: 14,
  113. textColor: Color(0XFFD6E9F1),
  114. )
  115. ],
  116. ),
  117. );
  118. }
  119. ///错误视图
  120. Widget get _errorView {
  121. return Container(
  122. width: double.infinity,
  123. height: double.infinity,
  124. alignment: Alignment.center,
  125. padding: const EdgeInsets.only(bottom: 10),
  126. child: GestureDetector(
  127. onTap: widget.errorRetry,
  128. child: Column(
  129. mainAxisSize: MainAxisSize.max,
  130. crossAxisAlignment: CrossAxisAlignment.center,
  131. mainAxisAlignment: MainAxisAlignment.center,
  132. children: <Widget>[
  133. const MyAssetImage(Assets.baseServicePageLoadError, width: 141, height: 117.5, fit: BoxFit.contain),
  134. MyTextView(
  135. widget.errorMessage ?? 'Data loading failed! Please refresh and try again'.tr,
  136. marginTop: 18,
  137. fontSize: 14,
  138. textColor: Color(0XFFD6E9F1),
  139. ),
  140. ],
  141. )));
  142. }
  143. ///数据为空的视图
  144. Widget get _emptyView {
  145. return Container(
  146. width: double.infinity,
  147. height: double.infinity,
  148. alignment: Alignment.center,
  149. padding: const EdgeInsets.only(bottom: 10),
  150. child: Column(
  151. mainAxisSize: MainAxisSize.max,
  152. crossAxisAlignment: CrossAxisAlignment.center,
  153. mainAxisAlignment: MainAxisAlignment.center,
  154. children: <Widget>[
  155. const MyAssetImage(Assets.baseServicePageNoData, width: 123.5, height: 115.5, fit: BoxFit.contain),
  156. MyTextView(
  157. 'There is currently no content available'.tr,
  158. marginTop: 18,
  159. fontSize: 14,
  160. textColor: Color(0XFFD6E9F1),
  161. ),
  162. ],
  163. ),
  164. );
  165. }
  166. }