Flutter/Riverpod - StateNotifier - call state change before end of method - safe? - flutter

In my Flutter app I am using two providers to manage a list display...
a 'goalListFutureProvider ' Future provider that retrieves the list of entries to display
a 'goalCrudProvider' StateNotifier provider that handles add/edit/delete actions on entries in the list
The code for the list provider is shown below, where state changes in the CRUD provider will trigger a refresh of the future list provider
final goalListFutureProvider = FutureProvider<List<GdGoal>>((ref) {
ref.watch(goalSchedulingProvider);
ref.watch(goalCrudProvider);
final aSearch = ref.watch(goalListSearchProvider);
return ref.watch(goalServiceProvider).find(aSearch);
});
Within my CRUD provider I am calling a state change as part of the process logic, the 'update' code is shown below..
class GdGoalNotifier extends StateNotifier<bool> {
Future<bool> update(GdGoal aGoal) async {
final aRes = await _goalService.update(aGoal);
state = !state;
return aRes;
}
Within the 'update' method is it correct to call 'state = !state' before the end of the function call? Or should I break out this state refresh to a separate CRUD provider method?

Related

How to communicate between two stateProviders in river pod?

i have just recently stated working with riverpod state mangement in flutter.
i have issue related to comunicate between to state providers.
here is my sample code:
class SomeClass_ONE extends stateNotifer <SomeState> {
SomeClass_ONE({required this.somevalue}):super(null);
final SomeCustomClass somevalue;
void methodOne(SomeState newstatevalue){
state = newstatevalue;
}
}
final someClassOneProvider =
StateNotifierProvider<SomeClass_ONE,SomeState>.
((ref)=>SomeClass_ONE(somevalue: SomeCustomClass()));
now i have another state provider class as below
class SomeClass_Two extends stateNotifer <SomeStateTwo> {
SomeClass_ONE({required this.somevalue}):super(null);
final SomeCustomClass somevalue;
void methodtwo(SomeState newstatevalue){
state = newstatevalue;
}
}
final someClassTwoProvider =
StateNotifierProvider<SomeClass_Two,SomeStateTwo>
((ref)=>someClassTwoProvider(somevalue: SomeCustomClass()));
now what i want to achhive is that on methodOne execution i have to listen that state cahnge and have to trigger methodTow and have to upate secondproviders state as well.
so how can i achive this without using Ref in class cunstroctors?
i have tried with ref.listner to trigger and have passed Ref in both class constructors. but as per some condition i can't use Ref directly in constructors as per some guideline followed by seniors.
You can pass a Ref ref object to the methodtwo method and then call the necessary methods from other StateNotifierProvider. In any case, to refer to other methods of other classes, you need to have a Ref object.
Try to use watch provided by StateNotifierProvider
Try this code:
class SomeClass_ONE extends stateNotifer <SomeState> {
SomeClass_ONE({required this.somevalue}):super(null);
final SomeCustomClass somevalue;
void methodOne(SomeState newstatevalue){
state = newstatevalue;
// Listen to the changes in the state of the first provider and call the methodtwo of the second provider
someClassTwoProvider.watch((_) => _.methodtwo(newstatevalue));
}
}

Have OrderCubit access the userId stored in AuthCubit

I am trying to create a fetchUserOrders() method in an OrderCubit. To do so, I need the userId which is held in the OrderState of the OrderCubit.
Both Cubits are setup in a MultiBlocProvider in main.dart.
How do I make a call like:
Future<void> fetchOrders() async {
emit(OrderState.loading());
final uid = context.read<AuthCubit>().state.uid;
final orders = await OrderRepo().getUserOrders(userId: uid);
emit(OrderState.loaded(orders));
}
within the OrderCubit?
I don't know of a way to get a context within a Cubit, so the above is not an option thus far. I have looked at using BlocListener, but as I understand it that only emits a new state in response to a change from AuthCubit - which is not refreshing a new value.
I appreciate any insight on best practice for reading values from one Cubit to another.

How can I store asynchronous operations and later assign them to a related instance?

I'm building a form using flutter_form_builder package. Certain form fields must be geolocated; for this purpose I'm using geolocator package.
Geolocation operations return futures. I do not want to block/await for the geolocation operation to complete because it would slow down filling out the form (i.e. can be less responsive due to bad signal, etc.).
I do not want to await any futures until the user hits submit; hopefully, by the time they do, most (if not all) futures will have completed. How can I avoid waiting for the futures to complete until the form is fully filled out?
My current idea is to have an instance in the main form widget state that will store the futures along with the key to the form field that they are associated with. This will allow me to update the form when a future completes.
This untested code demonstrates the above:
class GeolocationManager {
const GeolocationManager({
required this.form,
}) : geolocatedFields = const {};
/// Synchronous form JSON.
final Map<String, dynamic> form;
/// Futures associated by key to a geolocation request.
final Map<String, Future<Position>> geolocatedFields;
void add(
String key,
Future<Position> future,
/// Parses a json form field and inserts the associated [Position] before
/// converting back to a json form field.
dynamic Function(Position, dynamic) toJson,
) {
/// Store the future along with an associated form field key.
geolocatedFields[key] = future;
/// When the future completes, update the form json.
future.then((position) => form[key] = toJson(position, form[key]));
}
}

Understanding Cubit/Bloc state management

I'm new to flutter and I have experience in web application using state managements like Redux or Vuex where the initial state of a module might be something like:
{
value1: 0,
value2: 10,
aBool: false,
aString: 'Hello'
}
Then based on Reducers or Mutations we can update a single or multiple properties of the state.
Now, learning Flutter I decided to use Bloc/Cubit and online I cannot find the right answer to my problem, even because the majority of the example are always based on the crappy counter app and never on a more realistic scenario.
All I can see is something based on 4 states in Bloc: initial, loading, success and error.
This is fine when fetching data from an API, but what if my state has also more properties?
how to update those properties?
Actually I created my test Cubit to fetch something from my API, it works. Now I wish to add more properties on the state and update it based on actions, how can I do that?
Example state:
#freezed
abstract class TestState with _$TestState {
const factory TestState.initial() = _Initial;
const factory TestState.loading() = _Loading;
const factory TestState.success(UserData user) = _Success;
const factory TestState.error(String message) = _Error;
}
Example Cubit:
class TestCubit extends Cubit<TestCubit> {
TestCubit(this._testClient)
: super(TestState.initial());
final TestClient _testClient;
String greet = 'Hi';
Future<void> testFetchData() async {
...
emit(TestState.success(testData));
...
}
}
I can successfully handle the varioud initial, loading, etc... states.
I can correctly watch at the greet property: context.read<TestCubit>().greet
How should I now update that value with 'hello!'?
// TestCubit
updateGreet(String text) {
emit(I don't know);
}
I omitted all my various tries to update that value.
Thanks

How do I initialize a long-lived class in a Flutter app?

I have an Api class that accepts an (optional) authentication token that it uses for making authenticated requests e.g. Api(token: 'a9sa2ksas12').getUserDetails().
If the token is not passed, it has to perform the relatively expensive operation of reading it from sharedPreferences.
class Api {
static const BASEURL = "https://api.google.com/";
final String token;
Api({ this.token });
Future<http.response> getUserDetails() async {
return http.get('$BASEURL/user/', headers: { 'Authorization': token });
}
}
How can I setup my app so that the token is read only once from sharedPreferences and used throughout the app for all future Api() requests?
Some ideas I've considered and think that may work:
Have token be a global variable
Make the API class a singleton, and pass it around between "screens"
Well in general there's nothing bad in making you Repositories a singletons. But on the other hand, I don't like the concept of passing the API classs between the screens.
When widgets use your data source directly without any middleman, like Bloc or Provider, they tend to be polluted with a lot of presentation logic. I personally prefer to separate those concerns and let widgets do what they are made for: rendering the UI. This makes them smaller and easier to test.
What's more the API class should be responsible only for the network calls. It shouldn't be responsible for managing the token. I'd inject to the API class something like:
class TokenProvider
{
Future<String> getToken();
}
The responsibility of this class would be, you guessed it, to provide a token. It can return cached value or get it from the SharedPreferences. Thanks to this the API doesn't have to care where the token comes and how to handle it. It will do just one thing: api calls.
I ended up using the "Locator" pattern, via get_it.
The code was pretty simple.
Step 1: Setup a top-level locator.dart in lib/
// ./lib/locator.dart
import 'package:my_app/services/api.dart';
import 'package:get_it/get_it.dart';
GetIt locator = GetIt.instance;
void setupLocator() {
locator.registerLazySingleton(() => Api());
}
Step 2: Use api anywhere in the app by just importing the locator:
// ./lib/widgets/some_screen.dart
class _SomeScreenState extends State<SomeScreen> {
Api api = locator<Api>();
#override
void initState() {
api.getUserDetails().then((response) => {
// do anything you like with the response
});
super.initState();
}
The beauty of this approach is that Api is only ever initialized ONCE in the lifetime of the app, so I simply assign a token to it on initState without worrying about the widget getting disposed/rebuilt and repeated fetches to SharedPreferences.