how to handle multiple `loading` states with redux architecture in Flutter? - flutter

How do you guys handle multiple loading states with redux pattern in Flutter?
So here is my case:
I have 3 pages, each page calls a different API and shows a loading HUD while requesting the API.
Sub-Question#1 How do i manage these isLoading states?
If i do it in the AppState , i need to add multiple boolean properties to it, something like:
class AppState {
final bool apiOneIsLoading;
final bool apiTwoIsLoading;
final bool apiThreeIsLoading;
// other properties, bla bla...
}
However, Adding to many properties to the AppState class doesn’t sounds great I guess...
Sub-Question#2 How do I update the UI when the loading state changes?
One solution I come up with is to create actions for both loading and loaded state, like so:
class SomeMiddleware extends MiddlewareClass<AppState> {
#override
void call(Store<AppState> store, dynamic action, NextDispatcher next) {
if (action is CallAPIOneAction) {
store.dispatch(APIOneLoadingAction());
// call api one
api.request().then((result){
store.dispatch(APIOneLoadedAction())
})
}
}
}
But if I do this, I need to create 2 extra actions for each API call, is this a good idea? is it okay to change or send new actions in middleware class on the fly?
Please let me know if you have a good solution, thanks!

Related

Navigate to page on start in .NET Maui app

Seems like a simple question, but I haven't been able to find a simple answer. Essentially I want to choose which page in the app to start on based on some stored state. I added a GoToAsync call in the AppShell constructor, but this didn't work--which makes sense because the AppShell hasn't been fully constructed yet.
I found this answer, but it feels like it kind of skirts around the issue:
Maui AppShell - Navigate on Open
Where is the best place to inject some code that will run once on startup and can successfully navigate a .NET Maui app to a chosen page?
After playing around with overrides, it seems like overriding Application.OnStart works! Shell.Current is set at this point and navigation works.
Here's additional code that allows for asynchronous initialization and uses a Loading Page until the initialization is complete:
using MyApp.Services;
using MyApp.UI;
namespace MyApp;
public partial class App : Application
{
ConfigurationProviderService m_configProvider;
public App(ConfigurationProviderService configProvider)
{
m_configProvider = configProvider;
InitializeComponent();
MainPage = new LoadingPage();
}
protected override void OnStart()
{
var task = InitAsync();
task.ContinueWith((task) =>
{
MainThread.BeginInvokeOnMainThread(() =>
{
MainPage = new AppShell();
// Choose navigation depending on init
Shell.Current.GoToAsync(...);
});
});
base.OnStart();
}
private async Task InitAsync()
{
await m_configProvider.InitAsync();
}
}

Flutter Riverpod design pattern (inhibit garbage collection)

I've written a Swift/IOS package to externalize and standardize all of my Social/Federated/Firebase authentication boilerplate (both SDK's and UI). I've taken it upon myself to port this to Flutter as a learning exercise ... but to also allow custom UI to be passed-in via config.
Since I'm new to Flutter & Riverpod, I'm afraid I'm making some serious mistake & want to get feedback from you experts before I go too deep.
The package is called "Social Login Helper" or SLH, and this is the public API I desire:
runApp(
slh.authStateBuilder(
builder: (authStatus) {
switch (authStatus.stage) {
case SlhResultStage.initializing:
return SplashScreen();
case SlhResultStage.unauthenticated:
// using Riverpod and Nav 2.0
return slh.authFlowUi;
case SlhResultStage.authenticated:
return ExampleApp(appKey, authStatus, slh.logoutCallback);
case SlhResultStage.wantsAnnonOnlyFeatures:
return ExampleApp(appKey, null, slh.startAuthCallback);
case SlhResultStage.excessiveFailures: // restart the app
return TotalFailure();
}
},
),
);
As you can see from the above, the State/Stream builder at root must never be garbage collected or purged. I'm unclear if and when Riverpod will dispose my provider, or if Dart itself will collect objects that must remain immortal. I'm also unsure whether to use a StreamProvider or a State provider??
As you can see below, I've created an intentional memory-leak (deadlock) to guard me. I'm sure it's an anti-pattern, but being novice, I'm not sure how else to guarantee immortality.
All guidance and explicit feedback would be most welcome.
class LivingAuthState extends StateNotifier<SlhResultStage> {
// create deadly embrace to prevent this from ever being collected
_Unpurgeable _up;
LivingAuthState() : super(SlhResultStage.initializing) {
//
final StreamProvider<SlhResultStage> rssp =
StreamProvider<SlhResultStage>((ref) {
return this.stream.asBroadcastStream();
});
_up = _Unpurgeable(this, rssp);
// how do I keep rssp from ever being collected??
}
StreamProvider<SlhResultStage> get authStatusStream => _up.rssp;
void logout() {
this.state = SlhResultStage.unauthenticated;
}
void restartLogin() {
this.state = SlhResultStage.unauthenticated;
}
}
class _Unpurgeable {
final LivingAuthState _aliveState;
final StreamProvider<SlhResultStage> rssp;
_Unpurgeable(this._aliveState, this.rssp);
}
One improvement I'd like to see in the Riverpod documentation is clarity on HOW LONG a provider will live, WITHOUT an active listener, before it will self-dispose / garbage-collect.
Ah, it looks like I can subclass AlwaysAliveProviderBase() to achieve the same goal ... I'll experiment with this.
Move your provider final to the top level. Riverpod providers are top-level final variables.
Also remember to wrap your app in the riverpod provider.

