ChangeNotifier can't no longer be used in Riverpod? - flutter

I am a beginner in Flutter, and I am trying to use Riverpod. here is the sample code
import "package:flutter_riverpod/flutter_riverpod.dart";
class CounterNotifier extends ChangeNotifier {
int _value = 0;
int get value => _value;
void incrementValue() {
_value++;
notifyListeners();
}
}
but I have error, I can't find ChangeNotifier and notifyListeners() like this
in pubspec.yaml file, I am using flutter_riverpod: ^0.14.0+3
am I misising something?

ChangeNotifier is a Flutter class, not a Riverpod one; when working with it, you have to ensure you have imported it.
It gets bundled in with common imports such as package:flutter/material.dart, and as Reign mentioned in his comment, can be directly imported via package:flutter/src/foundation/change_notifier.dart.

Related

How to unit test a class that is created by provider?

So let's say I have a Counter class like this
class Counter extends ChangeNotifier {
int _i = 0;
int get myCounter => _i;
void increment() {
_i++;
notifyListeners();
}
void decrement() {
_i--;
notifyListeners();
}
}
I want to write a test file for it, so I expose its instance like this. The problem is, after I expose it, how do I access the instance of the class I just created? Like say, I increased _i value through a button, how will I access the instance that is created by Provider in order to test it?
I was looking to do the same but then I found this answer https://stackoverflow.com/a/67704136/8111212
Basically, you can get the context from a widget, then you can use the context to get the provider state
Btw, you should test a public variable like i instead of _i
Code sample:
testWidgets('showDialog', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(home: Material(child: Container())));
final BuildContext context = tester.element(find.byType(Scaffold)); // It could be final BuildContext context = tester.element(find.byType(Container)) depending on your app
final Counter provider = Provider.of<Counter>(context, listen: false);
expect(provider.i, equals(3));
});
You first initialize the Provider in your main.dart file using
ChangeNotifierProvider
after that you can use the class anywhere in your code by either using the Consumer widget or by using:
final counter = Provider.of<Counter>(context)
Here is a good post/tutorial about how to use Provider

How to access/inject ObjectBox database in repository in Flutter - Reso Coder DDD

