different lazy singletons in dart - flutter

I saw these lazy singleton types in different projects, as far as I know, both are lazy singleton, the second one is better it's already final. But I'm curious about your opinions. What is the difference between them and what do you recommend to use them in different cases?
Thank you in advance.
// SINGLETON 1
class AppConstant1 {
static late AppConstant1? _instance;
AppConstant1._();
static AppConstant1 instance() {
_instance ??= AppConstant1._();
return _instance!;
}
}
// SINGLETON 2
class AppConstant2 {
AppConstant2._();
static final instance = AppConstant2._();
}

It seems that the first singleton pattern isn't even work:
// SINGLETON 1
class AppConstant1 {
static late AppConstant1? _instance;
AppConstant1._();
static AppConstant1 instance() {
_instance ??= AppConstant1._();
return _instance!;
}
}
it produces Uncaught Error: LateInitializationError: Field '_instance' has not been initialized. exception.
The reason for this is that :
_instance ??= AppConstant1._();
actually means
_instance = _instance ?? AppConstant1._();
In other words before assigning _instance we are reading _instance but _instance hasn't been initialized yet. Hence the error. So the code should be something like this:
class AppConstant1 {
static AppConstant1? _instance;
AppConstant1._();
static AppConstant1 instance() {
_instance ??= AppConstant1._();
return _instance!;
}
}
The main difference between first and second variants is that in first variant we could initialize singleton in particular moment and we can provide dependences as method arguments. That's it.

Related

Singleton with dependency injection in Dart

Can anyone help me with using dependency injection on singleton?
here is my code:
class SpaceController extends ChangeNotifier {
final SpacesPersistence _persistence;
static final SpaceController _instance = SpaceController._internal(persistence: null);
factory SpaceController({required SpacesPersistence persistence}) {
return _instance;
}
SpaceController._internal({required SpacesPersistence persistence}) : _persistence = persistence;
}
abstract class SpacesPersistence {
Future<List<Entry>> getPath();
Future<List<Entry>> getEntries(Entry? parent, [String? keyword]);
Future<List<Connection>> getConnections([List<Entry>? entries]);
Future<void> add(Entry entry);
Future<void> remove(Entry entry);
Future<void> update(Entry entry);
Future<void> setPath(List<Entry> path);
}
This problem is that I want to inject the persistence variable, but _internal requires the argument and _persistence is not ready yet.
Try with passing new instance in _instance. Here SpacesPersistenceImpl is a class which implements SpaceController
class SpaceController extends ChangeNotifier {
final SpacesPersistence _persistence;
static final SpaceController _instance = SpaceController._internal(persistence: SpacesPersistenceImpl());
factory SpaceController() {
return _instance;
}
SpaceController._internal({required SpacesPersistence persistence}) : _persistence = persistence;
}
I simply grab a RiverPod provider, which gives me lazy initialization, discoverability as a singleton, and dependency injection via overrides for mocking during testing. Details at http://riverpod.dev/

Read provider inside a global or static method in Flutter

