network_error_screen.dart 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. import 'package:flutter/material.dart';
  2. import 'package:go_router/go_router.dart';
  3. import '../../core/l10n/app_localizations.dart';
  4. import '../../core/theme/app_colors.dart';
  5. /// 网络加载失败页面
  6. /// 用法:context.push('/network-error', extra: NetworkErrorExtra(...))
  7. class NetworkErrorScreen extends StatelessWidget {
  8. const NetworkErrorScreen({
  9. super.key,
  10. this.title = '',
  11. this.errorCode,
  12. this.onRetry,
  13. });
  14. /// AppBar 标题(调用方传入,例如"行情")
  15. final String title;
  16. /// 错误代码,如 NET_ERR_CONNECTION_TIMED_OUT
  17. final String? errorCode;
  18. /// 重新加载回调;为 null 时按钮执行 pop
  19. final VoidCallback? onRetry;
  20. @override
  21. Widget build(BuildContext context) {
  22. final cs = Theme.of(context).colorScheme;
  23. return Scaffold(
  24. appBar: AppBar(
  25. elevation: 0,
  26. leading: IconButton(
  27. icon: const Icon(Icons.chevron_left),
  28. onPressed: () => context.canPop() ? context.pop() : null,
  29. ),
  30. title: Text(
  31. title,
  32. style: TextStyle(
  33. color: cs.onSurface,
  34. fontSize: 16,
  35. fontWeight: FontWeight.w600,
  36. ),
  37. ),
  38. centerTitle: true,
  39. ),
  40. body: Center(
  41. child: Padding(
  42. padding: const EdgeInsets.symmetric(horizontal: 40),
  43. child: Column(
  44. mainAxisSize: MainAxisSize.min,
  45. children: [
  46. // WiFi 断开图标
  47. Container(
  48. width: 100,
  49. height: 100,
  50. decoration: BoxDecoration(
  51. color: const Color(0xFFF2F2F2),
  52. shape: BoxShape.circle,
  53. ),
  54. child: const Icon(
  55. Icons.wifi_off_rounded,
  56. size: 52,
  57. color: Color(0xFFBBBBBB),
  58. ),
  59. ),
  60. const SizedBox(height: 28),
  61. // 标题
  62. Text(
  63. AppLocalizations.of(context)!.pageLoadFailed,
  64. style: TextStyle(
  65. color: cs.onSurface,
  66. fontSize: 17,
  67. fontWeight: FontWeight.w600,
  68. ),
  69. ),
  70. const SizedBox(height: 10),
  71. // 副标题
  72. Text(
  73. AppLocalizations.of(context)!.networkErrorDesc,
  74. textAlign: TextAlign.center,
  75. style: TextStyle(
  76. color: cs.onSurface.withAlpha(153),
  77. fontSize: 13,
  78. height: 1.5,
  79. ),
  80. ),
  81. const SizedBox(height: 36),
  82. // 重新加载按钮(黑底白字)
  83. SizedBox(
  84. width: double.infinity,
  85. height: 50,
  86. child: FilledButton.icon(
  87. onPressed: onRetry ?? () => context.canPop() ? context.pop() : null,
  88. style: FilledButton.styleFrom(
  89. backgroundColor: AppColors.brand,
  90. foregroundColor: Colors.black,
  91. shape: const StadiumBorder(),
  92. ),
  93. icon: const Icon(Icons.refresh_rounded, size: 18),
  94. label: Text(
  95. AppLocalizations.of(context)!.reload,
  96. style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
  97. ),
  98. ),
  99. ),
  100. const SizedBox(height: 12),
  101. // 返回首页按钮(描边)
  102. SizedBox(
  103. width: double.infinity,
  104. height: 50,
  105. child: OutlinedButton(
  106. onPressed: () => context.go('/home'),
  107. style: OutlinedButton.styleFrom(
  108. foregroundColor: cs.onSurface,
  109. side: BorderSide(color: cs.outline),
  110. shape: const StadiumBorder(),
  111. ),
  112. child: Text(
  113. AppLocalizations.of(context)!.backHome,
  114. style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
  115. ),
  116. ),
  117. ),
  118. // 错误代码
  119. if (errorCode != null) ...[
  120. const SizedBox(height: 28),
  121. Text(
  122. AppLocalizations.of(context)!.errorCodeLabel(errorCode!),
  123. style: TextStyle(
  124. color: cs.onSurface.withAlpha(80),
  125. fontSize: 11,
  126. ),
  127. ),
  128. ],
  129. ],
  130. ),
  131. ),
  132. ),
  133. );
  134. }
  135. }