Provider trying to update my count when ever will I receive Notification? - flutter

I am unable to update my Ui
//// HERE I AM GETTING THE UPDATE DATA IN CONSOLE BUT UNABLT TO UPDATE INSIDE MY UI
My NotificationCountClass
class NotificationCount extends ChangeNotifier{
var count;
NotificationCount({
this.count =0,
});
addNotificationCount(){
count++;
notifyListeners();
print("Notification Count $count");
}
}
main : here i wrap the widget inside multiprovider so that i can use it any-where in my app
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => AppService()),
ChangeNotifierProvider(create: (context) => NotificationCount()),
],
child: Consumer<AppService>(
builder: (context, appService, child) {
return GetMaterialApp(
title: AppStrings.APP_TITLE,
theme: AppTheme.lightTheme,
darkTheme: AppTheme.dartTheme,
navigatorKey: GlobalVariable.navigatorKey,
supportedLocales: [
Locale('en'),
],
localizationsDelegates: [
CountryLocalizations.delegate,
],
themeMode: appService.isDarkMode ? ThemeMode.dark : ThemeMode.light,
initialRoute: AppRouter.SPLASH_SCREEN,
onGenerateRoute: AppRouter.router.generator,
// routes: {
// "/":(context) =>Home(),
// "/AppChat" : (context) => AppChat(),
// },
debugShowCheckedModeBanner: false,
// home: AppChat(),
);
},
),
);
// Using State with Consumer widget so that only required wiget rebuild
Consumer<NotificationCount>(
builder: (context, value, child) {
var count = value.count;
print("Count of Not : $count");
return Text(
"$count",
style: TextStyle(
color: Colors.white,
),
);
},
),
getting NotificationCount class with provider but still unable to update UI
final notificationCount = Provider.of<NotificationCount>(context , listen: false);

I could reproduce your issue on my side, and could fix it just by using the builder of MultiProvider instead of child.
Instead of
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => AppService()),
ChangeNotifierProvider(create: (context) => NotificationCount()),
],
child: Consumer<AppService>(
write somthing like:
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => AppService()),
ChangeNotifierProvider(create: (context) => NotificationCount()),
],
builder: (context, _) => Consumer<AppService>(
...

Related

Flutter Light and Dark mode not updating provider

I have a controller for theme changing like this
class ThemeProvider extends ChangeNotifier {
ThemeMode systemThemeMode = preferences.getBool(Keys.isDarkMode) == null
? ThemeMode.light
: preferences.getBool(Keys.isDarkMode) == true
? ThemeMode.dark
: ThemeMode.light;
bool isDarkMode =
SchedulerBinding.instance.window.platformBrightness.name == "dark"
? true
: false;
toggleTheme(bool isPro) {
if (isPro) {
isDarkMode = !isDarkMode;
preferences.setBool(Keys.isDarkMode, isDarkMode);
systemThemeMode = isDarkMode ? ThemeMode.dark : ThemeMode.light;
notifyListeners();
}
}
}
And this is my main.dart
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => UserController()),
ChangeNotifierProvider(create: (context) => TeamsController()),
ChangeNotifierProvider(create: (context) => MatchListController()),
ChangeNotifierProvider(create: (context) => NewsProvider()),
ChangeNotifierProvider(create: (context) => ThemeProvider()),
],
builder: (context, _) {
final themeProvider =
Provider.of<ThemeProvider>(context, listen: false);
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: LightDarkTheme.lightTheme,
darkTheme: LightDarkTheme.darkTheme,
title: 'CricketR Plus',
themeMode: themeProvider.systemThemeMode,
home: LoginScreen(),
);
});
I am updating value by toggleTheme and I can see its updating by print but theme is going to light to dark. But If I toggle and restart my app then its working perfectly fine.
With Provider.of<ThemeProvider>(context, listen: false) you specify that you don't want to listen to changes in the provider.
Instead, use this when set themeMode within MaterialApp:
themeMode: context.watch<ThemeProvider>().systemThemeMode,
You need to use Consumer widget for that
wrap your MaterialApp in it. Then, you can call the ThemeProvider inside the Consumer and update the themeMode of the MaterialApp whenever the theme is toggled.
Here is an example
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => UserController()),
ChangeNotifierProvider(create: (context) => TeamsController()),
ChangeNotifierProvider(create: (context) => MatchListController()),
ChangeNotifierProvider(create: (context) => NewsProvider()),
ChangeNotifierProvider(create: (context) => ThemeProvider()),
],
builder: (context, _) {
return Consumer<ThemeProvider>(
builder: (context, themeProvider, child) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: LightDarkTheme.lightTheme,
darkTheme: LightDarkTheme.darkTheme,
title: 'CricketR Plus',
themeMode: themeProvider.systemThemeMode,
home: LoginScreen(),
);
},
);
});

