Non-nullable instance field '_repository' must be initialized - flutter

I'm a beginner in flutter, and trying to follow a tutorial.
I'm stuck in the below error :
I have the below code in chat_service.dart that is used to get an API from Laravel/Mysql :
import 'package:buschat/repository/repository.dart';
class ChatService {
Repository _repository;
ChatService() {
_repository = Repository();
}
getAllChats() async {
return await _repository.httpGet('get-all-chat');
}
}
I'm receiving the following error in ChatService() method:
Non-nullable instance field '_repository' must be initialized.
Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'
The tutorial is working fine, and it's one year old.

You should try....
class ChatService {
Repository _repository = Repository();
ChatService() {
getAllChats();
}
getAllChats() async {
return await _repository.httpGet('get-all-chat');
}
}

Because of null-safety, all variables must be initialized unless they are marked as nullable. So you need to either give a value to your variable or use late keyword which means you need give it value later but you can't use it as null:
late Repository _repository;
or
// not recommended use (you need to use dependency injection)
Repository _repository = Repository();
You can also mark you variable nullable so you can use it as null:
Repository? _repository;

Related

Using Riverpod with Objects - always reinstantiate once property changes?

I am making first steps with Riverpod and just want to check if my understanding of handling changes of some data class properties using Riverpod is correct.
Imagine, I have a data class like that:
class MyClass {
final String name;
final int id;
const MyClass(this.name, this.id);
}
Then I create a StateNotifier:
class MyClassStateNotifier extends StateNotifier<MyClass> {
MyClassStateNotifier(MyClass state) : super(state);
void setName(String name) {
state.name = name;
}
}
And this won't work - UI will not be rebuilt after calling setName this way.
So I need to modify classes in the following way:
class MyClass {
final String name;
final int id;
const MyClass(this.name, this.id);
MyClass copyWith({name, id}) {
return MyClass(name ?? this.name, id ?? this.id);
}
}
and the StateNotifier as following:
class MyClassStateNotifier extends StateNotifier<MyClass> {
MyClassStateNotifier(MyClass state) : super(state);
void setName(String name) {
state = state.copyWith(name: name);
}
}
This pair will work and the UI will be rebuilt.
So, my question: does one always need to reinstantiate the object in this way?..
From my perspective, this is a bit strange (simple datatypes like String / int do not require this) and the boilerplate for copyWith method might become pretty huge if I have a dozen of object's properties.
Is there any better solution available for Riverpod or is it the only one and correct?..
Thanks in advance! :)
To trigger a state change you have to use the state setter. The implementation looks like this:
#protected
set state(T value) {
assert(_debugIsMounted(), '');
final previousState = _state;
_state = value;
/// only notify listeners when should
if (!updateShouldNotify(previousState, value)) {
return;
}
_controller?.add(value);
// ...
The internal StreamController<T> _controller needs to be triggered (add), to notify listeners (in this case riverpod) about updates.
By using state.name = something you're not informing the StateNotifier about a new state (not calling the state setter). Only your object holds the new value but nobody was notified.
Your state is mutable and that very often leads to such misbehavior. By using an immutable object you can prevent such errors in the first place. Write it yourself or use freezed.
Learn more about immutability in my talk

Riverpod create provider

I want to create provider that will return String with Riverpod. I'm using Provider as below. I'm getting an error The instance member 'name' can't be accessed in an initializer. Try replacing the reference to the instance member with a different expression. May someone tell me how should I create provider?
class LearnUser {
final String uid;
final String name;
final usernameProvider = Provider((ref) {
return name;
});
LearnUser({required this.uid});
}
I would separate the provider from the LearnUser class. It gives cleaner separation of concerns in my opinion. And what i understood from the Riverpod docs they are ment as global vars. Like the above poster mentioned your code won't work cause you are trying to access an instance variable.
Maybe something like this with a user service. I would also advice you look into StateNotifier with StateNotifierProvider you could use for example to create a user service.
// Creating the provider as global variable
final userNameProvider = Provider<String>((ref) =>
ref.watch(userService.state)?.user.name ?? '';
);
'name' is instance member. You need an instance to access a member variable.
You can try this
class LearnUser {
final String uid;
String name;
Provider usernameProvider;
LearnUser({#required this.uid}){
this.usernameProvider = Provider((ref) {
return this.name;
});
}
}

Unable to Access This in a Field Initializer to Read 'property"

I am trying to authenticate a user using bloc pattern and repository. I have also used GetIt to inject some of my dependencies like below:
final getItInstance = GetIt.I;
Future init(){
getItInstance.registerLazySingleton<APIClient>(() => APIClient());
getItInstance.registerLazySingleton<UserRemoteDataSource>(
() => UserRemoteDataSourceImpl(client: getItInstance()));
// commented out previously, getItInstance.registerLazySingleton<UserRepository>(
() => UserRepositoryImpl(dataSource: getItInstance()));
}
and the implementations of the faulty UserRepository classes are:
abstract class UserRepository {
Future<UserModel> loginUser(Map<String, dynamic> body);
Future<UserModel> registerUser(Map<String, dynamic> body);
Future<UserModel> getCurrentUser();
Future<void> logOut();
}
The UserRepositoryImpl class is just a wrapper to implement the above methods and connect with the remote data source via http, so has been omitted. From the DI class, one can easily see the dependencies and dependents, I have omitted them for brevity's sake.
Now, in my auth bloc, I am trying to pass the UserRepository and UserRepositoryImpl to the bloc constructor, to facilitate the api call but I get this error:
lib/presentation/blocs/authentication/authentication_bloc.dart:18:42: Error: Can't access 'this' in a field initializer to read '_repository'.
: _repository = repository, assert(_repository != null),
^^^^^^^^^^^
Here's the bloc constructor:
class AuthenticationBloc
extends Bloc<AuthenticationEvent, AuthenticationState> {
final UserRepository _repository;
AuthenticationBloc(UserRepositoryImpl repository)
: assert(_repository != null), _repository = repository,
super(AuthenticationStateInitial());
... other methods etc
}
Please, what does this mean and how do I rectify it? Thanks
I have realized my mistake, in the constructor, instead of asserting the constructor params to be non null, I was checking for the field value of the repository. Here's the correction from:
final UserRepository _repository;
AuthenticationBloc(UserRepositoryImpl repository)
: assert(**_repository** != null), _repository = repository,
super(AuthenticationStateInitial());
to:
final UserRepository _repository;
AuthenticationBloc(UserRepositoryImpl repository)
: assert(**repository** != null), _repository = repository,
super(AuthenticationStateInitial());
** signifies where the change was made in the two code blocks. Hope it can help someone too.

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];
}
}

