Reputation: 137
Here is my InheritedWidget,
class AuthViewInherited extends InheritedWidget {
const AuthViewInherited({
super.key,
required super.child,
required this.data,
});
final AuthViewProviderState data;
static AuthViewProviderState of(BuildContext context) {
final result = context.dependOnInheritedWidgetOfExactType<AuthViewInherited>();
if (result == null) {
throw Exception("Could not find AuthViewInherited");
}
return result.data;
}
@override
bool updateShouldNotify(covariant AuthViewInherited oldWidget) {
return oldWidget.data.isBottomSheetOpen != data.isBottomSheetOpen;
}
}
Here is my state class,
class AuthViewProvider extends StatefulWidget {
const AuthViewProvider({super.key, required this.child});
final Widget child;
@override
State<AuthViewProvider> createState() => AuthViewProviderState();
}
class AuthViewProviderState extends State<AuthViewProvider> {
bool isBottomSheetOpen = false;
void changeBottomSheetOpen(bool v) {
setState(() {
isBottomSheetOpen = v;
});
}
@override
Widget build(BuildContext context) {
return AuthViewInherited(data: this, child: widget.child);
}
When I use it in my view like this, I see "false" in the first frame. But I can't see it anymore when I trigger changeBottomSheetOpen() in AuthView. It works when I change it as updateShouldNotify => true but I don't want to make it that. Why oldWidget.data.isBottomSheetOpen != data.isBottomSheetOpen doesn't work?
class AuthView extends StatefulWidget {
const AuthView({super.key});
@override
State<AuthView> createState() => _AuthViewState();
}
class _AuthViewState extends State<AuthView> {
@override
Widget build(BuildContext context) {
final authViewProvider = AuthViewInherited.of(context);
print(authViewProvider.isBottomSheetOpen);
// I expect rebuild in here when I call **changeBottomSheetOpen(true)**, but not rebuild.
Upvotes: 1
Views: 48
Reputation: 2660
My best guess is that oldWidget.data
and this.data
compared in updateShouldNotify
are always same instance of AuthViewProviderState
hence isBottomSheetOpen
will always have same value and updateShouldNotify
will never return true
.
Quickest possible fix – is to add a new field to AuthViewInherited
to reflect isBottomSheetOpen
. In other words, add new bool
field, and use it in updateShouldNotify
. Something like this:
class AuthViewInherited extends InheritedWidget {
const AuthViewInherited({
super.key,
required super.child,
required this.data,
required this.someBool,
});
final AuthViewProviderState data;
// new field
final bool someBool;
static AuthViewProviderState of(BuildContext context) {
final result =
context.dependOnInheritedWidgetOfExactType<AuthViewInherited>();
if (result == null) {
throw Exception("Could not find AuthViewInherited");
}
return result.data;
}
@override
bool updateShouldNotify(covariant AuthViewInherited oldWidget) {
// add one more condition to check. Or, maybe, check only someBool.
return oldWidget.data.isBottomSheetOpen != data.isBottomSheetOpen ||
oldWidget.someBool != someBool;
}
}
And also update AuthViewProviderState
:
class AuthViewProviderState extends State<AuthViewProvider> {
bool isBottomSheetOpen = false;
void changeBottomSheetOpen(bool v) {
setState(() {
isBottomSheetOpen = v;
});
}
@override
Widget build(BuildContext context) {
return AuthViewInherited(
data: this,
child: widget.child,
someBool: isBottomSheetOpen,
);
}
}
But, in my opinion, current option still has downsides, it's difficult to understand, and it's still kinda error-prone. I think it's better to separate AuthViewInherited
and AuthViewProviderState
so they do not reference each other. For this you can create another of
static method in State:
class AuthViewProviderState extends State<AuthViewProvider> {
bool isBottomSheetOpen = false;
void changeBottomSheetOpen(bool v) {
setState(() {
isBottomSheetOpen = v;
});
}
@override
Widget build(BuildContext context) {
return AuthViewInherited(
// we get rid of data parameter in InheritedWidget.
child: widget.child,
someBool: isBottomSheetOpen,
);
}
// This is new method to get AuthViewProviderState down the tree.
static AuthViewProviderState of(BuildContext context) {
final result = context.findAncestorStateOfType<AuthViewProviderState>();
if (result == null) {
throw Exception("Could not find AuthViewInherited");
}
return result;
}
}
Usage inside _AuthViewState
will look like this:
class AuthView extends StatefulWidget {
const AuthView({super.key});
@override
State<AuthView> createState() => _AuthViewState();
}
class _AuthViewState extends State<AuthView> {
// We will create new field here and populate it in didChangeDependencies, because it's like best practice and I do not see reason to not use it :)
late bool shouldBottomSheetBeOpen;
// Here we will populate shouldBottomSheetBeOpen. read about this method in docs, but in two words: it's created specifically to use with InheritedWidget.
@override
void didChangeDependencies() {
super.didChangeDependencies();
shouldBottomSheetBeOpen = AuthViewInherited.of(context).someBool;
}
@override
Widget build(BuildContext context) {
// This line is not needed anymore that's why I commented it.
// You should use shouldBottomSheetBeOpen as a state in build method.
// final authViewProvider = AuthViewInherited.of(context);
final authViewProviderState = AuthViewProviderState.of(context);
print('isBottomSheetOpen: ${shouldBottomSheetBeOpen}');
In that case, you still be able to access to changeBottomSheetOpen()
method from AuthViewProviderState
through authViewProviderState. changeBottomSheetOpen()
but now your setup will be more separated and it will be easier to manage it, in case you need to update something.
Upvotes: 0