How can I clear the current state of my providers manually in Flutter app?

How can I clear the current state of my providers manually in my Flutter app? while I'm using MultiProvider
The use case I have is when a user signs out of my app then signs up as a new/different user the previous users' state is still stored in the providers.
it is cleared automatically when the app is restarted, however.
MultiProvider(
providers: ServiceProvider().providers,
child: Consumer<AppLocale>(builder: (BuildContext context, AppLocale locale, Widget? child) {
return MaterialApp(
title: 'Application',
localizationsDelegates: AppLocalizations.localizationsDelegates,
debugShowCheckedModeBanner: false,
navigatorKey: RouteService.navigatorKey,
scaffoldMessengerKey: RouteService.rootScaffoldMessengerKey,
supportedLocales: AppLocalizations.supportedLocales,
onGenerateRoute: Routes.generateRoute,
initialRoute: RoutePaths.SplashScreen,
onUnknownRoute: Routes.generateRoute,
navigatorObservers: <NavigatorObserver>[AppRouteObserver()],
locale: locale.appLocal,
theme: appTheme(),
// debugShowMaterialGrid: true,
// showPerformanceOverlay: true,
// checkerboardRasterCacheImages: true,
// checkerboardOffscreenLayers: true,
// showSemanticsDebugger: true,
);
}),
)
Below is my ServiceProvider class
class ServiceProvider {
final List<SingleChildWidget> _independentServices = <SingleChildWidget>[
// ignore: always_specify_types
Provider.value(value: null)
];
final List<SingleChildWidget> _dependentServices = <SingleChildWidget>[];
final List<SingleChildWidget> _uiConsumableProviders = <SingleChildWidget>[
// ignore: always_specify_types
ChangeNotifierProvider(
create: (BuildContext context) => AppLocale(),
),
// ignore: always_specify_types
ChangeNotifierProvider(
create: (BuildContext context) => UserDetailsProvider(),
),
// ignore: always_specify_types
ChangeNotifierProvider(
create: (BuildContext context) => AccountAddressProvider(),
),
// ignore: always_specify_types
ChangeNotifierProvider(
create: (BuildContext context) => ServiceProvidersProductsProvider(),
),
// ignore: always_specify_types
ChangeNotifierProvider(
create: (BuildContext context) => LookupValuesProvider(),
),
];
List<SingleChildWidget> get providers {
return <SingleChildWidget>[..._independentServices, ..._dependentServices, ..._uiConsumableProviders];
}
}

MultiRepositoryProvider doesn't instantiate Bloc

I recently started developing an app in Flutter, so I'm fairly new to the area. So I've been looking into using Blocs. However when I Instantiate my Bloc and my services everything works fine. That is, until I use MultiRepositoryProvider. I have 2 code snippets. The first one:
return RepositoryProvider<AuthenticationService>(
create: (context) {
return FakeAuthenticationService();
},
// Injects the Authentication BLoC
child: BlocProvider<AuthenticationBloc>(
create: (context) {
final authService = RepositoryProvider.of<AuthenticationService>(context);
return AuthenticationBloc(authService)..add(AppLoaded());
},
child: MaterialApp(
title: 'Authentication Demo',
theme: appTheme(),
home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (context, state) {
if (state is AuthenticationAuthenticated) {
// show home page
return HomePage(
user: state.user,
);
}
// otherwise show login page
return StartupPage();
},
),
)
),
);
This code works fine, but the second snippet which is exactly the same, except it utilized MultiRepositoryProvider doesn't work. Second code:
return MultiRepositoryProvider(
providers: [
RepositoryProvider<AuthenticationService>(
create: (context) => FakeAuthenticationService(),
child: BlocProvider<AuthenticationBloc>(
create: (context) {
final authService = RepositoryProvider.of<AuthenticationService>(context);
return AuthenticationBloc(authService)..add(AppLoaded());
},
),
)
],
child: MaterialApp(
title: 'Authentication Demo',
theme: appTheme(),
home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (context, state) {
if (state is AuthenticationAuthenticated) {
// show home page
return HomePage(
user: state.user,
);
}
// otherwise show login page
return StartupPage();
},
),
),
);
Now this second code gives me the error BlocProvider.of() called with a context that does not contain a Cubit of type AuthenticationBloc.
Does anyone know why this second code doesn't work?
I'm working on the same thing and I got an error but now resolved
return MultiRepositoryProvider(
providers: [
RepositoryProvider<TranslationRepository>(
create: (context) => TranslationRepository(),
),
RepositoryProvider<WeatherRepository>(
create: (context) => WeatherRepository(),
),
],
child: MultiBlocProvider(
providers: [
BlocProvider<WeatherBloc>(
create: (context) =>
WeatherBloc(context.read<WeatherRepository>()),
),
BlocProvider<ConnectivityBloc>(
create: (context) => ConnectivityBloc(),
),
BlocProvider<TranslationBloc>(
create: (context) =>
TranslationBloc(context.read<TranslationRepository>()),
),
],
child: MaterialApp(
title: 'Material App',
onGenerateRoute: router.generateRoute,
initialRoute: '/',
)));
First, in my create function I overrided the context with "_" but I got the same error.
Now with this snippet it works perfectly, just put the same context name as my providers before