Flutter object is not initialized after initState

I'm new to flutter, and i bumped into a problem.
I have a Feed model in my app that looks like this:
import 'package:uuid/uuid.dart';
class Feed {
// Static Members
var uuid = new Uuid();
// Members
String id;
bool isScheduled;
DateTime createdTime;
DateTime feedingTime;
String deviceId;
// Constructors
Feed({this.feedingTime, this.deviceId, this.isScheduled}) {
id = uuid.v4();
createdTime = DateTime.now();
}
Feed.fromDevice(deviceId) {
Feed(deviceId: deviceId, feedingTime: DateTime.now(), isScheduled: false);
}
}
Now i have my AddFeedForm that i'm trying to initialize with default values, in the InitState:
class _AddFeedFormState extends State<AddFeedForm> {
// Final Members
final _formKey = GlobalKey<FormState>();
final List<Machine> _devices = machinesFromServer;
// Members
Feed _feed;
#override
void initState() {
_feed = Feed.fromDevice(_devices.first.id);
super.initState();
}
But somehow after the initState the _feed parameter stays null!
Any ideas?
But somehow after the initState the _feed parameter stays null!
Are you sure this is the case, and not that you're getting a Feed instance that has null fields?
It looks like your named constructor is incorrect:
Feed.fromDevice(deviceId) {
Feed(deviceId: deviceId, feedingTime: DateTime.now(), isScheduled: false);
}
Here you're calling the default Feed constructor inside a named constructor, but not doing anything with the result - this is creating another Feed and then throwing it away. The one returned by the named constructor has not been initialised.
What you probably wanted was this:
Feed.fromDevice(deviceId):
this(deviceId: deviceId, feedingTime: DateTime.now(), isScheduled: false);
This makes the fromDevice constructor call the default constructor for initialisation of the instance, rather than creating another copy that goes unused.
Another option would be to make it a static method:
static fromDevice(deviceId) {
return Feed(deviceId: deviceId, feedingTime: DateTime.now(), isScheduled: false);
}
There wouldn't be much difference in this case.. Constructors seem nicer, but sometimes you might find that you want to a) make initialisation async (static methods can return a Future<Feed> but constructors cannot or b) do more processing of the arguments before they're passed to the real constructor that might not fit nicely in the initialiser call.