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!!)
Related
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.
From the docs I understood that one can call addListener() on a ChangeNotifier instance to add a custom listener to the stack.
This method accepts a callback with zero arguments (according to notifyListeners()), e.g.:
class MyClass extends ChangeNotifier {
MyClass() {
addListener(() {
// ...
});
}
}
From within the callback, how does one find out what properties or parts of MyClass have been changed?
ChangeNotifier does not have such capabilities inherently. You will have to implement your own logic. Specifically, you either have access to all of the properties of your ChangeNotifier implementation because you add the listener in its scope or you have access to it because you have a reference to it in your scope.
ChangeNotifier simply implements Listenable and provides some utilities for managing listeners. Furthermore, the documentation states the following about it:
ChangeNotifier is optimized for small numbers (one or two) of listeners. It is O(N) for adding and removing listeners and O(N²) for dispatching notifications (where N is the number of listeners).
I am not sure about options with better runtime complexity for notifying listeners, but you will not run into any issues in a regular Flutter app.
ValueNotifier
ValueNotifier is a pre-made implementation of ChangeNotifier that will notify its listeners when its value property is changed.
This is sufficient for most case, but since it appears that you want to create a custom ChangeNotifier, you can use the source code of ValueNotifier to take a look at an example implementation (it is very straight forward).
If you are just looking to do state management in general, ValueNotifiers usually work great. However, they are not applicable in every scenario. Hence, here is an extensive list with different state management options.
Considering the questions, I think the techniques that fit your needs best and the most popular options are the following:
InheritedWidget as it lets you notify dependents based on what data changed. Additionally, there is InheritedModel as an extension of this and InheritedNotifier that works with Listenable, just like ChangeNotifier does.
The BLOC pattern, which works with streams.
The provider package which is mostly a convenience wrapper for various Flutter state management techniques (InheritedWidget, StatefulWidget, ValueNotifier, etc.).
This is a bit of a generic software design question. Suppose you have a base class and lots of classes that derive from it (around 10).
There is some common functionality that is being shared between some of the classes (3-4 of derived classes need it). Basically a field for a UI control, an abstract method to create a UI control and the common code that uses the abstract method to recycle the UI piece (8-9 lines of code) using the abstract method. Something like this:
class BaseClass {
...
protected UIControl control;
protected abstract UIControl CreateUI();
protected void RecycleUI() {
if (/* some condition is met */) {
if (this.control != null) {
control.Dispose();
}
this.control = this.CreateUI();
this.AddToUITree(control);
}
}
...
}
Do you think it is OK to put this to base class instead of replicating the code in derived classes.
Drawback is that this piece of code is only used for some of the base classes and completely irrelevant for the other classes.
One alternative is to create an intermediate class that derives from BaseClass and use it as the base to the ones that need the functionality. I felt like creating a derived class for a couple line of code for a very specific purpose felt heavy. It doesn't feel like it is worth interrupting the inheritance tree for this. We try to keep the hierarchy as simple as possible so that it is easy to follow and understand the inheritance tree. Maybe if this was C++ where multiple inheritance is an option, it wouldn't be a big issue but multiple inheritance is not available.
Another option is to create a utility method and an interface to create/update the UI control:
interface UIContainer {
UIControl CreateUIControl();
UIControl GetUIControl();
void SetUIControl(UIControl control);
}
class UIControlUtil {
public void RecycleUI(UIContainer container) {
if (/* some condition is met */) {
if (container.GetUIControl() != null) {
container.GetUIControl().Dispose();
}
UIControl control = container.CreateUI();
container.SetUIControl(control);
container.AddToUITree(control);
}
}
}
I don't like this option because it bleeds UI logic externally which is less secure as its UI state can be manipulated externally. Also derived classes have to implement getter/setter now. One advantage is that there is another class outside of the aforementioned inheritance tree and it needs this functionality and it can use this utility function as well.
Do you have any other suggestions? Should I just suppress the urges that brew inside me to have common code not repeated?
One alternative is to create an intermediate class that derives from
BaseClass and use it as the base to the ones that need the
functionality.
Well, this is what I thought is the most appropriate. But it depends. The main question here is the following: are objects, that require UI recycling and really different from those, that do not? If they are really different, you have to create a new base class for them. If difference is really negligible, I think it's ok to leave things in a base class.
Do not forget about LSP.
We try to keep the hierarchy as simple as possible so that it is easy
to follow and understand the inheritance tree
I think more important here is to keep things not only simple, but also close to your real world things so that modeling new entities would be easy. Seeming easiness now may cause real troubles in the future.
Being able to share data between multiple view controllers and doing that in a way that makes use of recommended patterns such as MVC seems to be essential to create good apps, but my problem is that these things aren't clear at all for me.
I am conscient that this question is really dense, but for things to be clear I think you really need to understand the whole thing.
First of all we need to be sure of what Model, View and Controller are doing, here is how I would describe them, please tell me if I'm right about that:
Model : a class that's responsible for managing data, and only that (for example, a class that will go on the web to retrieve information, such as weather forecast).
View : a view is an object that's displayed to the user, who can often interact with it, that's the objects that you can drag and drop in Interface Builder (for example a button) and you might also create one from scratch, or custom an already existing one by subclassing it.
Controller : a controller is responsible for managing a view and its subviews, it receives events (such as viewDidLoad, or even when the user taps a button) and can react to it, for example, it might change the text of a label.
Now about the way they are interacting between each other, I'd say that the controller is between the view and the model, it's managing the view and might ask for data to the model. In addition to receiving events from the view, it might also receive events from the model, for example, if the controller asks to the model for a specific data on the web (let's say if it asks weather for a specific city) the data won't be available immediately, instead, the model will notify the controller so that it can update the view with the data it received. Am I right?
One of the first thing I'm wondering is if an object could be considered as a model if it isn't here to retrieve data, but to do other things that are simply not related to the view, for example, could an object that's responsible for communicating and managing a bluetooth accessory considered as a model ? Could an object that sends data to a cloud considered as a model ? And what about a Tic Tac Toe AI ?
Then, singleton instances, I often heard of them when an app had to share data between multiple views, but first of all, I never really understood why it was necessary to use them in this case ?
Then, here is a singleton that I found in an article of the We Heart Swift website.
class Singleton {
struct Static {
static let instance = Singleton()
}
class var sharedInstance: Singleton {
return Static.instance
}
}
Singleton.sharedInstance
The problem if that I have had difficulties to find anywhere more details about why it's written in this way, and most of all, can a singleton have an initializer that takes arguments? How to add properties and methods to a singleton like this one? What are exactly the Static structure and the sharedInstance?
My last question is about why, technically, does a singleton makes it possible to get an access to things we have defined somewhere else? What I mean is that if I create an instance of let's say, a Dog class in my AppDelegate, and if I want to access to this specific instance in a view controller, then it wouldn't be possible, so how does singleton makes that possible under the hood?
EDIT : Oh, and, is the use of singletons recommended by Apple?
Thank you.
It has to do with the static in the struct. Static is essentially a class variable that persists for every instance of that class, so when you make the shared instance static, every time you access it, even from another instance of Singleton.instance it is the same variable because it is static. It persists amongst instances. However, Swift does not support class variables yet, so when it does, that should quickly replace the Struct syntax that is common of singletons. It is very similar to static variables in java.
For example:
class Singleton {
var someVar = 0
struct Static {
static let instance = Singleton()
}
}
to create a singleton with a variable and the following to access it:
let foo = Singleton.Static.instance
foo.someVar = 11
let bar = Singleton.Static.instance
println(bar.someVar) // Prints 11
As you can see, bar.someVar was never set, and that is because the variable for the shared instance was set, so it prints 11.
I have inherited a project that has an awkwardly big interface declared (lets call it IDataProvider). There are methods for all aspects of the application bunched up inside the file. Not that it's a huge problem but i'd rather have them split into smaller files with descriptive name. To refactor the interface and break it up in multiple interfaces (let's say IVehicleProvider, IDriverProvider etc...) will require massive code refactoring, because there are a lot of classes that implement the interface. I'm thinking of two other ways of sorting things out: 1) Create multiple files for each individual aspect of the application and make the interface partial or 2) Create multiple interfaces like IVehicleProvider, IDriverProvider and have IDataProvider interface inhertit from them.
Which of the above would you rather do and why? Or if you can think of better way, please tell.
Thanks
This book suggests that interfaces belong, not to the provider, but rather to the client of the interface. That is, that you should define them based on their users rather than the classes that implement them. Applied to your situation, users of IDataProvider each use (probably) only a small subset of the functionality of that big interface. Pick one of those clients. Extract the subset of functionality that it uses into a new interface, and remove that functionality from IDataProvider (but if you want to let IDataProvider extend your new interface to preserve existing behavior, feel free). Repeat until done - and then get rid of IDataProvider.
This is difficult to answer without any tags or information telling us the technology or technologies in which you are working.
Assuming .NET, the initial refactoring should be very minimal.
The classes that implement the original interface already implement it in its entirety.
Once you create the smaller interfaces, you just change:
public class SomeProvider : IAmAHugeInterface { … }
with:
public class SomeProvider : IProvideA, IProvideB, IProvideC, IProvideD { … }
…and your code runs exactly the way it did before, as long as you haven't added or removed any members from what was there to begin with.
From there, you can whittle down the classes on an as-needed or as-encountered basis and remove the extra methods and interfaces from the declaration.
Is it correct that most if not all of the classes which implement this single big interface have lots of methods which either don't do anything or throw exceptions?
If that isn't the case, and you have great big classes with lots of different concerns bundled into it then you will be in for a painful refactoring, but I think handling this refactoring now is the best approach - the alternatives you suggest simply push you into different bad situations, deferring the pain for little gain.
One thing to can do is apply multiple interfaces to a single class (in most languages) so you can just create your new interfaces and replace the single big interface with the multiple smaller ones:
public class BigNastyClass : IBigNastyInterface
{
}
Goes to:
public class BigNastyClass : ISmallerInferface1, ISmallerInterface2 ...
{
}
If you don't have huge classes which implement the entire interface, I would tackle the problem on a class by class basis. For each class which implements this big interface introduce a new specific interface for just that class.
This way you only need to refactor your code base one class at a time.
DriverProvider for example will go from:
public class DriverProvider : IBigNastyInterface
{
}
To:
public class DriverProvider : IDriverProvider
{
}
Now you simply remove all the unused methods that weren't doing anything beyond simply satisfying the big interface, and fix up any methods where DriverProvider's need to be passed in.
I would do the latter. Make the individual, smaller interfaces, and then make the 'big' interface an aggregation of them.
After that, you can refactor the big interface away in the consumers of it as applicable.