I have a question, regarding reading providers from inside static methods or global methods. I am using riverpod and awesome_notification packages, and I need to alter the state the app, from the action of the notification, for this, the package uses static methods inside a controller class.
class NotificationController{
...
static Future<void> onActionReceivedMethod(ReceivedAction receivedAction) async {
...//some way to access a provider, to call methods on it
}
...
}
If there is another way of doing this that I am not seeing, please let me know.
I have not been able to find a way to do this.
You can:
Pass to the ref function as a parameter.
static Future<void> onActionReceivedMethod(ReceivedAction receivedAction, Ref ref) async {
final some = ref.read(someProvider);
}
Create a class that accepts the ref field in the constructor.
final notificationProvider = Provider((ref) => NotificationController(ref));
// or use tear-off
final notificationProvider = Provider(NotificationController.new);
class NotificationController {
NotificationController(Ref ref) {
_ref = ref;
}
static late final Ref _ref;
static Future<void> onActionReceivedMethod(ReceivedAction receivedAction) async {
final some = _ref.read(someProvider);
}
}
An additional example:
import 'package:riverpod/riverpod.dart';
final valueProvider = Provider<int>((_) => 5);
final managerProvider = Provider(ManagerProvider.new);
class ManagerProvider {
ManagerProvider(Ref ref) {
_ref = ref;
}
static late final Ref _ref;
static int getValue() => _ref.read(valueProvider);
}
void main() {
final container = ProviderContainer();
container.read(managerProvider);
final value = ManagerProvider.getValue();
print(value); // 5
}
Either way, you should always have access to `Ref'.
Update:
As #OppositeDragon and #Eran Ravid pointed out, we really can't access _ref in a static method. However, if you define _ref in the constructor, it is possible. I think it's a terrible anti-pattern, though. Use method 1 and you will be fine.

what is wrong here? (_instance' has not been initialized)

How I can initialize _instance?
I get "Field '_instance' has not been initialized"
Or any other ways to get rid of nullSafety,,,
here is the code:
class ShoppingBasketData{
static late ShoppingBasketData _instance ;
late List<product> _basketItem;
ShoppingBasketData(){
_basketItem =<product>[];
}
List<product> get basketItem => _basketItem;
set basketItem(List<product> value) {
_basketItem = value;
}
static ShoppingBasketData getInstance(){
if (_instance == null){
_instance = ShoppingBasketData();
}
return _instance;
}
}
What is wrong is that you declared _instance to be late. What that means is that you as a developer promise to initialize it before you access it. If you break that promise, you get an exception.
It seems that you want null to be a valid value, so what you need to do is make it nullable:
static ShoppingBasketData? _instance
You may also want to look into How do you build a Singleton in Dart? so you don't have to reinvent the wheel, and you may want to look into Flutter state management, because singleton is probably the worst of all options.

How can I create a non-modifiable class Flutter

I need to create a class called GeneralAppAndDeviceDetails which has the following fields:
import 'package:flutter/material.dart';
class GeneralAppAndDeviceDetails {
final bool isDarkModeEnabled;
final double deviceWidth;
final double deviceHeight;
final Color primaryColor;
final Color secondaryColor;
}
It basically stores general device and app information at the start of the program so that I don't need to create a new instance of Theme or MediaQuery class whenever I want to access these details.
The problem I'm facing is that how can I write this class so that after the fields' values are assigned, They will be unmodifiable? (so that nothing can change the field values)
(I tried to create a singleton class but I need to pass the values to the constructor and by using factory and private constructor, A user can create new classes with different parameters passed to the factory.)
The thing I need is to have static fields that can receive a value once and become unmodifiable after that. How can I achieve something similar?
Thank you
Update:
I wrote the class as below:
import 'package:flutter/material.dart';
class GeneralAppAndDeviceDetails {
final bool isDarkModeEnabled;
final double deviceWidth;
final double deviceHeight;
final Color primaryColor;
final Color secondaryColor;
static bool _isAlreadyCreated = false;
static GeneralAppAndDeviceDetails _instance;
factory GeneralAppAndDeviceDetails(bool isDarkModeEnabled, double deviceWidth,
double deviceHeight, Color primaryColor, Color secondaryColor) {
if (_isAlreadyCreated == false) {
_isAlreadyCreated = true;
_instance = GeneralAppAndDeviceDetails._(isDarkModeEnabled, deviceWidth,
deviceHeight, primaryColor, secondaryColor);
}
return _instance;
}
const GeneralAppAndDeviceDetails._(this.isDarkModeEnabled, this.deviceWidth,
this.deviceHeight, this.primaryColor, this.secondaryColor);
}
I use a flag to check if an instance was created before or not in here and with this code, a similar instance will be returned every time but is it the best way to achieve this?
This is your singleton class
class Test{
final String str;
static Test _singleton;
Test._internal({this.str});
factory Test(String str) {
return _singleton ??= Test._internal(
str: str
);
}
}
example code for you to try and test
void main() {
Test obj = Test('ABC');
print(obj.str);
Test obj1 = Test('XYZ');
print(obj1.str);
}
class Test{
final String str;
static Test _singleton;
Test._internal({this.str});
factory Test(String str) {
return _singleton ??= Test._internal(
str: str
);
}
}
try running this in dartpad for better understanding
you can make the class as singleton and then make these fields as private, accessible only through getters and setters, inside the setter you can check and discard the new value if there is already some value assigned to the field.

The instance member 'params' can't be accessed in an initializer

class LevelUp extends GetxController {
Map<String, String> params = Get.arguments;
var myTest = params.[comLevel];
}
Error report--"The instance member 'params' can't be accessed in an initializer." I am new to programming and this is being called directly from a widget. I checked the LevelUp map and it has contents. The error occurs where I am trying to assign the param value to myTest. It doesn't matter if I put the key in quotes or provide an integer. Any advice would be greatly appreciated.
You can't access params before you've initialized the object. To fix your example, move your myTest initialization into a constructor.
Also, I don't believe you should have a period before [comLevel].
class LevelUp extends GetxController {
Map<String, String> params = Get.arguments;
String myTest;
LevelUp() {
myTest = params[comLevel];
}
}
Null safety update:
Use late keyword: Dart 2.12 comes with late keyword which helps you do the lazy initialization which means until the field bar is used it would remain uninitialized.
class Test {
int foo = 0;
late int bar = foo; // No error
}
Although this question has been answered for the OP's case, I want to offer a solution to those receiving this error in a StatefulWidget scenario.
Consider a situation where you would want to have a list of selectable items that dictate which category to display. In this case, the constructor might look something like this:
CategoryScrollView({
this.categories,
this.defaultSelection = 0,
});
final List<String> categories;
final int defaultSelection;
Note the property defaultSelection is responsible for specifying which category should be selected by default. You would probably also want to keep track of which category is selected after initialization, so I will create selectedCategory. I want to assign selectedCategory to defaultSelection so that the default selection takes effect. In _CategoryScrollViewState, you cannot do the following:
class _CategoryScrollViewState extends State<CategoryScrollView> {
int selectedCategory = widget.defaultSelection; // ERROR
#override
Widget build(BuildContext context) {
...
}
}
The line, int selectedCategory = widget.defaultSelection; will not work because defaultSelection it hasn't been initialized yet (mentioned in other answer). Therefore, the error is raised:
The instance member 'widget' can't be accessed in an initializer.
The solution is to assign selectedCategory to defaultSelection inside of the initState method, initializing it outside of the method:
class _CategoryScrollView extends State<CategoryScrollView> {
int selectedCategory;
void initState() {
selectedCategory = widget.defaultSelection;
super.initState();
}
A simple example, where it shows how we can resolve the above issue,
Example: Create an instance of class B, and pass an instance of class A in the parameter of it
WRONG(Compile time error of initializer):
final A _a = A();
final B _b = B(_a);
shows error: The instance member '_a' can't be accessed in an initializer.
Right:
final A _a = A();
late final B _b;
AppointmentRepository() {
_b = B(_a);
}
#100% working solution
:
Juts place var myTest = params.[comLevel];
below your Build method.
eg.
class LevelUp extends GetxController {
Map<String, String> params = Get.arguments;
Widget build(BuildContext context) {
var myTest = params.[comLevel];
}
}
For me it happened Because i was trying to access a Property of a class instance (Lets Say Class A ) And Use this property to initialize Another Class (Class B) , The Property Was Integer Number and Was Defined
However , Since i didn't Make an Object from "Class A" I can access those propertied Belong to it !
I tried to use this property inside the "Build" Method so that an object is "Created/Built" And it Worked !
I also got the similar error.
And I found the solution as follows.
My first code:
final BuildContext mycontext = GlobalContextClass.navigatorKey.currentContext;
final PsValueHolder psValueHolder = Provider.of<PsValueHolder>(mycontext, listen: false);
Next is the code where the error is fixed:
final PsValueHolder psValueHolder = Provider.of<PsValueHolder>(GlobalContextClass.navigatorKey.currentContext, listen: false);
Instead of defining 2 variables in a row, I placed the first variable directly in the place of the 2nd variable.
Another solution is making your variable, a GetX parameter.
int count_myProducts = cartItems.length; //The instance member 'cartItems' can't be accessed in an initializer. (Documentation)
int get count_myProducts => cartItems.length;
see this video at 27:34
GetX State Management tutorial with Flutter
https://www.youtube.com/watch?v=ZnevdXDH25Q&ab_channel=CodeX
Just carry
var myTest = params.[comLevel];
into Widget build{} below.
like this :
class LevelUp extends GetxController {
Map<String, String> params = Get.arguments;
Widget build(BuildContext context) {
var myTest = params.[comLevel];
}
}