all the examples, I have seen, initialize ObjectBox in a State(less/full)Widget. I am using a layered architecture (currently refactoring to DDD) and wonder, how to inject my ObjectBox properly.
In my repository, I inject the data sources using the injectable and the getit packages with
#injectable
#LazySingleton (as: IJournalsRepository)
class JournalsRepository implements IJournalsRepository {
final JournalsRemoteDataSource journalsRemoteDataSource;
final JournalsLocalDataSource journalsLocalDataSource;
JournalsRepository(this.journalsLocalDataSource, this.journalsRemoteDataSource);
Those packages then create an instance of JournalsRemoteDataSource and of JournalsRemoteDataSource and inject it into the repository.
The ObjectBox example shows for initialization
class _MyHomePageState extends State<MyHomePage> {
Store? _store;
#override
void initState() {
super.initState();
openStore().then((Store store) => _store = store;);
}
#override
void dispose() {
_store?.close(); // don't forget to close the store
super.dispose();
}
}
So I am lacking an idea on how an injector could initialize ObjectBox or how I could access the objectBox object from within the injected JournalsRemoteDataSource if I would initialize objectBox in MyApp() (which is upstream to the HomePage)
PS: reopening the box in JournalsRemoteDataSource on every read/write event has a very poor performance
========== UPDATE ==========
supplementing my comment to #vaind
I have found your answer on this similar question in the meantime (not sure why I did not see it, initially). I hope to get this approach working here, too. However, I have still issues initializing the store. My prototype comes from Firestore and looks like this:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:injectable/injectable.dart';
#module
abstract class FirebaseInjectableModule {
#lazySingleton
FirebaseAuth get firebaseAuth => FirebaseAuth.instance;
}
though I do not understand where the getter firebaseAuth comes from and haven't found any explanation, yet. Anyways, I adapted that to
import 'package:injectable/injectable.dart';
import 'package:objectbox/objectbox.dart';
import 'package:test/objectbox.g.dart';
#module
abstract class ObjectboxInjectableModule {
#lazySingleton
Future<Store> get store async => await openStore();
}
and use this with
#LazySingleton (as: ILocalDataSource)
class ObjectBoxDataSource implements ILocalDataSource {
final Store _store;
final Box<JournalOboxEntity> _box;
ObjectBoxDataSource(this._store) : _box = _store.box();
Besides final Store _store being grey in IntelliJ (unused variable), I receive the error
You tried to access an instance of Store that is not ready yet
'package:get_it/get_it_impl.dart':
Failed assertion: line 404 pos 9: 'instanceFactory.isReady'
So following another answer of vaind, I implemented this as follows. My architecture follows a merge of Reso Coder's DDD and Clean Architecture tutorials. Basically it is DDD with the local/remote data source layer of Clean Architecture.
INFRASTRUCTURE directory
abstract data sources
abstract class ILocalDataSource {
Future<JournalDto> getJournal(int id);
Future<void> storeJournal(JournalDto record);
}
abstract class IRemoteDataSource {
Future<JournalDto> getJournal(int problemClassId);
}
data source implementation
#LazySingleton (as: ILocalDataSource)
class ObjectBoxDataSource implements ILocalDataSource {
final Store _store;
final Box<JournalOboxEntity> _box;
ObjectBoxDataSource(this._store) : _box = _store.box();
injectable module in infrastructure/core
#module
abstract class ObjectBoxInjectableModule {
#preResolve // <<<<<<<<<<<<< needed for async init
#lazySingleton
Future<Store> get store async => await openStore();
}
And now the trick to get it work: My later errors where caused by an injector init not yet finished. After changing injection.dart in the root folder to a Future and awaiting the call in main(), it worked. injection.dart now looks like this:
final GetIt getIt = GetIt.instance;
#injectableInit
Future<void> configureInjection(String env) async {
$initGetIt(getIt, environment: env);
}
I don't have experience with packages get_it & injectable, but from the docs, I think the following alternatives would work. Using get_it directly, not sure about the right way to achieve the same with injectable (generator for get_it) but I guess if you're familiar with it you can configure it to generate the same code.
Alternative A, lazy (async) singleton
GetIt.I.registerSingletonAsync<Store>(openStore);
Alternative B, setup in main(), probably preferrable
change your main to sth like:
void main() async {
GetIt.I.registerSingleton<Store>(await openStore());
runApp(MyApp());
}
Note: Looks like get_it provides a way to reset, which would result in reopening the same store. To avoid issues if you use that, you'd also need to implement a version of get_it's dispose that calls store.close().

ChangeNotifier isn't recognized by provider package

so I have this simple code:
import 'package:provider/provider.dart';
class DataModel with ChangeNotifier{
bool _isLoading = true;
set isLoading(bool value){
_isLoading = value;
notifyListeners();
}
get isLoading => _isLoading;
}
ChangeNotifier and notifyListeners() aren't recognized.
My dependencies:
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
provider: ^4.3.3
I'm very confused as to why is it happening, this is the exact setup in the installation page (and it worked in other projects).
This project is also connected to a git lab project, I don't know if it is related.
btw, it's not like that with other keywords that are in the provider package - it perfectly recognizes ChangeNotifierProvider i.e
You are importing an incorrect package, the correct one is package:flutter/foundation.dart or alternatively package:flutter/material.dart, try with:
import 'package:flutter/material.dart';
class DataModel with ChangeNotifier{
bool _isLoading = true;
set isLoading(bool value){
_isLoading = value;
notifyListeners();
}
get isLoading => _isLoading;
}
package:provider/provider.dart is used in the files where the calls to the provider are made, not where it is defined.
See the example from the docs: https://github.com/flutter/samples/blob/master/provider_shopper/lib/models/cart.dart
A hint, in VS Code if you right click on ChangeNotifier and select Go to Definition, the definition can be traced to the foundation package.

Multiple questions related to Flutter basic

I am coming from React and trying to understand Flutter. There are some differences between dart vs JS and React vs Flutter. However, it seems a bit similar to each other. However, there is some confusion toward Flutter.
void
It does not return the value. Therefore, it is used to void main(). Does it mean like the function 'main()' will be executed and I will never be able to reuse void main()?
<>
In the code, I have here
class LoginScreen extends StatefulWidget {
createState() {
return new LoginScreenState();
}
}
class LoginScreenState extends State<LoginScreen> {
Widget build(context) {
return Container(
margin: EdgeInsets.all(20.0),
child: Column(
children: [
emailField(),
// passwordField(),
// submitButton(),
],
));
}
}
I can see that State<LoginScreen> is related to class LoginScreen. It is an extension of the LoginScreen State?
Future<int> successor = future.then((int value) {
},
However, in here Future <----- I do not understand why it has to state 'int' in this code. What is the proper use of <>?
Stateful vs Stateless
Can I just use the Stateful all the time even if I do not have any state like React? Where there is no difference?
You need to learn basic dart.
void main() is same as main(). The only difference is declaring the return type. main() is called only once by the flutter engine.
Read about generics. <> is the called the diamond operator and is used to specify the type of something. Future<int> means the function will return something in future and it will strictly be of int type. If you don't specify the type flutter will treat it as dynamic.
You can use stateful classes if you are using state management packages like provider or rxdart to maintain state.
class LoginScreen extends StatefulWidget {
createState() {
return new LoginScreenState();
}
}
enter code here
changed to
class LoginScreen extends StatefulWidget {
LoginScreenState createState()=>LoginScreenState();
}
}
enter code here
Stateful vs Stateless`enter code here`
stateful has state object and stateless has no state object.using stateful you can changes widget data .on other hand in stateful you cant

Flutter bloc - Getting data from DB asynchronously

I am making an app using the BLoC architecture using the flutter_bloc package, but I need to get data from a database, which is asynchronous, meaning I can't initialize the BLoC with data from my database. Is there a way I can do this? My BLoC class text is
import 'package:countdown/database/utils.dart';
import 'package:countdown/events/countdown_event.dart';
import 'package:countdown/models/countdown.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class HomeBloc extends Bloc<CountdownEvent, List<Countdown>> {
#override
// TODO: implement initialState
List<Countdown> get initialState => DatabaseUtils.getCountdowns();
#override
Stream<List<Countdown>> mapEventToState(CountdownEvent event) {
}
}
I know this is very similar to This Question, but that question's answers don't have any code snippets which would be very helpful.
create one separate async function and call if from initState(). this is my code you can call your method according to use.
#override
HomeState get initialState {
_checkLocationSettings();
return InitialHomeState();
}
_checkLocationSettings() async {
locationUpdateSetting = await repository.isLocationUpdateOn();}