update StateProvider with flutter_riverpod - flutter

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

Related

Riverpod - fetch data in ConsumerWidget when accessing it

I have a StateNotifier whose state I want to use in a widget.
As far as I know, if you watch a StateNotifierProvider with ref.watch in a build, the widget gets rebuilt every time the state changes.
Now, In the StateNotifier I have a DatabaseService instance to call api requests and set the state accordingly and in a ConsumerWidget I watch the state.
The thing is, I want to call a fetch method defined in StateNotifier the first time the widget builds, so I can display the data retrieved from the database.
Something like this
class MyStateNotifier extends StateNotifier<CustomState> {
final DatabaseService databaseService;
MyStateNotifier(this.databaseService) : super(CustomStateInit());
Future<void> fetchData() async {
state = CustomStateLoading();
final result = await databaseService.apiRequest();
state = CustomStateSuccess(result);
}
}
final stateNotifierProvider = StateNotifierProvider((ref) => MyStateNotifier());
and in the widget
class MyWidget extends ConsumerWidget {
// I have to call the MyStateNotifier fetchData() method to get the data
Widget build(BuildContext context, WidgetRef ref) {
final data = ref.watch(stateNotifierProvider);
return Column(
children: data.map((e) => Text(e)).toList()
);
}
}
To call fetchData() once you watch your stateNotifierProvider you need to call it in the StateNotifier's constructor like this:
MyStateNotifier(this.databaseService) : super(CustomStateInit()){fetchData();}

How to have access Context in didChangeAppLifecycleState lifecycle hook using flutter HookWidget?

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...

Equivalent of ChangeNotifierProvider widget in Riverpod

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.

Flutter Provider, using child widget to update a list

I'm new to using Flutter and I am currently struggling to understand how to use the Provider package for the following task, or if it is even the correct implementation in the first place.
I have a widget that uses another widget within itself to update a time value.
In the parent widget I have the following:
class _AddTimesScreenState extends State<AddTimesScreen> {
List<TimeOfDay> times = [];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Provider<List<TimeOfDay>>.value(
value: times,
child: SetTimes()
In the 2nd widget, which is used to update the times list by using a time picker I have:
class _SetTimesState extends State<SetTimes> {
#override
Widget build(BuildContext context) {
final times = Provider.of<List<TimeOfDay>>(context);
Essentially my goal is to be able to update the times list in the 2nd widget so it can then be used in the first widget. I have methods to add TimeOfDay objects to the list, but when the code is run the list in the first widget does not appear to be updated.
Am I using Provider in a way that it's intended, or have I completely misunderstood its application?
Thanks
In the TimeOfDay class make sure you are extending it with Change Notifier.
How does provider know it has to rebuild?
When the class (TimeOfDay in your case) extends ChangeNotifier, you are provided with a method called notifylisteners() , this triggers a rebuild to all the widgets consuming the provider. So you should call this in the function that is changing the objects data in your class TimeOfDay.
So make sure you are:
extending ChangeNotifier in your class/model.
calling notifylisteners when data is changed.
Example :
class MyClass extends ChangeNotifier{
int a = 0;
addSomething(){
//Here we are changing data
a = a + 1;
notifylisteners();
}
}
let me know if this solves your error.

How to persist bloc instance in widget tree

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.