JavaFX Model View ViewModel where should I implement tasks?

There are several tutorials and examples on this topic out there but they are all a sort of generic build only in one class to show how it works generally.
So my question is when I would like to follow the MVVM pattern where I have to implement all my tasks?
Given the following:
Model:
class Model {
/* When I place the Task here how can I deal with arguments and results from ViewController? */
public BufferedImage bigTask (String this, String and, Image that){
// Some code to build a BufferedImage
}
}
ViewModel:
class ViewController {
private BufferedImage myBufferedImage;
#FXML
private Button aButton;
/*Should I implement my Task here? But how I get information about progress? */
final Task<Integer> myTask = new Task<Integer>(){
#Override
protected Integer call() throws Exception{
updateProgress( // How to get here? Is it the right place? )
return null;
}
};
#FXML
void setOnAction(ActionEvent actionEvent){
myBufferedImage = Model.bigTask("this", "that", new Image("path"));
}
}
Hope I could explain the problem.
Thanks in advance!
In general your tasks should be implemented in the ViewModel.
The actual implementation of business logic should be done in the Model for example in a service class. The ViewModel can then use this service and handle all the ui specific actions like creating a Task for async execution and updating a progress value. However the ViewModel may not directly update a ProgressIndicator but instead the viewModel could have a DoubleProperty "progress" that is updated in the ViewModel. In the ViewController/CodeBehind you bind the actual ProgressIndicator to this progress property of the ViewModel. This way the ViewModel is independent from actual UI controls and the View doesn't contain any business logic.
Your example is a little bit special I think. Normally I would say that "BufferedImage" is a ui specific class that only belongs to the View and not the ViewModel nor the Model. However your example looks like BufferedImage is the result of a business action. In this case I would create a ObjectProperty<BufferedImage> in your ViewModel and put the task to load the image in the ViewModel too. In your ViewController I would add a listener to this property and put the image into the ui when it changes.
This way your View class is independet of how the image is loaded.

Show AlertDialog from ViewModel using MvvmCross

I am using MvvmCross for creation my Android-app and I faced with the following problem:
When I'm trying to show AlertDialog, that was created in ViewModel, the
"Unhandled Exception: Android.Views.WindowManagerBadTokenException" appears.
public class MyViewModel : MvxViewModel
{
public ICommand ShowAlertCommand { get; private set; }
public AuthorizationViewModel()
{
ShowAlertCommand = new MvxCommand(() =>
{
var adb = new AlertDialog.Builder(Application.Context);
adb.SetTitle("Title here");
adb.SetMessage("Message here");
adb.SetIcon(Resource.Drawable.Icon);
adb.SetPositiveButton("OK", (sender, args) => { /* some logic */});
adb.SetNegativeButton("Cancel", (sender, args) => { /* close alertDialog */});
adb.Create().Show();
});
}
}
When I was researching I have found that it happens because of transmission of the reference to the Context but not on the Activity in the AlertDialog.Builder.
In this topic I found the following decision:
Receive references to the current Activity through the use of GetService(), but I didn't found mvvmcross plugins for work with IMvxServiceConsumer, IMvxAndroidCurrentTopActivity interfaces.
My question is can I show AlertDialog from ViewModel? And how can I get the reference to Activity, but not to the Application.Context?
And what is the correct way to close AlertDialog that the user would stay on the current View?
In general, you should try not to put this type of code into ViewModels
because ViewModels should stay platform independent
because ViewModels should be unit testable - and it's hard to unit test when the code shows a dialog
I'd also recommend you don't put code like this inside a ViewModel Constructor - these constructors are generally called during navigations and displaying a Dialog during a transition is likely to be problematic.
With those things said, if you do want to get hold of the current top Activity within any code, then you can do this using the IMvxAndroidCurrentTopActivity
public interface IMvxAndroidCurrentTopActivity
{
Activity Activity { get; }
}
Using this, any code can get the current Activity using:
var top = Mvx.Resolve<IMvxAndroidCurrentTopActivity>();
var act = top.Activity;
if (act == null)
{
// this can happen during transitions
// - you need to be sure that this won't happen for your code
throw new MvxException("Cannot get current top activity");
}
var dlg = new AlertDialog.Builder(act);
//...
dlg.Create().Show();
The use of IMvxAndroidCurrentTopActivity is discussed in MvvmCross: How to pass Android context down to MvxCommand?
The approach taken in that question/answer is also one of the ways I would generally approach showing dialogs from a ViewModel:
I would create an IFooDialog interface
Ideally I would probably make this interface asynchronous - e.g. using async or using an Action<DialogResult> callback parameter
on each platform I would implement that in the UI project
the ViewModels can then use IFooDialog when a dialog is needed and each platform can respond with an appropriate UI action
This 'Dialog Service' type of approach is common in Mvvm - e.g. see articles like http://www.codeproject.com/Articles/36745/Showing-Dialogs-When-Using-the-MVVM-Pattern (although that article is very Windows specific!)
There are also a few other questions on here about MvvmCross and dialogs - although they may contain reference to older v1 or vNext code - e.g. Alerts or Popups in MvvmCross and Unable run ProgressDialog - BadTokenException while showind

Widget notifying other widget(s)

How should widgets in GWT inform other widgets to refresh themselfs or perform some other action.
Should I use sinkEvent / onBrowserEvent?
And if so is there a way to create custom Events?
I have solved this problem using the Observer Pattern and a central Controller. The central controller is the only class that has knowledge of all widgets in the application and determines the way they fit together. If someone changes something on widget A, widget A fires an event. In the eventhandler you call the central controller through the 'notifyObservers()' call, which informes the central controller (and optionally others, but for simplicity I'm not going into that) that a certain action (passing a 'MyEvent' enum instance) has occurred.
This way, application flow logic is contained in a single central class and widgets don't need a spaghetti of references to eachother.
It's a very open ended question - for example, you could create your own static event Handler class which widgets subscribe themselves to. e.g:
Class newMessageHandler {
void update(Widget caller, Widget subscriber) {
...
}
}
customEventHandler.addEventType("New Message", newMessageHandler);
Widget w;
customEventHandler.subscribe(w, "New Message");
...
Widget caller;
// Fire "New Message" event for all widgets which have
// subscribed
customEventHandler.fireEvent(caller, "New Message");
Where customEventHandler keeps track of all widgets subscribing to each named event, and calls the update method on the named class, which could then call any additional methods you want. You might want to call unsubscribe in the destructor - but you could make it as fancy as you want.
So here is my (sample) implementation,
first let's create a new event:
import java.util.EventObject;
import com.google.gwt.user.client.ui.Widget;
public class NotificationEvent extends EventObject {
public NotificationEvent(String data) {
super(data);
}
}
Then we create an event handler interface:
import com.google.gwt.user.client.EventListener;
public interface NotificationHandler extends EventListener {
void onNotification(NotificationEvent event);
}
If we now have a widget implementing the NotificationHanlder, we can
trigger the event by calling:
((NotificationHandler)widget).onNotification(event);