Some events added in the MultiBlocProvider doesn't get dispatched Flutter

I have added a new bloc SyncBloc to a MultiBlocProvider (flutter_bloc package) that creates all the blocs needed for MapScreen and for some it also adds Events needed to present data in the screen itself.
The problem is that while location events (for LocationBloc) are added correctly from the MultiBlocProvider itself, sync events (for SyncBloc) are not. If I instead add them from MapScreen's MultiBlocListener as
BlocProvider.of<SyncBloc>(context).add(SyncLanguages());
they work as expected so looks like SyncBloc has been provided correctly.. Can you spot what I'm doing wrong with the newer SyncBloc or point me in the right direction?
As always thank you very much for your time and help.
This is the MultiBlocProvider in main():
#override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: [
const AppLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale('en', ''),
const Locale('it', ''),
const Locale('es', ''),
],
localeResolutionCallback:
(Locale locale, Iterable<Locale> supportedLocales) {
for (Locale supportedLocale in supportedLocales) {
if (supportedLocale.languageCode == locale.languageCode ||
supportedLocale.countryCode == locale.countryCode) {
return supportedLocale;
}
}
return supportedLocales.first;
},
debugShowCheckedModeBanner: false,
home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (context, state) {
if (state is Unauthenticated) {
return LoginScreen(userRepository: _userRepository);
}
if (state is Authenticated) {
return MultiBlocProvider(
providers: [
BlocProvider<TrackingBloc>(
create: (context) => TrackingBloc(),
),
BlocProvider<DirectionsBloc>(
create: (context) => DirectionsBloc(),
),
BlocProvider<GeoBloc>(
create: (context) => GeoBloc(),
),
BlocProvider<RouteBloc>(
create: (context) => RouteBloc(),
),
BlocProvider<SchedulerBloc>(
create: (context) => SchedulerBloc(),
),
BlocProvider<CheckerBloc>(
create: (context) => CheckerBloc(),
),
BlocProvider<LocationBloc>(
create: (context) => LocationBloc(
mapRepository: _mapRepository,
)
..add(GetLocationStream())
..add(GetLocation())
..add(GetIsoLocationUser())),
BlocProvider<SyncBloc>(
create: (context) =>
SyncBloc()..add(SyncLanguages())..add(SyncIcons())),
BlocProvider<AlertBloc>(create: (context) {
return AlertBloc(
alertRepository: _alertRepository,
);
}),
],
child: MapScreen(
// mapRepository: _mapRepository,
user: state.user,
// alertRepository: FirebaseAlertRepository(),
),
);
}
return SplashScreen();
},
),
navigatorObservers: [
FirebaseAnalyticsObserver(analytics: analytics),
],
);
SyncEvent:
abstract class SyncEvent {
const SyncEvent();
#override
List<Object> get props => [];
}
class SyncLanguages extends SyncEvent {}
class SyncIcons extends SyncEvent {}
and SyncBloc:
class SyncBloc extends Bloc<SyncEvent, SyncState> {
#override
SyncState get initialState => InitialState();
Stream<SyncState> mapEventToState(SyncEvent event) async* {
if (event is SyncLanguages) {
print('SyncLanguages received');
}
if (event is SyncIcons) {
print('SyncIcons received');
}
}
}
The problem has to do with the BlocProvider's create method being lazy by default. So until the .of method is called BlocProvider doesn't create the bloc. To make it create the bloc immediately just set lazy: parameter to false.
BlocProvider<LocationBloc>(
lazy: false,
create: (context) => LocationBloc(
mapRepository: _mapRepository,
)
..add(GetLocationStream())
..add(GetLocation())
..add(GetIsoLocationUser())),
BlocProvider<SyncBloc>(
lazy: false,
create: (context) => SyncBloc()
..add(SyncLanguages())
..add(SyncIcons())),
This actually works, though AuthenticationBloc and LocationBloc events were sent even without the lazy parameter set to false. Still gotta check why that but I guess those two blocs are being created respectively by a BlocBuilder and a BlocListener. I'll edit the answer as soon as I find out for sure.

