Is there a widget equivalent of the ChangeNotifierProvider widget of Provider in Riverpod?
The use case is to create a provider only when a page whose parent widget is ChangeNotifierProvider/or a page that has ChangeNotifierProvider in its widget tree has been pushed unto the Navigator stack using create. I would like the provider to be automatically disposed when the page is popped and the ChangeNotifierProvider widget is removed from the widget tree just like in Provider.
Riverpod has a ChangeNotifierProvider too, so you can use that.
As for the "I would like the provider to be automatically disposed when the page is popped", this functionality is instead implemented using autoDispose
So in the end, the syntax would be:
class MyNotifier extends ChangeNotifier {}
final myNotifierProvider = ChangeNotifierProvider.autoDispose<MyNotifier>((ref) {
return MyNotifier();
});
...
class MyWidget extends ConsumerWidget {
#override
Widget build(BuildContext context, ScopedReader watch) {
MyNotifier myNotifier = watch(myNotifierProvider);
}
}
With this, when all the widgets using MyNotifier are destroyed (aka when the route is popped), then MyNotifier will be disposed.
Related
Is it possible to update StateProvider from the Class outside the Widget?
Or I need to use another way?
My code
class SettingScreen extends ConsumerWidget with WidgetsBindingObserver {
This is the place I would like to update the state
Widget build(BuildContext context, WidgetRef ref) {
Update and read state working fine
}
}
var locationPermissionProvider = StateProvider<bool>((_) => true,);
I found a simple solution and it's working
removed WidgetRef & StateProvider
changed to StatefulWidget
update the state inside setState
and change the state in the following function inside the class
didChangeAppLifecycleState
I'm trying to access context so i can read my provider but since this lifecycle hook is out side the widget tree. it's not accessible. is there a way to get access to context?
I researched a little bit and finally discussed with narcodico from the flutter bloc community, so the credits are for him.
Therefore, mixin WidgetsBindingObserver on a state class, the context is available even in the overrides like didChangeAppLifecycleState since they are part of the state class.
Also, take in consideration to move to BlocProvider above the state widget.
Example
class HomePageProvider extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => getIt<InAppPurchasesBloc>(),
child: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _HomePageState();
}
}
class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
...
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
context
.read<InAppPurchasesBloc>()
.add(const InAppPurchasesEvent.getPurchaserInfo());
}
}
...
}
I am afraid you can't access context inside didChangeAppLifecycleState.
For anyone interested, you can save your scaffold state in a global key, and access the context from its current state.
You can use useEffect function, read more:
https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useEffect.html;
Widget build(BuildContext context) {
useEffect(() {
//what would you write in initState
},
);
You may consider using the Riverpod package instead of Provider. Riverpod is from the same author as Provider and considered the "better Provider", but with many improvements including Flutter independence, meaning it does not rely on a context to work, and you can use it almost the same way as provider.
Using Riverpod, along with Flutter Hooks, you can do something like:
// create a provider in a global context
final myProvider = Provider((ref) => myClass());
// access the provider inside your class
class MyWidget extends HookWidget{
//access the provider using a hook
final myClassProvider = useProvider(myProvider);
//... your logic
#override
Widget build (BuildContext context){/* ... build widget tree... */}
}
Consider this very useful and concise tutorial with how to use Riverpod with Flutter Hooks and StateNotifier, ChangeNotifier, etc...
I am trying to pass a User object from my stateful widget, to its state.
class NavBar extends StatefulWidget {
final User user;
NavBar({this.user});
#override
NavBarState createState() => NavBarState();
}
class NavBarState extends State<NavBar> {
int _currentIndex = 0;
final List<Widget> _children = [
CalendarWidget(),
HomeWidget(),
MessagingWidget(),
ProfilePage(user: user)
];
Widget build(BuildContext context) {
All other solutions say to use widget.user like ProfilePage(user: widget.user) but that throws error:
Only static members can be accessed in initializers.dart(implicit_this_reference_in_initializer)
How can I access User object in the state of NavBar so I can send it to ProfilePage()?
you can use widget.yourObjectsName to access StateFul widget's Object in its state
you can use any property of Stateful Widgets from its state by widget property of state which gives reference to state's Widget properties.
How do I update the state of a parent widget onTap of a child widget using provider
Currently what I am doing is pass a function from parent to child that calls the setState() function at the parent's end (Please refer the psuedocode below), but I'm looking for a more robust way of doing this.
class ParentWidget extends StatefulWidget {
#override
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
_refresh() {
setState(() {});
}
#override
Widget build(BuildContext context) {
return Container(
child: new ChildWidget(
notifyParent: _refresh(),
),
);
}
}
class ChildWidget extends StatelessWidget {
final Function notifyParent;
ChildWidget({#required this.notifyParent});
#override
Widget build(BuildContext context) {
return Container(
child: new FlatButton(
onPressed: notifyParent(), child: new Text("Update Parent")),
);
}
}
This is the simplest way I could represent the problem, the setState() in the parent (in the actual code) compels the parent widget to update its view.
In the actual code, I have the child widget handling the db queries, a click of the child widget changes the values in the db, and these values are to be updated by parent widget. Hence when I click the child widget, it updates the db then calls the notifyParent() which then leads to the parent querying the db again and updating the view.
Is there a better way of doing this without using the setState() by using the provider? How can I use say a ChangeNotifierProvider to notify of the db changes? Or is there any way I can notify the parent widget whenever something in the db is changed so that the parent can refresh itself?
Thank You.
I'm using an InheritedWidget to expose a bloc class to child components. However, every time the widget tree gets recreated, a new instance of the bloc class is instantiated. As I'm using BehaviourSubject classes to store the latest values of some textfields, I'm loosing the current values with every recreation. How could this be solved, i.e. the bloc class should only be instantiated once.
It depends on how your provider were made, if it's an a extension of StatefulWidget with an a InheritedWidget.
If it's only extends from a InheritedWigdet, you'll miss the dispose method because it doesn't extends from StatefulBuilder, but, will never instantiate again, and the dispose method will be when you'll close your application. Check this example:
class Provider extends InheritedWidget {
Provider({Key key, Widget child}) : super(key: key, child: child);
final AppBloc bloc = AppBloc();
static AppBloc of(BuildContext context) =>
(context.inheritFromWidgetOfExactType(Provider) as Provider).bloc;
#override
bool updateShouldNotify(Provider oldWidget) => true;
}
This AppBloc is a component that has all my applcation's blocs.
But, if your provider extends an a StatefulWidget with a InheritedWidget, you can pass your bloc as a constructor parameter in the class you want, and this class should be Stateful too, so you can pass in the initState and will be rebuilted only when you access it again.
If you are using a StatefulWidget then you can instantiate the bloc in the initState method.