this is simple of implementation for StreamingSharedPreferences using Provider
Future<void> main() async {
final preferences = await StreamingSharedPreferences.instance;
final settings = MyAppSettings(preferences);
runApp(
Provider<MyAppSettings>.value(value: settings, child: MyApp()),
);
}
as i have another multiple Provider in application such as:
runApp(
MultiProvider(providers: [
Provider(builder: (_) => database.userTableDao),
Provider(builder: (_) => database.postsTableDao),
Provider(builder: (_) => database.postsTableDao),
Provider(
builder: (_) => ApiService.create(),
dispose: (_, ApiService) => service.client.dispose(),
)
], child: OKToast(child: MyHomePage())),
);
i'm not sure how can make StreamingSharedPreferences provider inside them, for example:
MultiProvider(providers: [
...
Provider<ApplicationSettings>.value(value: settings),
...
], child: OKToast(child: MyHomePage())),
in this code i used instance of StreamingSharedPreferences.instance into main function and after that i added inside MultiProvider, for example:
final settings = ApplicationSettings( await StreamingSharedPreferences.instance );
MultiProvider(providers: [
...
Provider( builder: (_) => settings ),
...
], child: OKToast(child: MyHomePage())),
Related
I tried to implement a FilterEventCubit that listens to the states of a LocationTrackerCubit and an EventLoaderCubit. I used the tutorial from Felix Angelov (https://bloclibrary.dev/#/fluttertodostutorial) as a template:
class EventFilterCubit extends Cubit<EventFilterState> {
final EventLoaderCubit eventLoaderCubit;
final UserCubit userCubit;
final LocationTrackerCubit locationTrackerCubit;
StreamSubscription? eventsSubscription;
StreamSubscription? locationSubscription;
EventFilterCubit(
this.eventLoaderCubit, this.userCubit, this.locationTrackerCubit)
: super(
eventLoaderCubit.state is EventsUpToDate &&
userCubit.state is UserUpToDate &&
// TODO: not working correctly
locationTrackerCubit.state is LocationLoadSuccess
? EventFilterState.filteredEventsLoadSuccess(
(eventLoaderCubit.state as EventsUpToDate).events,
EventFilter.initial(
LatLng(
(locationTrackerCubit.state as LocationLoadSuccess)
.location
.latitude,
(locationTrackerCubit.state as LocationLoadSuccess)
.location
.longitude,
),
),
)
: const EventFilterState.filteredEventsLoadInProgress(),
) {
eventsSubscription = eventLoaderCubit.stream.listen(
(state) {
if (state is EventsUpToDate) {
print("Eventloaderbloc updated events");
eventsUpdated((eventLoaderCubit.state as EventsUpToDate).events);
}
},
);
locationSubscription = locationTrackerCubit.stream.listen(
(state) {
if (state is LocationLoadSuccess) {
print("locationtrackercubit updated location");
locationUpdated(
(locationTrackerCubit.state as LocationLoadSuccess).location,
);
}
},
);
}
The EventFilterCubit then builds the feed with all the events.
When I build the app or do a hot restart everything works just fine. But it stops listening after the first state update, so anytime a new event is added or the events are reloaded, nothing happens in the UI and the updateEvents function is not triggered.
Here is also my EventLoaderCubit:
part 'event_loader_cubit.freezed.dart';
part 'event_loader_state.dart';
#injectable
class EventLoaderCubit extends Cubit<EventLoaderState> {
final IEventRepository eventRepository;
// TODO: Streams atm useless
final StreamController<List<Event>> _eventController =
StreamController<List<Event>>();
Stream<List<Event>> get eventStream => _eventController.stream;
EventLoaderCubit(this.eventRepository)
: super(
const EventLoaderState.loadInProgress(),
) {
refreshEvents();
}
Future<void> refreshEvents() async {
print("refresh events");
emit(const EventLoaderState.loadInProgress());
final eventsFailOrSuccess = await eventRepository.loadEvents();
eventsFailOrSuccess.fold(
(failure) => emit(
const EventLoaderState.loadFailure(
EventFailure.serverError(),
),
),
(events) {
_eventController.add(events);
// getIt<EventFilterCubit>().eventsUpdated(events);
emit(
EventLoaderState.eventsUpToDate(events),
);
},
);
}
}
And the provider initialization:
child: BlocProvider(
create: (context) => getIt<AuthCubit>()..initialized(),
child: BlocBuilder<AuthCubit, AuthState>(
builder: (context, state) {
if (state is Authenticated) {
return MultiBlocProvider(
providers: [
BlocProvider<LocationTrackerCubit>(
create: (context) => getIt<LocationTrackerCubit>(),
),
BlocProvider<UserCubit>(
create: (context) => getIt<UserCubit>(),
),
BlocProvider<EventLoaderCubit>(
create: (context) =>
// TODO: Doesn't execute refreshEvents()
getIt<EventLoaderCubit>(),
),
BlocProvider<EventFilterCubit>(
create: (context) => getIt<EventFilterCubit>(),
),
],
child: _materialApp(context, authedRouter),
Also, as it is written in the code, the refreshEvents() function doesn't run when the Cubit is injected.
I had also troubles with the getIt dependency injection and BloCs. I solved this by changing the bloc access to the context.read and now it works fine.
You could try something like:
return MultiBlocProvider(
providers: [
BlocProvider<UserCubit>(
lazy: false,
create: (context) => UserCubit(
getIt<IUserRepository>(),
),
),
BlocProvider<LocationTrackerCubit>(
lazy: false,
create: (context) => LocationTrackerCubit(),
),
BlocProvider<EventLoaderCubit>(
lazy: false,
create: (context) => EventLoaderCubit(
getIt<IEventRepository>(),
),
),
BlocProvider<EventFilterCubit>(
create: (context) => EventFilterCubit(
eventLoaderCubit:
BlocProvider.of<EventLoaderCubit>(context),
userCubit: BlocProvider.of<UserCubit>(context),
locationTrackerCubit:
BlocProvider.of<LocationTrackerCubit>(context),
),
),
],
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>(
...
I'm currently working on a Flutter app and I have some issue to write tests.
When I run my test I have the following exception :
"The following ProviderNotFoundException was thrown building Consumer(dirty):
Error: Could not find the correct Provider above this Consumer
Widget"
This is a piece of my main.dart
Widget build(BuildContext context) {
themeData = Theme.of(context);
return Consumer<AppThemeNotifier>(
builder: (BuildContext context, AppThemeNotifier value, Widget? child) {
customAppTheme = AppTheme.getCustomAppTheme(value.themeMode());
return MultiBlocProvider(
providers: [
BlocProvider<AuthentificationBloc>(
create: (context) => AuthentificationBloc(),
),
BlocProvider<RegisterBloc>(
create: (context) => RegisterBloc(),
),
BlocProvider<EventsBloc>(
create: (context) => EventsBloc(),
),
BlocProvider<UsersBloc>(
create: (context) => UsersBloc(),
),
],
And this is the test that I try to run
void main() {
Widget skeleton(widgetToTest)
{
return Consumer<AppThemeNotifier>(
builder: (BuildContext context, AppThemeNotifier value, Widget? child) {
return widgetToTest;
});
}
testWidgets('My login page contains two textfield to enter my credentials',
(WidgetTester tester) async {
await tester.pumpWidget(skeleton(Login2Screen()));
final textFormFieldFinder = find.byElementType(TextFormField);
expect(textFormFieldFinder, findsNWidgets(2));
});
}
I tried to create the "skeleton" function to add the Provider but it doesn't work...
Any help is really appreciate :)
You need to load AppThemeNotifier via ChangeNotifierProvider or any other suitable provider.
await tester.pumpWidget(
ChangeNotifierProvider<AppThemeProvider>(
create: (context) => AppThemeProvider(),
child: skeleton(Login2Screen()),
),
);
I'm writing an app using BLOC architecture and registered bloc providers like this in the main.dart:
runApp(MultiBlocProvider(providers: [
BlocProvider<OrderBloc>(
create: (context) {
return OrderBloc()..add(OrderInitialEvent());
},
),
BlocProvider<AuthenticationBloc>(
create: (context) {
return AuthenticationBloc(userService: userService)..add(AppStarted());
},
),
...
], child: MyApp()));
Now I need to use Provider approach along with BLOC but not sure how to register it? Is it possible? Thanks
You can just nest them:
runApp(MultiBlocProvider(providers: [
BlocProvider<OrderBloc>(
create: (context) {
return OrderBloc()..add(OrderInitialEvent());
},
),
BlocProvider<AuthenticationBloc>(
create: (context) {
return AuthenticationBloc(userService: userService)..add(AppStarted());
},
),
...
],
child:
MultiProvider(
providers: [
Provider<Something>(create: (_) => Something()),
Provider<SomethingElse>(create: (_) => SomethingElse()),
Provider<AnotherThing>(create: (_) => AnotherThing()),
],
child: MyApp(),
)));
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.