global_notification_layer.dart 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. import 'dart:math';
  2. import 'package:calendar_view/notification_demo/notification_card.dart';
  3. import 'package:calendar_view/notification_demo/notification_controller.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:get/get.dart';
  6. /// 全局消息通知层
  7. class GlobalNotificationLayer extends StatefulWidget {
  8. const GlobalNotificationLayer({Key? key, required this.child})
  9. : super(key: key);
  10. final Widget child;
  11. @override
  12. GlobalNotificationLayerState createState() => GlobalNotificationLayerState();
  13. }
  14. class GlobalNotificationLayerState extends State<GlobalNotificationLayer> {
  15. late NotificationController notificationController;
  16. List<SystemNotification> notifications = [];
  17. final GlobalKey<AnimatedListState> _listKey = GlobalKey<AnimatedListState>();
  18. final ScrollController _scrollController = ScrollController();
  19. static const double _notificationWidth = 420;
  20. bool get _isShowCloseAll => notifications.length > 2;
  21. @override
  22. void initState() {
  23. super.initState();
  24. notificationController =
  25. Get.put<NotificationController>(NotificationController());
  26. notificationController.onReceiveNotification
  27. .addListener(_onReceiveNotification);
  28. }
  29. @override
  30. void dispose() {
  31. super.dispose();
  32. notificationController.onReceiveNotification
  33. .removeListener(_onReceiveNotification);
  34. }
  35. void _onReceiveNotification(e, SystemNotification notification) {
  36. _insertNotification(notifications.length, notification);
  37. }
  38. // 新增消息
  39. void _insertNotification(index, SystemNotification item) {
  40. if (_listKey.currentState is! AnimatedListState) return;
  41. notifications.insert(index, item);
  42. _listKey.currentState!.insertItem(index);
  43. setState(() {});
  44. }
  45. // 移除消息
  46. void _removeNotification(index) {
  47. if (_listKey.currentState is! AnimatedListState) return;
  48. final SystemNotification notificationToRemove = notifications[index];
  49. notifications.removeAt(index);
  50. _listKey.currentState!.removeItem(
  51. index,
  52. (context, animation) => _buildNotificationSizeOut(
  53. context, index, animation, notificationToRemove),
  54. duration: const Duration(milliseconds: 120));
  55. setState(() {});
  56. }
  57. /// 滑动式移除
  58. void _removeNotificationSliderOut(index) {
  59. if (_listKey.currentState is! AnimatedListState) return;
  60. final SystemNotification notificationToRemove = notifications[index];
  61. notifications.removeAt(index);
  62. _listKey.currentState!.removeItem(
  63. index,
  64. (context, animation) => _buildNotificationSliderOut(
  65. context, index, animation, notificationToRemove),
  66. duration: const Duration(milliseconds: 200));
  67. setState(() {});
  68. }
  69. /// 瞬间移除
  70. void _removeNotificationInstant(index) {
  71. if (_listKey.currentState is! AnimatedListState) return;
  72. final SystemNotification notificationToRemove = notifications[index];
  73. notifications.removeAt(index);
  74. _listKey.currentState!.removeItem(
  75. index,
  76. (context, animation) =>
  77. _buildNotificationToRemove(notificationToRemove),
  78. duration: const Duration(milliseconds: 0));
  79. setState(() {});
  80. }
  81. void _closeAll() {
  82. final animateFrame = min(notifications.length, 8);
  83. final int total = notifications.length;
  84. for (var i = 0; i < total; i++) {
  85. if (total - i > animateFrame) {
  86. _removeNotificationInstant(0);
  87. } else {
  88. Future.delayed(Duration(milliseconds: 100 * (i + animateFrame - total)),
  89. () {
  90. _removeNotificationSliderOut(0);
  91. });
  92. }
  93. }
  94. }
  95. @override
  96. Widget build(BuildContext context) {
  97. return Stack(
  98. children: [
  99. widget.child,
  100. Positioned(
  101. bottom: 0,
  102. right: 0,
  103. child: SizedBox(
  104. width: _notificationWidth,
  105. child: Align(
  106. alignment: Alignment.bottomRight,
  107. child: AnimatedList(
  108. key: _listKey,
  109. initialItemCount: notifications.length,
  110. controller: _scrollController,
  111. shrinkWrap: true,
  112. itemBuilder: _buildNotificationFromRightSliderIn,
  113. ),
  114. ),
  115. ),
  116. ),
  117. if (_isShowCloseAll) _buildCloseAllButton()
  118. // _buildCloseAllButton()
  119. ],
  120. );
  121. }
  122. // 从右侧划入
  123. Widget _buildNotificationFromRightSliderIn(
  124. BuildContext context, int index, Animation<double> animation) {
  125. return SlideTransition(
  126. position: Tween<Offset>(
  127. begin: const Offset(1, 0),
  128. end: Offset.zero,
  129. ).animate(CurvedAnimation(
  130. parent: animation,
  131. curve: Curves.fastOutSlowIn,
  132. reverseCurve: Curves.fastOutSlowIn.flipped)),
  133. child: SizeTransition(
  134. axis: Axis.vertical,
  135. sizeFactor: animation,
  136. child: _buildNotificationCard(index),
  137. ),
  138. );
  139. }
  140. /// 收缩移除
  141. Widget _buildNotificationSizeOut(BuildContext context, int index,
  142. Animation<double> animation, SystemNotification notification) {
  143. return SizeTransition(
  144. axis: Axis.vertical,
  145. sizeFactor: animation,
  146. child: _buildNotificationToRemove(notification),
  147. );
  148. }
  149. /// 滑动移除
  150. Widget _buildNotificationSliderOut(BuildContext context, int index,
  151. Animation<double> animation, SystemNotification notification) {
  152. return SlideTransition(
  153. position: Tween<Offset>(
  154. begin: const Offset(1, 0),
  155. end: Offset.zero,
  156. ).animate(CurvedAnimation(
  157. parent: animation,
  158. curve: Curves.fastOutSlowIn,
  159. reverseCurve: Curves.fastOutSlowIn.flipped)),
  160. child: SizeTransition(
  161. axis: Axis.vertical,
  162. sizeFactor: animation,
  163. child: _buildNotificationToRemove(notification),
  164. ),
  165. );
  166. }
  167. Widget _buildNotificationCard(int index) {
  168. return NotificationCard(
  169. notification: notifications[index],
  170. onClose: () {
  171. _removeNotification(index);
  172. },
  173. );
  174. }
  175. Widget _buildNotificationToRemove(SystemNotification notification) {
  176. return NotificationCard(
  177. notification: notification,
  178. onClose: () {},
  179. );
  180. }
  181. Widget _buildCloseAllButton() {
  182. return Positioned(
  183. right: 20,
  184. top: 20,
  185. child: SizedBox(
  186. height: 40,
  187. child: ElevatedButton.icon(
  188. style: ButtonStyle(
  189. backgroundColor: MaterialStateProperty.all(
  190. const Color.fromARGB(255, 238, 77, 77)),
  191. ),
  192. icon: const Icon(Icons.close),
  193. onPressed: () {
  194. _closeAll();
  195. },
  196. label: Text('全部关闭(${notifications.length})'),
  197. ),
  198. ),
  199. );
  200. }
  201. }