I have a screen that displays
a stock value of an asset
an asset selection dropdown
For now, I put all those values in a singe State class:
class AssetsLoaded extends AssetsState {
final List<ActiveSymbol> assets;
List<String> get markets {
return assets.map((e) => e.market).toSet().toList();
}
String selectedMarket;
ActiveSymbol selectedAsset;
int selectedAssetPrice;
AssetsLoaded({this.assets, this.selectedMarket, this.selectedAsset, this.selectedAssetPrice});```
}
Should I separate this State class into several smaller State classes in Cubit architecture? E.g. assets list seem unrelated to selection information. Should I keep all the variables that are consumed by the screen in one state, or should I create several smaller states and cubits?
Generally, you'll want to keep related data together. Since these two bits of information are both related to assets, they should be kept in the same Cubit/Cubit state. This means they can be accessed easily together, alongside if you need to return both at the same time, some version of both, or modify one in relation to another.
If instead you split them up, you may need to introduce loose coupling, which isn't great.
Related
I have a Flutter app that uses an SQLite database that has lots of inter-dependent tables. So far I have used Provider to create a class for each table in my database. The classes rely on each other and need to know when some of the others update.
I have added all 23 of my providers to main.dart using MultiProvider. I'm finding that Provider won't allow me to access ClassA from ClassB which is a deal-breaker.
I am considering a few options:
Option 1 - Riverpod
This tool allows me to make different groups of data aware of each other, but the downside is a lot of extra code. You have to create an entire StateNotifierProvider just to watch a single list of data. The learning curve feels steep, too.
//One of these for every single reactive data list + other provider types for other data types
class ClassANotifier extends StateNotifier<List<DataType1>> {
ClassANotifier(): super([]);
...
}
Option 2 - GetX
There is an enormous amount of debate about GetX, but I'm trying to ignore all of that. It seems that GetX controllers allow me to reference other controllers anywhere I want. It appears this would solve my problem (but potentially bring unwanted side-effects that the internet is raging about).
class ControllerA extends GetxController {
var controllerB = Get.put(ControllerB());
}
class ControllerB extends GetxController {
...
}
Option 3 - Single, Monolithic Provider Class
I recently learned that I can break a Dart class up across multiple files like this:
//=== class_a.dart ===
part 'class_b.dart';
class ClassA with ChangeNotifier {
...
}
And then in the other files:
//=== class_b.dart ===
part of 'class_a.dart';
extension ClassB on ClassA{
...
}
This would approach would allow me to use Provider to have a single class I put in my widgets, and I can access any part of my data structure across my app. I would have to be careful to name things clearly, but otherwise it seems like it could work.
Is there another way to handle lots of inter-dependent data like this in my app's state?
GetX is talked about a lot, but when you work with it, you fall in love. I use it in all my projects, they are not projects for large companies, but for many screens (50 to 100).
It's very simple and its learning curve is smooth!
I use it and will continue to use it!!!
Screen A -> Screen B -> Screen C (in B and C you have access to A controller) when close A, GetX down A Controller!! (this optimizes memory usage!!)
I'm new to riverpod, and I want to check that I am doing things correct.
I have a screen on my Flutter app where the use inputs lots of information about a session. Like start time, end time, notes, duration, date etc etc. All this data in the end is stored in a dart complex object MySession(), will all the above properties.
My question is, in the meantime, I am creating a seerate provider for each field. Like this:
final selectedDateProvider = StateProvider((ref) => DateTime.now());
final sessionDurationMinutesProvider = StateProvider<int>((ref) => 0);
Now in the class, I call the providers like this in the build method:
selectedDate = ref.watch(selectedDateProvider);
sessionDurationMinutes = ref.watch(sessionDurationMinutesProvider);
Then I display them in the widgets.
When they are editing, I save the state like this:
ref.read(selectedDateProvider.notifier).state = datePick;
My question is, I have lots and lots of fields on this page. So I have to create lots of providers for each field. Is this the correct practise? Can I not make a customclass for all these fields, and then make one provider which will return this custom class?
On the riverpod docs it says: You should not use StateProvider if: your state is a complex object (such as a custom class, a list/map, ...)
https://riverpod.dev/docs/providers/state_provider
I hope its clear!
Thanks
You already answered your question ;)
Yes, you can.
Make a class that will store the state of all the input fields and expose it through StateProvider.
To do it effectively you will probably need a copyWith method which can be written manually or generated. One of the possible approaches is to use freezed.
whats the difference between these 2 widgets and its necessary to use ChangeNotifier in every Provider or there is many ways to use Provider?
Provider exposes a value down the widget tree so that children can have access to it, regardless their location (but still, they must be at least one level below the provider). Generally you use a provider to expose a "cache" to a series of widgets or as a neat way of sharing data across multiple pages. Note that:
By default, when reading a value stored in a provider, nothing happens. It means that if you're exposing a class and you change some internals of it, children won't listen to it.
If your class exposed via provider mixes with ChangeNotifier then you've the possibility to rebuild listeners when something changes.
You are absolutely not forced to use ChangeNotifier in your model classes exposed by a provider. Use it when you need some widgets to listen to changes but if that's not the case, just ignore it.
Example 1 - (no ChangeNofitier)
You are using a TabBarView to work with tabs (let's say you have 3 pages) and your pages need to share some data. Instead of sharing data using a Navigator, which can become cumbersome, go for a provider.
class MyDataHolder {
int _value = 0;
void updateValue(int a) {
_value = a;
}
}
And then do something like this:
Provider<MyDataHolder>(
create: (_) => MyDataHolder(),
child: MyWidgetWithTabs(),
)
In this way you can easily share data among pages
Example 2 - (with ChangeNotifier)
Still the above case but if you want your pages to listen to changes on a particular data, then use a notifier. Like this:
class MyDataHolder with ChangeNotifier {
int _value = 0;
void updateValue(int a) {
_value = a;
notifyListeners();
}
}
Now children listen to changes. Differently from before, when calling updateValue listeners will be rebuilt.
So both ways are good but they have different purposes. If you don't need a series of listeners to be rebuilt in response to updates, just don't use ChangeNotifier.
A friend and myself are creating a Goosebumps-style adventure game, where at each stage the user is presented with a potential set of 4 choices, and the user's choice affects the outcome of the story.
What data structure should I use for this?
This is my main idea -- Objects
In trying to keep the game as close to the real life idea of these cards as possible, create one 'card' base class, and have lots of other cards inherit from this - superclass would contain Stringx5( x1story x4choiceStories) intx5 (x1CardIDNumber x4CardIDChoices).
This would then allow me to pump out objects easily with the material we already have, and have a system class controlling all the processing for user choices and displaying information onscreen. And again with the system in place and a base card class, it would allow for different stories in the future and whatnot. Trying to make this as reusable as possible and write as little code as possible (I'm not writing over a thousand if Statements.)
One thing that isn't clear to me (and the actual reason I'm posting this question in my inability to find the answer): isn't inheritance meant to be for other classes that are similar but with slight differences, e.g. managers and employees, making my idea completely wrong and a massive waste of memory?
I have looked into the following:
Hash tables: the examples seem to be more phone book oriented, and I don't think it would suit my needs
Abstraction to define a story type: also doesn't seem to suit my needs
No real need for inheritance as the cards are exactly the same, just the data on them changes. I'd use inheritance if there were special cards that need to behave differently to all the rest.
You can do what you want with something like this pseudo code:
class Card {
Card getChoice(int i); // returns choices[i]
string storyText;
Card[] choices; // Use an stl collection rather than an array for ease of addition.
}
Basically you create each card so it links to all the other cards (trick here is making sure you create the cards in the right order - easy solution: create them with no choices and add the different choices via an addChoice(Card) method later.
Your Game class starts with the first card (basically the head of a tree to all the cards), and does something like:
Card runCard(Card card)
{
Card nextCard = null;
showStoryText(card);
// Display a line for each choice in the card and get the user's response.
// Convert the response to the correct index.
int selection = promptForAction(card);
if (selection >= 0 && selection < card.numChoices()) {
nextCard = card.getChoice(selection);
}
return nextCard;
}
void run()
{
Card card = firstCard;
while(card != null) {
card = runCard(card);
}
}
This shouldn't be too bad. Essentially you need a tree structure.
Your main class could look like (forgive my lack of knowledge of c++)
class Node {
Node option1;
Node option2;
Node option3;
Node option4;
}
So your Node instances can point to other instances of Node.
It's probably better to have some sort of collection of Node instances, that way you can have as many or as few as you want. You can add a field to Node that indicates which option it is (1, 2, etc).
The only other thing you need is reference to the initial Node.
I'm trying to familiarize myself with the "Places & Activities" design pattern for GWT development, and so far I think it has a lot of potential. I especially like the way how once you start thinking about your application in terms of "Places", browser history virtually just lands in your lap with almost no extra effort.
However, one thing just bothers me: All the articles and code examples I've seen so far gloss over one (as far as I am concerned, major) aspect: the 'M' part of the 'MVP', i.e. the Model!
In normal MVP architecture, as far as I understand it, the Presenter holds a reference to the Model, and is responsible for updating it according to UI events or, respectivly, updating the UI according to Model changes.
Now, in all the articles/samples for "P&A" the Activities seem to be taking the place of the Presenter, but unlike 'normal' Presenters, they get discarded and (re-)created whenever a new Place arrives, so they can't be the ones to store the client state, or it would be lost every time. Activities are cheap to create, so it's not much of a hassle, but I wouldn't want to create the model of a complex application over and over again.
All the samples are fairly simple and don't have much of a state and therefore just ignore the Model aspect, but where would an actual, complex application store its state?
I think I found my own answer. The main problem seems to be that all of the simple code examples make the Activities be the Presenters, as in:
public class NavigationActivity extends AbstractActivity implements NavigationView.Presenter {
private final ClientFactory clientFactory;
private final NavigationPlace place;
public NavigationActivity(ClientFactory clientFactory, NavigationPlace place) {
this.clientFactory = clientFactory;
this.place = place;
}
#Override
public void start(AcceptsOneWidget panel, EventBus eventBus) {
NavigationView navView = clientFactory.getNavigationView();
navView.setSearchTerm(place.getSearchTerm());
navView.setPresenter(this);
panel.setWidget(navView);
}
#Override
public void goTo(Place place) {
clientFactory.getPlaceController().goTo(place);
}
}
Now Activities are fairly short-lived, whereas a typical Presenter in the 'classical' sense has a much longer lifespan in order to maintain the binding between the model and the UI. So what I did was to implement a separate Presenter using the standard MVP design pattern, and all the Activity does is something like this:
public class NavigationActivity extends AbstractActivity {
private final ClientFactory clientFactory;
private final NavigationPlace place;
public NavigationActivity(ClientFactory clientFactory, NavigationPlace place) {
this.clientFactory = clientFactory;
this.place = place;
}
#Override
public void start(AcceptsOneWidget panel, EventBus eventBus) {
NavigationPresenter presenter = clientFactory.getNavigationPresenter();
presenter.setSearchTerm(place.getSearchTerm());
presenter.go(panel);
}
}
So, instead of the Activity fetching the View, and acting like a Presenter on it, it fetches the actual Presenter, just informs it of the client state change induced by the Place, and tells it where to display its information (i.e. the view). And the Presenter is then free to manage the view and the model any way it likes - this way works a lot better (at least with what I have in mind for the application I'm working on) than the code examples I have found so far.
You're right, almost always the presenter is the only guy holding the model and managing its lifetime. Model from previous GWT versions has simply been a DTO, (and continues to be) a POJO which are created by the GWT deserializer when RPC methods return, or created by Presenters and filled with UI data by UIHandlers, and sent to the server.
I have taken efforts, to keep the Models lifetime encapsulated within the Activities lifetime, and avoided storing state outside of activities. (I do however have a singleton Global State maintained for use from anywhere in the application.) This I think is what GWT MVP engineers assumed will happen - It makes sense when, say the user has navigated away from a Place, for the models associated with that place also to get disposed (& collected). Create models, fill them up inside the activities, and make service calls to update the server before navigating away (or triggered by some control on the page), and let them go along with the activity - is what I have been doing till date.
A bigger project I have been involved in faced quite a few browser memory footprint issues, and all of them were due to objects which are associated with another (not currently viewed) Place being in memory. It was hard to track and remove references to these objects, and since the user has navigated away from the screen - the "why are my old screen objects still in memory?" question cropped up frequently and subsequently got fixed. This is why, upfront, I chose to keep the Model's lifetime encapsulated within the Activities lifetime in my current pet project.
If you have models that span across (that are filled/accessed by) several activities (as is the case if you have sidebars & master/container widgets), some redesigning of models maybe in order, if you have examples I will try and help.