Modifying Lists inside of classes - Flutter, Riverpod, Hooks - flutter

I would like to have a class that contains a list of another class and be able to update it using Riverpod/Hooks. I'm trying to figure out the best syntax or even way to approach the issue.
I'm starting with a StateProvider to make it simpler and thought to use the List method .add() to add to the list but I'm getting the error: "The method 'add' can't be unconditionally invoked because the receiver can be 'null'." or null check operator used on null value (when I add the !)- so I think I'm not writing it correctly and I'm sure it has to do with the Riverpod or even Dart syntax.
Any suggestions or resources would be appreciated. Thanks in advance.
So for example:
Session({this.title, this.sessionThings})
String? title;
List<SessionThings> sessionThings;
}
class SessionThings{
SessionThings({this.title, this.time})
String? title;
double? time;
}
ref.read(buildExSessionProvider.notifier).state = Session()..sessionThings.add(SessionThings(title: 'something', time: 20);
using:
hooks_riverpod: ^1.0.3
flutter_hooks: ^0.18.3

Do not access state outside a notifier. Something like this could work:
class SessionStateNotifier extends StateNotifier<Session?> {
SessionStateNotifier() : super(null);
void create(String title) => state = Session(title: title);
void add(SessionThings things) =>
state = Session(title: state?.title, sessionThings: [...state?.sessionThings ?? [], things]);
}
Use with hooks
class MyApp extends HookConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
final state = useState(0);
final Session? session = ref.watch(sessionProvider);
final SessionStateNotifier sessionStateNotifier = ref.watch(sessionProvider.notifier);
return <widget>
}
}
``

Related

Flutter Riverpod call StateProvider within a class that itself is a StateProvider

