| 471e09183e20ec9c20cca9b580fecc10 | |||||
| 471e09183e20ec9c20cca9b580fecc10 |
| part of 'noti_bloc.dart'; | part of 'noti_bloc.dart'; | ||||
| abstract class NotiEvent extends Equatable { | |||||
| abstract class NotiEvent { | |||||
| const NotiEvent(); | const NotiEvent(); | ||||
| @override | @override |
| part of 'noti_bloc.dart'; | part of 'noti_bloc.dart'; | ||||
| abstract class NotiState extends Equatable { | |||||
| abstract class NotiState { | |||||
| const NotiState(); | const NotiState(); | ||||
| @override | @override |
| import 'package:farm_tpf/custom_model/NotificationDTO.dart'; | import 'package:farm_tpf/custom_model/NotificationDTO.dart'; | ||||
| import 'package:farm_tpf/data/repository/repository.dart'; | import 'package:farm_tpf/data/repository/repository.dart'; | ||||
| import 'package:farm_tpf/presentation/custom_widgets/app_bar_widget.dart'; | |||||
| import 'package:farm_tpf/presentation/custom_widgets/bottom_loader.dart'; | import 'package:farm_tpf/presentation/custom_widgets/bottom_loader.dart'; | ||||
| import 'package:farm_tpf/presentation/custom_widgets/dash_line_widget.dart'; | import 'package:farm_tpf/presentation/custom_widgets/dash_line_widget.dart'; | ||||
| import 'package:farm_tpf/presentation/custom_widgets/loading_list_page.dart'; | import 'package:farm_tpf/presentation/custom_widgets/loading_list_page.dart'; | ||||
| import 'package:farm_tpf/presentation/screens/notification/update_count_noti_bloc.dart'; | import 'package:farm_tpf/presentation/screens/notification/update_count_noti_bloc.dart'; | ||||
| import 'package:farm_tpf/presentation/screens/plot_detail/sc_plot_detail.dart'; | import 'package:farm_tpf/presentation/screens/plot_detail/sc_plot_detail.dart'; | ||||
| import 'package:farm_tpf/presentation/screens/plot_detail/sc_plot_information.dart'; | |||||
| import 'package:farm_tpf/presentation/screens/plot_detail/sc_plot_parameter.dart'; | |||||
| import 'package:farm_tpf/utils/NotificationsBloc.dart'; | |||||
| import 'package:farm_tpf/utils/const_icons.dart'; | import 'package:farm_tpf/utils/const_icons.dart'; | ||||
| import 'package:farm_tpf/utils/pref.dart'; | import 'package:farm_tpf/utils/pref.dart'; | ||||
| import 'package:firebase_messaging/firebase_messaging.dart'; | |||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_bloc/flutter_bloc.dart'; | import 'package:flutter_bloc/flutter_bloc.dart'; | ||||
| import 'package:farm_tpf/utils/formatter.dart'; | import 'package:farm_tpf/utils/formatter.dart'; | ||||
| } | } | ||||
| class _NotificationScreenState extends State<NotificationScreen> { | class _NotificationScreenState extends State<NotificationScreen> { | ||||
| final FirebaseMessaging _firebaseMessaging = FirebaseMessaging(); | |||||
| var notiBloc = NotiBloc(repository: Repository()); | var notiBloc = NotiBloc(repository: Repository()); | ||||
| @override | |||||
| void initState() { | |||||
| super.initState(); | |||||
| _firebaseMessaging.configure( | |||||
| onMessage: (Map<String, dynamic> message) async { | |||||
| print("onMessage: $message"); | |||||
| try { | |||||
| notiBloc.add(OnRefreshFromNotification()); | |||||
| updateCountNotiBloc.getNotifications((data) {}, (err) {}); | |||||
| } catch (e) { | |||||
| print(e); | |||||
| } | |||||
| }); | |||||
| } | |||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| return BlocBuilder( | |||||
| cubit: notiBloc, | |||||
| builder: (context, state) { | |||||
| return HoldInfinityWidget( | |||||
| notiBloc: notiBloc, | |||||
| ); | |||||
| }); | |||||
| } | |||||
| } | |||||
| class HoldInfinityWidget extends StatelessWidget { | |||||
| final NotiBloc notiBloc; | |||||
| HoldInfinityWidget({@required this.notiBloc}); | |||||
| final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>(); | |||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| return Scaffold( | |||||
| key: _scaffoldKey, | |||||
| backgroundColor: Colors.white, | |||||
| body: BlocBuilder<NotiBloc, NotiState>( | |||||
| cubit: notiBloc, | |||||
| builder: (context, state) { | |||||
| return SafeArea( | |||||
| child: InfinityView( | |||||
| notiBloc: notiBloc, | |||||
| ), | |||||
| ); | |||||
| }, | |||||
| )); | |||||
| } | |||||
| } | |||||
| class InfinityView extends StatefulWidget { | |||||
| final NotiBloc notiBloc; | |||||
| InfinityView({@required this.notiBloc}); | |||||
| @override | |||||
| _InfinityViewState createState() => _InfinityViewState(); | |||||
| } | |||||
| class _InfinityViewState extends State<InfinityView> { | |||||
| final _scrollController = ScrollController(); | final _scrollController = ScrollController(); | ||||
| final _scrollThreshold = 250.0; | final _scrollThreshold = 250.0; | ||||
| List<NotificationDTO> currentItems = new List<NotificationDTO>(); | List<NotificationDTO> currentItems = new List<NotificationDTO>(); | ||||
| int currentPage = 0; | int currentPage = 0; | ||||
| bool currentHasReachedMax = true; | bool currentHasReachedMax = true; | ||||
| bool isUpdatingFromApi = true; | bool isUpdatingFromApi = true; | ||||
| Stream<LocalNotification> _notificationsStream; | |||||
| var pref = LocalPref(); | var pref = LocalPref(); | ||||
| var token; | var token; | ||||
| final maxScroll = _scrollController.position.maxScrollExtent; | final maxScroll = _scrollController.position.maxScrollExtent; | ||||
| final currentScroll = _scrollController.position.pixels; | final currentScroll = _scrollController.position.pixels; | ||||
| if (maxScroll - currentScroll < _scrollThreshold) { | if (maxScroll - currentScroll < _scrollThreshold) { | ||||
| widget.notiBloc.add(DataFetched()); | |||||
| notiBloc.add(DataFetched()); | |||||
| } | } | ||||
| }); | }); | ||||
| token = await pref.getString(DATA_CONST.TOKEN_KEY); | token = await pref.getString(DATA_CONST.TOKEN_KEY); | ||||
| @override | @override | ||||
| void initState() { | void initState() { | ||||
| getSharedPrefs(); | getSharedPrefs(); | ||||
| widget.notiBloc.add(DataFetched()); | |||||
| notiBloc.add(DataFetched()); | |||||
| _notificationsStream = NotificationsBloc.instance.notificationsStream; | |||||
| _notificationsStream.listen((notification) { | |||||
| updateCountNotiBloc.getNotifications((data) {}, (err) {}); | |||||
| print('Notification: $notification'); | |||||
| notiBloc.add(OnRefresh()); | |||||
| }); | |||||
| super.initState(); | super.initState(); | ||||
| } | } | ||||
| @override | @override | ||||
| Widget build(BuildContext context) { | Widget build(BuildContext context) { | ||||
| return BlocBuilder<NotiBloc, NotiState>( | return BlocBuilder<NotiBloc, NotiState>( | ||||
| cubit: widget.notiBloc, | |||||
| cubit: notiBloc, | |||||
| builder: (context, state) { | builder: (context, state) { | ||||
| if (state is NotiFailure) { | if (state is NotiFailure) { | ||||
| return Center(child: Text(state.errorString)); | return Center(child: Text(state.errorString)); | ||||
| style: TextStyle( | style: TextStyle( | ||||
| fontWeight: FontWeight.w500, fontSize: 22), | fontWeight: FontWeight.w500, fontSize: 22), | ||||
| )), | )), | ||||
| BlocBuilder<NotiBloc, NotiState>( | |||||
| cubit: widget.notiBloc, | |||||
| builder: (context, state) { | |||||
| if (state is NotiSuccess) { | |||||
| if (state.items.length > 0) { | |||||
| return FlatButton( | |||||
| onPressed: () { | |||||
| widget.notiBloc.add( | |||||
| MarkAllNotificationUpdate(status: "1")); | |||||
| }, | |||||
| child: Text('Đánh dấu đã đọc tất cả', | |||||
| style: TextStyle( | |||||
| fontWeight: FontWeight.normal, | |||||
| fontSize: 14, | |||||
| color: Colors.blue))); | |||||
| } else { | |||||
| return SizedBox(); | |||||
| } | |||||
| } else { | |||||
| return SizedBox(); | |||||
| } | |||||
| }, | |||||
| ) | |||||
| FlatButton( | |||||
| onPressed: () { | |||||
| notiBloc | |||||
| .add(MarkAllNotificationUpdate(status: "1")); | |||||
| }, | |||||
| child: Text('Đánh dấu đã đọc tất cả', | |||||
| style: TextStyle( | |||||
| fontWeight: FontWeight.normal, | |||||
| fontSize: 14, | |||||
| color: Colors.blue))) | |||||
| ], | ], | ||||
| )), | )), | ||||
| Expanded( | Expanded( | ||||
| return index >= state.items.length | return index >= state.items.length | ||||
| ? BottomLoader() | ? BottomLoader() | ||||
| : ItemInfinityWidget( | : ItemInfinityWidget( | ||||
| notiBloc: widget.notiBloc, | |||||
| notiBloc: notiBloc, | |||||
| unread: state.unread, | unread: state.unread, | ||||
| read: state.read, | read: state.read, | ||||
| currentItems: currentItems, | currentItems: currentItems, | ||||
| controller: _scrollController, | controller: _scrollController, | ||||
| ), | ), | ||||
| onRefresh: () async { | onRefresh: () async { | ||||
| widget.notiBloc.add(OnRefresh()); | |||||
| notiBloc.add(OnRefresh()); | |||||
| })) | })) | ||||
| ], | ], | ||||
| ); | ); |
| void getNotifications(Function(NotificationObjectDTO) onSuccess, | void getNotifications(Function(NotificationObjectDTO) onSuccess, | ||||
| Function(String) onError) async { | Function(String) onError) async { | ||||
| _repository.getNotifications().then((value) { | _repository.getNotifications().then((value) { | ||||
| print('get noti ok ${value.numberUnreadTotal}'); | |||||
| onSuccess(value); | onSuccess(value); | ||||
| _notiFetcher.sink.add(value); | _notiFetcher.sink.add(value); | ||||
| }).catchError((onError) { | }).catchError((onError) { |
| import 'dart:io'; | |||||
| import 'package:dio/dio.dart'; | |||||
| import 'package:farm_tpf/custom_model/NotificationDTO.dart'; | |||||
| import 'package:farm_tpf/data/repository/repository.dart'; | import 'package:farm_tpf/data/repository/repository.dart'; | ||||
| import 'package:farm_tpf/data/repository/user_repository.dart'; | |||||
| import 'package:farm_tpf/main.dart'; | |||||
| import 'package:farm_tpf/models/index.dart'; | import 'package:farm_tpf/models/index.dart'; | ||||
| import 'package:farm_tpf/presentation/custom_widgets/bottom_loader.dart'; | import 'package:farm_tpf/presentation/custom_widgets/bottom_loader.dart'; | ||||
| import 'package:farm_tpf/presentation/custom_widgets/loading_list_page.dart'; | import 'package:farm_tpf/presentation/custom_widgets/loading_list_page.dart'; | ||||
| import 'package:farm_tpf/presentation/screens/plot_detail/sc_plot_detail.dart'; | import 'package:farm_tpf/presentation/screens/plot_detail/sc_plot_detail.dart'; | ||||
| import 'package:farm_tpf/utils/const_assets.dart'; | import 'package:farm_tpf/utils/const_assets.dart'; | ||||
| import 'package:farm_tpf/utils/const_color.dart'; | import 'package:farm_tpf/utils/const_color.dart'; | ||||
| import 'package:farm_tpf/utils/const_common.dart'; | |||||
| import 'package:farm_tpf/utils/pref.dart'; | import 'package:farm_tpf/utils/pref.dart'; | ||||
| import 'package:firebase_messaging/firebase_messaging.dart'; | |||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_bloc/flutter_bloc.dart'; | import 'package:flutter_bloc/flutter_bloc.dart'; | ||||
| import 'package:farm_tpf/utils/const_string.dart'; | import 'package:farm_tpf/utils/const_string.dart'; | ||||
| import 'package:farm_tpf/utils/formatter.dart'; | import 'package:farm_tpf/utils/formatter.dart'; | ||||
| import 'package:get/get.dart'; | |||||
| import 'bloc/plot_bloc.dart'; | import 'bloc/plot_bloc.dart'; | ||||
| } | } | ||||
| class _PlotListScreenState extends State<PlotListScreen> { | class _PlotListScreenState extends State<PlotListScreen> { | ||||
| UserRepository _userRepository = UserRepository(); | |||||
| final FirebaseMessaging _firebaseMessaging = FirebaseMessaging(); | |||||
| var pref = LocalPref(); | var pref = LocalPref(); | ||||
| var token; | var token; | ||||
| var client; | var client; | ||||
| String pushkey = ""; | String pushkey = ""; | ||||
| String currentFullName = ""; | String currentFullName = ""; | ||||
| Future<Null> getSharedPrefs() async { | |||||
| token = await pref.getString(DATA_CONST.TOKEN_KEY); | |||||
| pushkey = await pref.getString(DATA_CONST.PUSH_KEY); | |||||
| currentFullName = await pref.getString(DATA_CONST.CURRENT_FULL_NAME); | |||||
| var options = BaseOptions(baseUrl: ConstCommon.baseUrl); | |||||
| options.headers["Authorization"] = "Bearer $token"; | |||||
| client = Dio(options); | |||||
| if (Platform.isIOS) { | |||||
| _firebaseMessaging | |||||
| .requestNotificationPermissions(IosNotificationSettings()); | |||||
| } | |||||
| _firebaseMessaging.configure( | |||||
| onMessage: (Map<String, dynamic> message) async { | |||||
| print("onMessage---: $message"); | |||||
| }, | |||||
| onBackgroundMessage: Platform.isIOS ? null : myBackgroundMessageHandler, | |||||
| onLaunch: (Map<String, dynamic> message) async { | |||||
| print("onLaunch: $message"); | |||||
| Future.delayed(Duration(milliseconds: 500), () { | |||||
| _notificationNavigateOnFCM(message); | |||||
| }); | |||||
| }, | |||||
| onResume: (Map<String, dynamic> message) async { | |||||
| print("onResume: $message"); | |||||
| _notificationNavigateOnFCM(message); | |||||
| }, | |||||
| ); | |||||
| _firebaseMessaging.requestNotificationPermissions( | |||||
| const IosNotificationSettings( | |||||
| sound: true, badge: true, alert: true, provisional: true)); | |||||
| _firebaseMessaging.onIosSettingsRegistered | |||||
| .listen((IosNotificationSettings settings) { | |||||
| print("Settings registered: $settings"); | |||||
| }); | |||||
| if (pushkey?.isEmpty ?? true) { | |||||
| _firebaseMessaging.getToken().then((String token) { | |||||
| assert(token != null); | |||||
| print("Push Messaging token: $token"); | |||||
| _userRepository.updateFcmToken(token).then((value) { | |||||
| print("send push key successful"); | |||||
| pref.saveString(DATA_CONST.PUSH_KEY, token); | |||||
| }); | |||||
| // client.put(""); | |||||
| }); | |||||
| } else { | |||||
| print("Don't need get push key"); | |||||
| } | |||||
| if (currentFullName?.isEmpty ?? true) { | |||||
| try { | |||||
| var currentUser = await _userRepository.getUser(); | |||||
| pref.saveString(DATA_CONST.CURRENT_FULL_NAME, currentUser.fullName); | |||||
| print("fullname: ${currentUser.fullName}"); | |||||
| } catch (e) { | |||||
| print("error: ${e.toString()}"); | |||||
| } | |||||
| } | |||||
| } | |||||
| _notificationNavigateOnFCM(Map<String, dynamic> message) { | |||||
| //parse data difference between Ios and Android | |||||
| var noti; | |||||
| if (Platform.isAndroid) { | |||||
| var data = message['data']; | |||||
| noti = NotificationDTO() | |||||
| ..contents = data['contents'] | |||||
| ..tbCropId = data['tbCropId'] | |||||
| ..type = data['type']; | |||||
| } else { | |||||
| noti = NotificationDTO.fromJson(message); | |||||
| } | |||||
| if (noti.contents == "ENV_UPDATE") { | |||||
| Get.to(PlotDetailScreen( | |||||
| cropType: noti.type, | |||||
| cropId: noti.tbCropId, | |||||
| initialIndex: 0, | |||||
| )); | |||||
| } else if (noti.contents == "PIC_UPDATE") { | |||||
| Get.to(PlotDetailScreen( | |||||
| cropType: noti.type, | |||||
| cropId: noti.tbCropId, | |||||
| initialIndex: 1, | |||||
| )); | |||||
| } else { | |||||
| //Go home | |||||
| } | |||||
| } | |||||
| @override | @override | ||||
| void initState() { | void initState() { | ||||
| super.initState(); | super.initState(); | ||||
| // getSharedPrefs(); | |||||
| } | } | ||||
| @override | @override |
| import 'package:badges/badges.dart'; | import 'package:badges/badges.dart'; | ||||
| import 'package:dio/dio.dart'; | import 'package:dio/dio.dart'; | ||||
| import 'package:farm_tpf/custom_model/NotificationDTO.dart'; | |||||
| import 'package:farm_tpf/custom_model/NotificationObjectDTO.dart'; | import 'package:farm_tpf/custom_model/NotificationObjectDTO.dart'; | ||||
| import 'package:farm_tpf/data/repository/user_repository.dart'; | import 'package:farm_tpf/data/repository/user_repository.dart'; | ||||
| import 'package:farm_tpf/presentation/screens/account/sc_account.dart'; | import 'package:farm_tpf/presentation/screens/account/sc_account.dart'; | ||||
| import 'package:farm_tpf/presentation/screens/notification/update_count_noti_bloc.dart'; | import 'package:farm_tpf/presentation/screens/notification/update_count_noti_bloc.dart'; | ||||
| import 'package:farm_tpf/presentation/screens/plot/sc_plot.dart'; | import 'package:farm_tpf/presentation/screens/plot/sc_plot.dart'; | ||||
| import 'package:farm_tpf/presentation/screens/plot_detail/sc_plot_detail.dart'; | import 'package:farm_tpf/presentation/screens/plot_detail/sc_plot_detail.dart'; | ||||
| import 'package:farm_tpf/utils/NotificationsBloc.dart'; | |||||
| import 'package:farm_tpf/utils/const_color.dart'; | import 'package:farm_tpf/utils/const_color.dart'; | ||||
| import 'package:farm_tpf/utils/const_common.dart'; | import 'package:farm_tpf/utils/const_common.dart'; | ||||
| import 'package:farm_tpf/utils/const_icons.dart'; | import 'package:farm_tpf/utils/const_icons.dart'; | ||||
| } | } | ||||
| class _TabbarScreenState extends State<TabbarScreen> { | class _TabbarScreenState extends State<TabbarScreen> { | ||||
| Stream<LocalNotification> _notificationsStream; | |||||
| UserRepository _userRepository = UserRepository(); | UserRepository _userRepository = UserRepository(); | ||||
| final FirebaseMessaging _firebaseMessaging = FirebaseMessaging(); | final FirebaseMessaging _firebaseMessaging = FirebaseMessaging(); | ||||
| var pref = LocalPref(); | var pref = LocalPref(); | ||||
| } | } | ||||
| _firebaseMessaging.configure( | _firebaseMessaging.configure( | ||||
| onMessage: (Map<String, dynamic> message) async { | onMessage: (Map<String, dynamic> message) async { | ||||
| print("onMessage---: $message"); | |||||
| print("onMessage--tabbar-: $message"); | |||||
| try { | try { | ||||
| print('call bloc update noti'); | |||||
| final String type = message['tbCropType']; | |||||
| final String contents = message['contents']; | |||||
| final String tbCropId = message['tbCropId']; | |||||
| updateCountNotiBloc.getNotifications((data) {}, (err) {}); | updateCountNotiBloc.getNotifications((data) {}, (err) {}); | ||||
| final notification = LocalNotification(type, contents, tbCropId); | |||||
| NotificationsBloc.instance.newNotification(notification); | |||||
| if (contents == "ENV_UPDATE") { | |||||
| if (Get.isSnackbarOpen) Get.back(); | |||||
| Get.snackbar(null, 'Thông số môi trường được cập nhật'); | |||||
| } else if (contents == "PIC_UPDATE") { | |||||
| if (Get.isSnackbarOpen) Get.back(); | |||||
| Get.snackbar(null, 'Người phụ trách được cập nhật'); | |||||
| } else { | |||||
| //Go home | |||||
| } | |||||
| } catch (e) { | } catch (e) { | ||||
| print('error'); | print('error'); | ||||
| print(e); | print(e); | ||||
| onLaunch: (Map<String, dynamic> message) async { | onLaunch: (Map<String, dynamic> message) async { | ||||
| print("onLaunch: $message"); | print("onLaunch: $message"); | ||||
| Future.delayed(Duration(milliseconds: 500), () { | Future.delayed(Duration(milliseconds: 500), () { | ||||
| updateCountNotiBloc.getNotifications((data) {}, (err) {}); | |||||
| _notificationNavigateOnFCM(message); | _notificationNavigateOnFCM(message); | ||||
| }); | }); | ||||
| }, | }, | ||||
| onResume: (Map<String, dynamic> message) async { | onResume: (Map<String, dynamic> message) async { | ||||
| print("onResume: $message"); | print("onResume: $message"); | ||||
| updateCountNotiBloc.getNotifications((data) {}, (err) {}); | |||||
| _notificationNavigateOnFCM(message); | _notificationNavigateOnFCM(message); | ||||
| }, | }, | ||||
| ); | ); | ||||
| getSharedPrefs(); | getSharedPrefs(); | ||||
| changeTabbar.initValue(); | changeTabbar.initValue(); | ||||
| updateCountNotiBloc.getNotifications((data) {}, (err) {}); | updateCountNotiBloc.getNotifications((data) {}, (err) {}); | ||||
| _notificationsStream = NotificationsBloc.instance.notificationsStream; | |||||
| _notificationsStream.listen((notification) { | |||||
| updateCountNotiBloc.getNotifications((data) {}, (err) {}); | |||||
| print('Notification: $notification'); | |||||
| }); | |||||
| } | } | ||||
| _notificationNavigateOnFCM(Map<String, dynamic> message) { | _notificationNavigateOnFCM(Map<String, dynamic> message) { | ||||
| try { | try { | ||||
| final int type = message['tbCropType']; | |||||
| final String type = message['tbCropType']; | |||||
| final String contents = message['contents']; | final String contents = message['contents']; | ||||
| final int tbCropId = message['tbCropId']; | |||||
| final String tbCropId = message['tbCropId']; | |||||
| if (contents == "ENV_UPDATE") { | if (contents == "ENV_UPDATE") { | ||||
| Get.to(PlotDetailScreen( | Get.to(PlotDetailScreen( | ||||
| cropType: type, | |||||
| cropId: tbCropId, | |||||
| cropType: int.parse(type), | |||||
| cropId: int.parse(tbCropId), | |||||
| initialIndex: 0, | initialIndex: 0, | ||||
| )); | )); | ||||
| } else if (contents == "PIC_UPDATE") { | } else if (contents == "PIC_UPDATE") { | ||||
| Get.to(PlotDetailScreen( | Get.to(PlotDetailScreen( | ||||
| cropType: type, | |||||
| cropId: tbCropId, | |||||
| cropType: int.parse(type), | |||||
| cropId: int.parse(tbCropId), | |||||
| initialIndex: 1, | initialIndex: 1, | ||||
| )); | )); | ||||
| } else { | } else { |
| import 'package:rxdart/rxdart.dart'; | |||||
| class LocalNotification { | |||||
| final String type; | |||||
| final String contents; | |||||
| final String tbCropId; | |||||
| LocalNotification(this.type, this.contents, this.tbCropId); | |||||
| } | |||||
| class NotificationsBloc { | |||||
| NotificationsBloc._internal(); | |||||
| static final NotificationsBloc instance = NotificationsBloc._internal(); | |||||
| final BehaviorSubject<LocalNotification> _notificationsStreamController = | |||||
| BehaviorSubject<LocalNotification>(); | |||||
| Stream<LocalNotification> get notificationsStream { | |||||
| return _notificationsStreamController; | |||||
| } | |||||
| void newNotification(LocalNotification notification) { | |||||
| _notificationsStreamController.sink.add(notification); | |||||
| } | |||||
| void dispose() { | |||||
| _notificationsStreamController?.close(); | |||||
| } | |||||
| } |