I'm using flutter_bloc and have created a sessionState that gets triggered when a user has successfully authenticated and the state saves the user object (did
).
I use a Cubit to change the state which then results in showing different screens in the ui. But whenever I want to access the did
object of the Verified sessionState which holds the user information, I have to create an if else statement in every file to check if the sessionState is Verified
and if that is true I can access the did object with state.did
.
I would be interested to know if it's possible to provide this did
object to all underlying widgets without passing it down manually every widget.
I would like to be able to just access the did object from the context so that I can access it everywhere below where it is provided.
My SessionState:
abstract class SessionState {}
class UnkownSessionState extends SessionState {}
class Unverified extends SessionState {}
class Verified extends SessionState {
Verified({required this.did});
final Did did;
}
The SessionCubit
launches states that I use to define the global state of the app and show different screens as a result.
class SessionCubit extends Cubit<SessionState> {
SessionCubit(this.commonBackendRepo) : super(UnkownSessionState()) {
attemptGettingDid();
}
final CommonBackendRepo commonBackendRepo;
final SecureStorage secureStorage = SecureStorage();
Future<void> attemptGettingDid() async {
try {
//more logic
emit(Unverified());
} catch (e) {
emit(Unverified());
}
}
void showUnverified() => emit(Unverified());
void showSession(Did did) {
emit(Verified(did: did));
}
}
This is how I currently access the did
object in every file:
BlocBuilder<SessionCubit, SessionState>(builder: (context, state) {
if (state is Verified) {
return CustomScrollView(
slivers: <Widget>[
SliverAppBar(
elevation: 0.0,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
title: Text(
state.did.username,
style: Theme.of(context).textTheme.headline5,
),
),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: CredentialDetailsView(),
))
],
);
} else {
return const Text("Unverified");
}
});
Edit:
That's how I would imagine the optimal solution to my scenario:
- Create if/else to check if state is verfied in one parent widget.
- If state is verfied create a Provider as a child with the 'did' Object so that the children of this Provider don't have to access the SessionState but can just access the provided
did
object.
Response to Robert Sandberg's answer
I also have an AppNavigator
that either start the authNavigator
or sessionNavigator
. So the sessionNavigator
is the parent widget of all widgets that get shown only when the state is Verified
. So I think this would be a great place to wrap the sessionNavigator with the Provider.value
, as you explained it.
I wrapped my SessionNavigator with the Provider.value
widget and provided the state object:
class AppNavigator extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<SessionCubit, SessionState>(
builder: (context, state) {
return Navigator(
pages: [
if (state is UnkownSessionState)
MaterialPage(child: StartupScreen()),
//show auth flow
if (state is Unverified)
MaterialPage(
child: BlocProvider(
create: (context) => AuthCubit(context.read<SessionCubit()),
child: AuthNavigator(),
)),
//show session flow
if (state is Verified)
MaterialPage(
child: Provider.value(
// here I can access the state.did object
value: state,
child: SessionNavigator(),
))
],
onPopPage: (route, result) => route.didPop(result),
);
},
);
}
}
For testing I tried to watch the provided value inside of my Home screen which is a child of the SessionNavigator:
...
class _HomeState extends State<Home> {
@override
Widget build(BuildContext context) {
//
final sessionState = context.watch<SessionState>();
return CustomScrollView(
slivers: <Widget>[
SliverAppBar(
floating: true,
expandedHeight: 60.0,
backgroundColor: Colors.white,
flexibleSpace: FlexibleSpaceBar(
title: Text(
// I can't access state.did because value is of type SessionState
sessionState.did,
style: Theme.of(context).textTheme.headline6,
),
titlePadding:
const EdgeInsetsDirectional.only(start: 20, bottom: 16)),
)
],
);
}
}
But because the type of my value is SessionState
I can't access the did object of the underlying Verified state. I though about providing the state.did object but I don't know how I would watch for that type.
I also got the error that my home screen couldn't find the Provider in the build context. Could that happen because my Home screen has a Navigator
as parent?
This is a visualization of my app architecture: