Singleton with dependency injection in Dart - flutter

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/

Related

Combining Riverpod Providers Bidirectionally

How can we access a method from the being wrapped riverpod provider?
ContentProvider can access user value from UserProvider by using "watch". There is no problem for this direction. On the other hand, UserProvider also needs access to the methods of ContentProvider. So bidirectional communication is required.
For this case, I need to call deleteContents method from UserProvider.
I don't prefer to merge them to keep logic safe.
class ContentProviderNotifier extends ChangeNotifier {
final User? currentUser;
ContentProviderNotifier({required this.currentUser});
addContent(Content content) {
content.user = currentUser?.name;
...
}
deleteContents() {
...
}
}
final contentProvider = ChangeNotifierProvider<ContentProviderNotifier>(
(ref) {
final user = ref.watch(userProvider).currentUser;
return ContentProviderNotifier(currentUser: user);
},
);
class UserProviderNotifier extends ChangeNotifier {
UserProviderNotifier();
User? currentUser;
deleteUsers(){
// here to call a method from contentProvider
deleteContents();
}
}
final userProvider = ChangeNotifierProvider<UserProviderNotifier>(
(ref) {
return UserProviderNotifier();
},
);
If I try to feed UserProvider with ContentProvider like this
final userProvider = ChangeNotifierProvider<UserProviderNotifier>(
(ref) {
final content = ref.watch(contentProvider); // <----
return UserProviderNotifier(content);
},
);
But I know, It won't make sense.
The type of 'userProvider' can't be inferred because it depends on itself through the cycle: contentProvider, userProvider.
Try adding an explicit type to one or more of the variables in the cycle in order to break the cycle.darttop_level_cycle
You can create UserProviderNotifier so it takes ref as an input, like this:
class UserProviderNotifier extends ChangeNotifier {
UserProviderNotifier(this.ref);
final Ref ref;
deleteUsers() {
// here to call a method from contentProvider
ref.watch(contentProvider.notifier).deleteContents();
}
}
final userProvider = ChangeNotifierProvider<UserProviderNotifier>(
(ref) {
return UserProviderNotifier(ref);
},
);
This section of the Riverpod docs mentions this is a common use-case.

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.

different lazy singletons in dart

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.

A value of type 'Future<AppDatabase>' can't be assigned to a variable of type 'AppDatabase'

I am trying to create a persistent interface which forks db calls to floor or another self made web db static store.
Anyway...
The interface part is looking like this:
peristent_interface.dart
import 'package:flutter/material.dart';
import 'package:mwork/database/floor/entities/map_location_entity.dart';
import 'package:mwork/database/floor/result/map_location_result.dart';
import 'persistent_stub.dart'
if(dart.library.io) 'persistent_native.dart'
if(dart.library.js) 'persistent_web.dart';
abstract class Persistent extends ChangeNotifier {
static Persistent? _instance;
static Persistent? get instance{
_instance ??= getPersistent();
return _instance;
}
Future<List<MapLocationResult?>?> getMapLocations();
Future<MapLocationResult?> getMapLocation({int id});
Future<void> insertReplaceMapLocation(MapLocation mapLocation);
Future<void> insertReplaceMapLocations(List<MapLocation> mapLocations);
}
All seems nice so far, but the trouble appears when the init() function below returns Future<AppDatabase> not AppDatabase as I want.
persistent_native.dart
import 'package:floor/floor.dart';
import 'package:mwork/database/floor/database/database.dart';
import 'package:mwork/database/floor/entities/map_location_entity.dart';
import 'package:mwork/database/floor/result/map_location_result.dart';
import 'package:mwork/services/persistent/persistent_interface.dart';
import 'package:mwork/common/m_work_config.dart' as m_work_config;
Persistent getPersistent() => PersistentNative();
class PersistentNative extends Persistent {
final AppDatabase _appDatabase = init(); //<-- Fails here !!
static Future<AppDatabase> init() async {
return await $FloorAppDatabase.databaseBuilder(m_work_config.mWorkFloorDb).build();
}
#override
Future<List<MapLocationResult?>?> getMapLocations() async {
return await _appDatabase.mapLocationDao.getMapLocations();
}
#override
Future<MapLocationResult?> getMapLocation({int id=-1}) async {
return await _appDatabase.mapLocationDao.getMapLocation(id);
}
#override
Future<void> insertReplaceMapLocation(MapLocation mapLocation) async {
_appDatabase.mapLocationDao.insertMapLocation(
mapLocation
);
}
#override
Future<void> insertReplaceMapLocations(List<MapLocation> mapLocations) async {
_appDatabase.mapLocationDao.insertMapLocations(
mapLocations
);
}
}
How should I return AppDatabase from init() ?
Maybe you should change the type of the init() function to AppDatabase instead of Future<AppDatabase>? For me it seems that the code is right one and should return AppDatabase.
The init method returns a future, since you wait for it ( and it is a recommended way)
if you would like to return the AppDatabase only, rewrite it as follows::
static AppDatabase init() {
return $FloorAppDatabase.databaseBuilder(m_work_config.mWorkFloorDb).build().then((AppDatabase db) => db);}
Doing this will have some implications though, this wont be awaited meaning that any call depending on this would return late..
I'd recommend using an await clause to the callee,
for example
static Future<AppDatabase> init() async {
return await $FloorAppDatabase.databaseBuilder(m_work_config.mWorkFloorDb).build();
}
and then calling it as::
final AppDatabase db = await (...........);
or:::
YourClass.init().then((AppDatabase db) { /* anything here*/});

How can I access an instance of an object in different classes / pages without a widget tree context?

I am trying to access an instance of an RtcEngine object for AgoraIO from another class/page that doesn't have a widget tree, and therefore no context to refer to with Provider.
First I'm calling initPlatformState() from this class in order to initialize the RtcEngine engine:
class Game extends StatefulWidget {
#override
_GameState createState() => _GameState();
}
class _GameState extends State<Game> implements GameListener {
#override
void initState() {
super.initState();
Agora().initPlatformState(widget.playerId);
}
}
initPlatformState initializes the RtcEngine by creating an instance called engine that I need to use later on to call other methods. This class also contains the method I want to call later using the same instance to adjustVolume...
class Agora {
RtcEngine engine;
// Initialize the agora app
Future<void> initPlatformState(int playerId) async {
RtcEngine engine = await RtcEngine.create(APP_ID);
}
void adjustVolume(int uid, int volume) {
engine.adjustUserPlaybackSignalVolume(uid, volume);
}
}
This is the class that I want to call adjustVolume from. I was considering using Provider to pass the instance to this class but it extends another class and it doesn't have a widget tree with context so I'm not sure how thats possible or if there is a better way.
class Remote extends Component {
final int id;
Remote(this.id);
#override
void update() {
//this is where I'm trying to access the engine instance that was created to call adjustUserPlaybackSignalVolume method
}
}
Any suggestions on how to reuse that instance of "engine" given my situation would be greatly appreciated!