Reputation: 1202
So, i'm trying to update a value from parent widget or child widget. Here is the parent widget
class MainPage extends StatefulWidget {
@override
_MainPageState createState() => new _MainPageState();
}
class _MainPageState extends State<MainPage> {
GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey();
NotificationCounter _notificationCounter =
new NotificationCounter(initialCount: 0);
................
_fcm.configure(
onMessage: (Map<String, dynamic> message) async {
_notificationCounter.increment();
},
onLaunch: (Map<String, dynamic> message) async {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => NotificationPage()),
);
},
onResume: (Map<String, dynamic> message) async {
print("resume");
Navigator.push(
context,
MaterialPageRoute(builder: (context) => NotificationPage()),
);
},
);
}
Then i have this child statefulwidget
class CustomAppbar extends StatefulWidget implements PreferredSizeWidget {
final double height;
final String reqFrom;
int Notificationcounter;
CustomAppbar(
{Key key,
@required this.height,
@required this.reqFrom,
this.Notificationcounter})
: super(key: key);
@override
_CustomAppbarState createState() => new _CustomAppbarState();
@override
// TODO: implement preferredSize
Size get preferredSize => Size.fromHeight(height);
}
class _CustomAppbarState extends State<CustomAppbar> {
String userId = "", userFullname = "";
String appName, packageName, version = "", buildNumber;
NotificationCounter _notificationCounter = new NotificationCounter();
........
}
and here is my NotificationCounter
class NotificationCounter {
int initialCount = 0;
BehaviorSubject<int> _subjectCounter;
NotificationCounter({this.initialCount}) {
_subjectCounter = new BehaviorSubject<int>.seeded(
this.initialCount); //initializes the subject with element already
}
Observable<int> get counterObservable => _subjectCounter.stream;
void increment() {
initialCount++;
_subjectCounter.sink.add(initialCount);
}
void setValue(int newVal){
initialCount = newVal;
_subjectCounter.sink.add(newVal);
}
void decrement() {
initialCount--;
_subjectCounter.sink.add(initialCount);
}
void dispose() {
_subjectCounter.close();
}
}
Ok, inside the CustomAppbar
class i update the counter value with this
Future<dynamic> _CountNotification() async {
prefs = await SharedPreferences.getInstance();
try {
final response = await http.post(
Configuration.url + "api/countNotification",
body: {"userId": prefs.getString("userId")});
final JsonDecoder _decoder = new JsonDecoder();
var notificationCount = _decoder.convert(response.body);
setState(() {
_notificationCounter.setValue(notificationCount);
});
} catch (e) {
return null;
}
}
For an example it set the value to 5. Then , from MainPage()
there is new notification which will increment the value, but the value is not changing .
How can i fix it ? did i miss something ? thanks in advance and sorry for my english.
I'm following this https://medium.com/flutter-community/why-use-rxdart-and-how-we-can-use-with-bloc-pattern-in-flutter-a64ca2c7c52d
Upvotes: 3
Views: 2017
Reputation: 2341
Make NotificationCounter
singleton.
class NotificationCounter {
//Private Constructor
NotificationCounter._();
static NotificationCounter _instance;
factory NotificationCounter.instance() {
if (_instance == null) {
_instance = NotificationCounter._();
}
return _instance;
}
}
And use NotificationCounter.instance()
everywhere.
Use InheritedWidget
class InheritedNotificationCounter extends InheritedWidget {
final NotificationCounter notificationCounter;
InheritedNotificationCounter({this.notificationCounter, Widget child}) : super(child: child);
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
// TODO: https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html
return true;
}
static InheritedNotificationCounter of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(InheritedNotificationCounter) as InheritedNotificationCounter);
}
}
Contruct your MaterialApp like
final NotificationCounter notificationCounter = NotificationCounter() /*or NotificationCounter.instance() // Dependong on the way you implement it*/;
@override
Widget build(BuildContext context) {
return InheritedNotificationCounter(
notificationCounter: notificationCounter,
child: MaterialApp(
...
),
);
}
So, now all the widgets can access NotificationCounter
like
InheritedNotificationCounter.of(context).notificationCounter
Hope it helps.
As requested in comments
You can use BLOC here for managing notifications. FCM/NotificationService will send notifications to BLOC and all the widgets that need notifications can subscribe for the notifications. Sample implementation
import 'package:rxdart/rxdart.dart';
class LocalNotification {
final String type;
final Map data;
LocalNotification(this.type, this.data);
}
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();
}
}
import 'package:firebase_messaging/firebase_messaging.dart';
import 'notifications_bloc.dart';
class LocalNotificationService {
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
bool _started = false;
LocalNotificationService._internal();
static final LocalNotificationService instance = LocalNotificationService._internal();
// ********************************************************* //
// YOU HAVE TO CALL THIS FROM SOMEWHERE (May be main widget)
// ********************************************************* //
void start() {
if (!_started) {
_start();
_started = true;
_refreshToken();
}
}
void _refreshToken() {
_firebaseMessaging.getToken().then(_tokenRefresh, onError: _tokenRefreshFailure);
}
void _start() {
_firebaseMessaging.requestNotificationPermissions();
_firebaseMessaging.onTokenRefresh.listen(_tokenRefresh, onError: _tokenRefreshFailure);
_firebaseMessaging.configure(
onMessage: _onMessage,
onLaunch: _onLaunch,
onResume: _onResume,
);
}
void _tokenRefresh(String newToken) async {
print(" New FCM Token $newToken");
}
void _tokenRefreshFailure(error) {
print("FCM token refresh failed with error $error");
}
Future<void> _onMessage(Map<String, dynamic> message) async {
print("onMessage $message");
if (message['notification'] != null) {
final notification = LocalNotification("notification", message['notification'] as Map);
NotificationsBloc.instance.newNotification(notification);
return null;
}
if (message['data'] != null) {
final notification = LocalNotification("data", message['data'] as Map);
NotificationsBloc.instance.newNotification(notification);
return null;
}
}
Future<void> _onLaunch(Map<String, dynamic> message) {
print("onLaunch $message");
return null;
}
Future<void> _onResume(Map<String, dynamic> message) {
print("onResume $message");
return null;
}
}
Stream<LocalNotification> _notificationsStream;
@override
void initState() {
super.initState();
_notificationsStream = NotificationsBloc.instance.notificationsStream;
_notificationsStream.listen((notification) {
// TODO: Implement your logic here
// You might want to incement notificationCouter using above mentioned logic.
print('Notification: $notification');
});
}
@override
void dispose() {
_notificationsStream?.dispose();
}
Upvotes: 3