I'm currently working on a simple project crypto_wallet. State management (BLoC) and Value Equality (freezed) creating CRUD operation of DB (Firebase) and in the watch method, I use StreamSubcription code is :
#injectable
class CoinWatcherBloc extends Bloc<CoinWatcherEvent, CoinWatcherState> {
final ICoinRepository _repository;
CoinWatcherBloc(this._repository, this._coinStreamSubscription) :
super(CoinWatcherState.initial());
StreamSubscription<Either<CoinFailure, KtList<CoinEntity>>>? _coinStreamSubscription;
#override
Stream<CoinWatcherState> mapEventToState(CoinWatcherEvent event) async* {
yield* event.map(
watchCoin: (e) async* {
yield CoinWatcherState.loadInProgress();
await _coinStreamSubscription?.cancel();
_coinStreamSubscription = _repository.watchCoin().listen(
(failureOrSuccess) => add(
CoinWatcherEvent.coinsReceived(failureOrSuccess),
),
);
},
coinsReceived: (e) async* {
yield e.failureOrCoin.fold(
(f) => CoinWatcherState.loadFailure(f),
(coin) => CoinWatcherState.loadSuccess(coin),
);
},
);
}
#override
Future<void> close() async {
await _coinStreamSubscription?.cancel();
return super.close();
}
}
And at last I closed the stream. I inject all third party modules on #lazySingleton :
#module
abstract class FirebaseInjectableModule {
#lazySingleton
FirebaseAuth get firebaseAuth => FirebaseAuth.instance;
#lazySingleton
FirebaseFirestore get firebaseFirestore => FirebaseFirestore.instance;
#lazySingleton
GoogleSignIn get googleSignIn => GoogleSignIn();
}
Then It says :
Object/factory with type StreamSubscription<Either<CoinFailure, KtList<CoinEntity>>> is not
registered inside GetIt.
(Did you accidentally do GetIt sl=GetIt.instance(); instead of GetIt sl=GetIt.instance;
Did you forget to register it?)
If I also register this class like this.
#lazySingleton
StreamSubcription get streamSubcription => StreamSubcription();
Then it throws compile time error that abstract classes can't be instantiated like the all Third Party Classes I've registered. How to inject abstract classes? Is there any other way to do this? or I shouldn't use StreamSubcription something else? I'd be thankful <3 :)
You should not add this._coinStreamSubscription to your bloc's constructor. Remove it from the constructor, and declare it as a late final instance.
class CoinWatcherBloc extends Bloc<CoinWatcherEvent, CoinWatcherState> {
final ICoinRepository _repository;
CoinWatcherBloc(this._repository) :
super(CoinWatcherState.initial());
late final StreamSubscription<Either<CoinFailure, KtList<CoinEntity>>>? _coinStreamSubscription;
get_it was trying to inject the abstract streamSubscription since it is in your constructor. And you do not need this. Also, if you look at it from a testing perspective, there is no need to mock the streamSubscription, you can instead mock the class that supplies data to it - which is the repository in this case
I generally just inject the implementation of the abstract class like
#LazySingleton(as:AbstractClass).
Related
I am attempting to convert an old flutter code to a null safety code and encounter a problem with an abstract authentication class using firebase, basically its listening to a authStateChange
abstract class AuthBase {
User get currentUser;
Stream<User> authStateChanges();
....
}
class Auth implements AuthBase {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
#override
Stream<User> authStateChanges() => _firebaseAuth.authStateChanges();
But after running the code it returns A value of type 'Stream<User?>' can't be returned from the method 'authStateChanges' because it has a return type of 'Stream<User>'. error
so what i did was cast it
#override
Stream<User> authStateChanges() => _firebaseAuth.authStateChanges() as Stream<User>;
But now i encounter a new problem type '_AsBroadcastStream<User?>' is not a subtype of type 'Stream<User>' in type cast any advice on how to address this.
_firebaseAuth.authStateChanges()
will give User or null so need to make it nullable as
Stream<User?> authStateChanges() => _firebaseAuth.authStateChanges() as Stream<User?>;
and also need to change in the base class as well as same.
I am getting the error
[core/no-app] No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp()
But in my code I have already initialised it. Plus the android studio compiler says this before the app runs on the mobile
I/FirebaseApp(20998): Device unlocked: initializing all Firebase APIs for app x
My Code:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
var fbApp = await Firebase.initializeApp();
runApp(MyApp());
}
I don't know if main() can be async or not, maybe it is source of your problem. Below is code which work for me.
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
and than i invoke initializeApp() method inside my state class.
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final Future<FirebaseApp> _initFirebaseSdk = Firebase.initializeApp();
// ... rest of my logic ...
}
Initialize firebase without assigning to a variable
When you assign it to a variable, probably the compiler doesn't set it as the default instance.
Also remove the Future<void> and replace just void.
Then you can initialize the firebase app without assigning it to any variable.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
}
If you are trying to access initialized firebase's (concrete)application, you can do it like:
And the same approach could be used for sub-services of the firebase.
FirebaseAuth.instance
I am writing a Firestore Model abstract class that handles common operations. Each model matches a collection in Firestore... I want to refer to that both from a create instance and before the instance exists. But I can't make collectionPath static because I can't override static methods, variables, getters, etc... I get that.
Could create an instance as needed maybe Model().collectionPath but I couldn't get that to work.
Is there a way to do this? How are others making these types of Models?
This is what I'm trying to do:
abstract class Model {
String get collectionPath => "";
void create() async {
CollectionReference collection = FirebaseFirestore.instance.collection(this.collectionPath);
this.reference = await collection.add(this.toMap());
}
static Stream<QuerySnapshot> snapshots() {
return FirebaseFirestore.instance.collection(this.collectionPath).snapshots();
}
//...
class User extends Model {
String get collectionPath => "users";
//...
I want to be able add an existing instance to the firestore:
User user = User("Values");
user.create();
And I'd also like to load all the users before I've created any particular one:
Widget _buildBody(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: User.snapshots(),
I have 4 pages. I called getRide() method in every 4 pages. it's means 4 times database call. Am I right? Is it possible to create a singleton for this scenario?
Firebase Service:
class FirebaseService {
final Firestore _db = Firestore.instance;
Stream<List<RideModel>> getRide() {
return _db.collection('ride')
.snapshots()
.map((list) => list.documents.map((doc) => RideModel.fromFirestore(doc))
.toList());
}
}
Calling Method:
#override
void initState() {
super.initState();
db.getRide().listen(getRide);
}
void getRide(List<RideModel> model) {
if (!mounted) return;
setState(() {
rideModel = model;
});
}
I can't pass rideModel through Navigator. because when change data in ride collection need to change 4 pages UI.
Someone tells me this answer is correct for the above problem.
I found this way to solve this problem.
I used get_it package and create service locator,
GetIt locator = GetIt.instance;
void setupSingletons() async {
locator.registerLazySingleton<FirebaseService>(() => FirebaseService());
}
And then added to the main class
void main() {
setupSingletons();
runApp(MultiProvider(
providers: globalProviders,
child: MyApp(),
));
}
And every screen I added,
class _Screen1 extends State<Screen1> {
// final db = FirebaseService();
FirebaseService db = GetIt.I.get<FirebaseService>();
I am a beginner of programming and dart lang. I have a question about how to write class about database service related to flutter and firestore.
// what is the difference this
class DbService {
final Firestore _db;
DbService() : _db = Firestore.instance;
Future<QuerySnapshot> getDataCollection(String id) {
return _db.collection(id).getDocuments();
}
}
// and this
class DbService {
final Firestore _db = Firestore.instance;
Future<QuerySnapshot> getDataCollection(String id) {
return _db.collection(id).getDocuments();
}
}
// when use this class
_dbService = DbService();
What is the best practice. or should I use singleton to instantiate this class? Any comments or help is appreciate.
They are the same for computer, but they give me different impression.
2nd one is obvious, where 1st one telling me the constructor used to take value, or may be take value in future version.