I have a Class that is being called by Widgets but this Class needs to pull data from another Class. Basically, I am using Riverpod as Dependency Injection, and am unsure if this is "correct" or am I doing it wrong. Here is what I did:
main.dart
var myClass1 = Class1();
final class1Provider = StateProvider((ref) => myClass1);
final class2Provider = StateProvider((ref) => Class2(myClass1));
Is this the recommended way or should I do something else?
FYI this does work;
Widget build(BuildContext context) {
displayData = (ref.watch(class2Provider.notifier).state).getData();
thanks
This is incorrect. You are using riverpod but are mixing it with global variables which trashes the whole point of using a state-management library.
You should create instances inside the StateProvider:
final class1Provider = StateProvider((ref) => Class1());
In order to access the value of one provider in another you need to use ref.watch() method inside the body of the provider:
final class2Provider = StateProvider((ref) {
final myClass1 = ref.watch(class1Provider);
return Class2(myClass1);
});
Finally, to consume a StateProvider you are watching the notifier instead of the state. This will give you an initial value correctly but will not rebuild your UI when the state changes. Instead you should watch the provider directly.
Widget build(BuildContext context) {
final displayData = ref.watch(class2Provider).getData();
}
For more info read the docs thoroughly https://riverpod.dev/docs/getting_started/.

Flutter - How to call functions (and variables) between stateful widgets?

I have this function in a widget (homescreen):
void toggleRecording() async {
// HERE IS THE CONFUSION I GUESS
_isRecording = !_isRecording;
recorder = SoundStream(isRecording: _isRecording);
//recorder.toggleRecording(_isRecording);
setState(() {
_isRecording = recorder.isRecording;
});
if (_isRecording) {
startTimer();
_stopwatch.start();
} else {
stopTimer();
_stopwatch.stop();
}
}
It needs to call (trigger) another function in my recorder class:
void toggleRecording() async {
widget.isRecording ////// currently being passed as an argument from homescreen
? {_recorder.stop, await save(_micChunks, 44100)}
: _recorder.start;
}
Also, the boolean variable _isRecording is present in both the classes. How do I sync the state?
In your situation passing reference of function through widgets will work. However best practice of this will be using provider package.
Managing functions from provider will allow you to control functions from all pages.
If you change a variable you can call notifylistener() function inside your provider function. So that you can change state of widget.
I will try to explain it in a glance however this is an important subject of flutter.
Here is my folder structure
At provider folder we define our provider classes.
Firstly i define class which extends changeNotifier class. This is what make this class provider.
Side note: notifyListener() function here calls setState of every widget if you use any variables inside that class and this is what you are searching for.
Then i import it into my main.dart file or whatever file you want. Only condition is being above the widget that you will use provider at.
At last you can use your function at everywhere if you import provider package and define your provider like i did in this code.
At last here is the visualized stucture of provider package.
I wish i explained it well. There is more about it on youtube.
Pass the function to other widget
using Function keyword
Say ContentWidget is your child and ParentWidget is parent
class ParentWidget extends StatefulWidget {
//Do Something
void onSomeFunction()
{
ContentWidget(onTimerUpdate:onTimerClosed)
}
void onTimerClosed()
{
//Perform Operation on Timer Change
}
}
class ContentWidget extends StatefulWidget {
final Function onTimerUpdate;
ContentWidget({
Key key,
#required this.onTimerUpdate,
}) : super(key: key);
void onAnyActionFromChild()
{
widget.onTimerUpdate() //Note () can have any param or can be null
}

Flutter How to get the value of a Provider out

I have this Provider in my code. its suppose to return list
class ServingProvider extends ChangeNotifier {
List<Serving> servings = [];
getServings() {
Firestore.instance
.collection('servings')
.getDocuments()
.then((value) => value.documents)
.then((value) =>
value.map((serving) => Serving.fromJson(serving.data)).toList())
.then((value) => servings.addAll(value))
.then((value) => notifyListeners());
print(servings);
}
// List<Serving> get servings => servings;
}
I want to get the value here but I'm getting null for some reason.
class KMultiScrollWheel extends StatelessWidget {
final String title;
final String value;
final BuildContext passedContext;
final String dialogTitle;
KMultiScrollWheel({
#required this.title,
this.value,
#required this.passedContext,
this.dialogTitle,
});
#override
Widget build(BuildContext context) {
final servings = Provider.of<ServingProvider>(context).servings;
print(servings)
This is the wrapper of my class.
ChangeNotifierProvider(
create: (_) => ServingProvider(),
child: KMultiScrollWheel(
title: 'Serving Size',
passedContext: context,
dialogTitle: 'Select Serving',
),
),
I'm coming from react and I'm completely lost for something that should be rather simple. thanks in advance.
One last question whats the point of using get method when I can access the servings already declared at the top.
As you are using realtime database, use StreamProvider instead of ChangeNotifierProvider , this medium article can help you out
I am new to this and can't comment since I am not sure if this will work, but I don't see you calling the getServings method, so maybe your list isn't filled yet?
You could try something like this:
final servingsProvider = Provider.of<ServingProvider>(context);
(await) servingsProvider.getServings(); //and make getServings() async since it connects to Firebase
print(servingsProvider.servings);
Of course you should handle this in a future or streambuilder, to render it.
I think getServings is a Future function, so you must handle the case when servings is null. You can simply add getServings in the constructor of ServingProvider:
class ServingProvider extends ChangeNotifier {
ServingProvider(){
// call getServings in constructor, notify listener when servings update
getServings();
}
...
and handle null case of servings in KMultiScrollWheel build by your own.
The other ways are FutureProvider (set initialData when data is not ready) or StreamProvider.

How do I go about synchronously initialising my provider? (Riverpod)

I have a provider like so:
final profileImagesProvider =
ChangeNotifierProvider.family<ProfileImagesNotifier, List<String>>(
(ref, profileImages) => ProfileImagesNotifier(profileImages));
class ProfileImagesNotifier extends ChangeNotifier {
ProfileImagesNotifier(List<String> images);
List<String> images;
}
However, when I try to use the provider:
Widget build(BuildContext context, ScopedReader watch) {
var profileImages = watch(ProfileImagesNotifier(['test_1.png', 'test_2.png']))
print(profileImages.images) //null
}
The list is retrieved as a null.
Am I doing this right? (I'm a completely noob when it comes to river pod and state management).
Was incorrectly calling the constructor.
Should have been:
ProfileImagesNotifier(this.images);
rather than
ProfileImagesNotifier(List<String> images);

SignalR client flutter library shows null values in another widget when using Flutter Provider

Am trying to connect on a SignalR server with flutter, and I tried to use Provider to access the data which comes to another Widget, but am receiving a null value. This is the SignalR library for flutter am using.
Below is my Class which extends a ChangeNotifier :
class BreakingNewsSignalRClient with ChangeNotifier {
String _breakingNews;
Future<void> getBreakingNews() async {
final hubConnection = HubConnectionBuilder()
.withUrl('https://services.xxxx.com/breakingNews')
.build();
await hubConnection.start();
hubConnection.on("UpdateBreakingNews", (data){
_breakingNews = data.toString();
print(data.toString());
notifyListeners();
});
hubConnection.onclose((error) => print("Connection Closed"));
}
String get breakingNewsFunction => _breakingNews;
}
class BreakingNewsModel {
final String News;
BreakingNewsModel(this.News);
}
As you can see the code above, am having a getter String get breakingNewsFunction => _breakingNews;, this is called in another Stateful Widget to populate some data from the SignalR server, but it returns null in the other widget, however, when you try to print data here in the getBreakingNews method the data is shown.
Below is another Widget class which receives the data :
class BreakingNews extends StatefulWidget {
const BreakingNews({Key key, #required this.article, #required this.index})
: super(key: key);
final Article article;
final int index;
#override
_BreakingNewsState createState() => _BreakingNewsState();
}
class _BreakingNewsState extends State<BreakingNews> {
settings() async{
var breakingNewsInfo =
Provider.of<BreakingNewsSignalRClient>(context, listen: false);
await breakingNewsInfo.getBreakingNews();
print('lets see -- ${breakingNewsInfo.breakingNewsFunction}');
}
#override
void initState() {
// TODO: implement initState
settings();
super.initState();
}
}
So when you look at this line print('lets see -- ${breakingNewsInfo.breakingNewsFunction}');, it prints null, am still wondering what am doing wrong here.
Kindly need some help.
Did you try data[0]?
Can you write it?
_breakingNews = data[0].toString();
instead of
_breakingNews = data.toString();
This is pretty simple. Your Future<void> getBreakingNews() async returns void and therefore null. Just adjust the return type to whatever you need.
Edit:
Actually, the problem is you are not calling your getter here.
await breakingNewsInfo.getBreakingNews();
So either return your result from the function, or call the getter. Either should work.