Flutter: Dynamic Initial Route

Dears,
I am using provider dart package which allows listeners to get notified on changes to models per se.
I am able to detect the change inside my main app root tree, and also able to change the string value of initial route however my screen is not updating. Kindly see below the code snippet and the comments lines:
void main() => runApp(_MyAppMain());
class _MyAppMain extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<UserProvider>.value(
value: UserProvider(),
),
ChangeNotifierProvider<PhoneProvider>.value(
value: PhoneProvider(),
)
],
child: Consumer<UserProvider>(
builder: (BuildContext context, userProvider, _) {
return FutureBuilder(
future: userProvider.getUser(),
builder: (BuildContext context, AsyncSnapshot<User> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
final User user = snapshot.data;
String initialScreen = LoginScreen.path;
// (1) I am able to get into the condition
if (user.hasActiveLogin()) {
initialScreen = HomeOneScreen.path;
}
return MaterialApp(
title: 'MyApp',
theme: ThemeData(
primarySwatch: Colors.green,
accentColor: Colors.blueGrey,
),
initialRoute: initialScreen,
// (2) here the screen is not changing...?
routes: {
'/': (context) => null,
LoginScreen.path: (context) => LoginScreen(),
RegisterScreen.path: (context) => RegisterScreen(),
HomeOneScreen.path: (context) => HomeOneScreen(),
HomeTwoScreen.path: (context) => HomeTwoScreen(),
RegisterPhoneScreen.path: (context) => RegisterPhoneScreen(),
VerifyPhoneScreen.path: (context) => VerifyPhoneScreen(),
},
);
},
);
},
),
);
}
}
Kindly Note the Below:
These are are paths static const strings
LoginScreen.path = "login"
RegisterScreen.path = "/register-screen"
HomeOneScreen.path = "home-one-screen"
HomeTwoScreen.path = "home-two-screen"
RegisterPhoneScreen.path = "/register-phone-screen"
VerifyPhoneScreen.path = "/verify-phone-screen"
What I am missing for dynamic initialRoute to work?
Many Thanks
According to this issue described on github issues it is not permissible to have initial route changes. At least this is what I understood. However what I did is that I replaced the initialRoute attribute with home attr. Thus this change mandates that initialScreen becomes a widget var.
The changes is shown below:
void main() => runApp(_MyAppMain());
class _MyAppMain extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<UserProvider>.value(
value: UserProvider(),
),
ChangeNotifierProvider<PhoneProvider>.value(
value: PhoneProvider(),
)
],
child: Consumer<UserProvider>(
builder: (BuildContext context, userProvider, _) {
return FutureBuilder(
future: userProvider.getUser(),
builder: (BuildContext context, AsyncSnapshot<User> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
final User user = snapshot.data;
// (1) This becomes a widget
Widget initialScreen = LoginScreen();
if (user.hasActiveLogin()) {
initialScreen = HomeOneScreen();
}
return MaterialApp(
title: 'MyApp',
theme: ThemeData(
primarySwatch: Colors.green,
accentColor: Colors.blueGrey,
),
home: initialScreen,
// (2) here the initial route becomes home attr.
routes: {
'/': (context) => null,
LoginScreen.path: (context) => LoginScreen(),
RegisterScreen.path: (context) => RegisterScreen(),
HomeOneScreen.path: (context) => HomeOneScreen(),
HomeTwoScreen.path: (context) => HomeTwoScreen(),
RegisterPhoneScreen.path: (context) => RegisterPhoneScreen(),
VerifyPhoneScreen.path: (context) => VerifyPhoneScreen(),
},
);
},
);
},
),
);
}
}
Also note on my RegistrationScreen on success api response I did Navigator.of(context).pop()
Thanks