I'm learning flutter coming from a react background. I want to use my model in another class.
This is my model
class User {
final String id;
final String userName;
User({
this.id,
this.userName,
});
}
On my Widget i want to use the properties of that model, so i can get some type safe.
class _SignUpScreenState extends State<SignUpScreen> {
#override
Widget build(BuildContext context) {
final User _user; // I get an error on this line.
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
I get teh following error.
The value of the local variable '_user' isn't used.
Try removing the variable, or using it.dartunused_local_variable
The final variable '_user' must be initialized.
Try initializing the variable.
I'm a little bit confused how do you properly infer a model on class widget?
Think about the model as a template - the way you are initializing the model - means there is nothing in it, and using the final variable means it will never change.
This causes an error, because the object is effectively null, and always will be.
If you initialize a variable with final you need give it data:
final User _user = User(id: 1, userName: test);
print(_user);
//Prints: Instance of User
Otherwise, don't use the final variable, and you can assign data to _user later"
User _user;
_user = User(id: 1, userName: test);
print(_user);
//Prints: Instance of User
I will explain what your error means one by one.
Flutter will have a warning that you are creating a value that you aren't using.
The value of the local variable '_user' isn't used.
This is what they suggested:
Try removing the variable, or using it.dartunused_local_variable
These two error code is saying that your variable has to be instantiated:
The final variable '_user' must be initialized.
Try initializing the variable.
The solution to your problem is to instansiate User:
final User _user=User(id:"myId",userName:"myUserName");
Note that the final keyword is means that you are declaring a variable that will never have its value change.
Related
I'm trying to add a positional parameter with default value with GetX in Flutter but I got an error.
import 'package:get/get.dart';
class MainParentModel {
int id;
String name;
RxBool isSelected;
MainParentModel(this.id, this.name, {this.isSelected = false.obs});
}
Error: The default value of an optional parameter must be constant.dartnon_constant_default_value
How can I add this default value?
Thank you
.obs is a getter that returns an Rx<T> of whatever type you add it to. But it's not a constant value so that won't work. You'll notice that if you just use a regular bool and remove .obs the error goes away.
If it has to be type RxBool, then I'm pretty sure your only option is making it required and passing in false.obs whenever you create a new MainParentModel.
MainParentModel(this.id, this.name, {required this.isSelected});
Then a new instance would look like this.
final model = MainParentModel(1, 'Bob', isSelected: false.obs);
Another approach is creating reactive objects where you'll need, I never use classes with reactive variables, for example:
class MainParentModel {
int id;
String name;
bool isSelected;
MainParentModel(this.id, this.name, {this.isSelected = false});
}
In the controller:
Rx<MainParentModel> mainParentModel = MainParentModel().obs;
if you need to rebuild the page when changing isSelected variable:
mainParentModel.update((model){
model.isSelected = true;
});
or
mainParentModel.refresh();
This will trigger rebuild all Obx that you use mainParentModel.value.?
in the flutter when you are defining a model. the convention is to define properties as final and write a copyWith for class instead of defining non-final vars and removing the copyWith method. what is the exact reason for this? is it a flutter performance thing?
for example:
class Emplyee {
final String name;
final String id;
Emplyee({required this.name, required this.id});
Emplyee copyWith({String? name, String? id}) {
return Emplyee(id: id ?? this.id, name: name ?? this.name);
}
Map<String, dynamic> toJson() => {
"name": name,
"id": id,
};
Emplyee.fromJson(Map<String, dynamic> json)
: name = json["name"],
id = json["id"];
}
P.S. I know this convention makes sense in widgets. but my question is about data model classes.
It's for Immutability. Mutable class is error-prone.
So basically the copyWith method makes it possible for you to create a new instance of the class and then you can edit whatever you want in this new instance of the class, without affecting data in the original class.
So that's what the copyWith method does, I don't think it's for performance, I think it just aids some coding use cases.
Immutability reduces the risk of errors by side effects. Have a look at this code:
class User {
String name;
User({required this.name});
}
void main() {
final user = User(name: 'Stefan');
someFunction(user);
print(user.name);
}
someFunction(User user){
user.name = 'Thomas';
}
This snippet prints 'Thomas' because the function manipulates the user object. In the main function, you have no chance to know what happens with the object.
With immutability, this would not be possible. It would be necessary to create a new instance of User to have a User named 'Thomas'. The instance in the main function would be the same.
Using final makes a class immutable.
You can check Why do we need immutable class?
Immutable class is good for caching purpose and it is thread safe.
The reason behind using copyWith() is flexible, allowing any number of properties to be updated in a single call.
More about immutable-data-patterns-in-dart-and-flutter
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;
});
}
}
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];
}
}
I have a final and I am trying initializing that in the constructor. It is giving me error & If I don't make it final I get a warning.
This class (or a class which this class inherits from) is marked as '#immutable', but one or more of its instance fields are not final: GenderCard.genderSvg",
My Code:
GenderCard({#required this.genderType}) {
genderSvg = '/assets/' + 'genderType' + '.svg';
}
final String genderType;
final String genderSvg;
#override
Widget build(BuildContext context) {
final instance variables must be initialized in the initializer list. See the language guide.
Instance variables can be final but not const. Final instance
variables must be initialized before the constructor body starts — at
the variable declaration, by a constructor parameter, or in the
constructor’s initializer list.
Change your constructor to:
class GenderCard {
GenderCard({#required this.genderType})
: genderSvg = '/assets/$genderType.svg';
final String genderType;
final String genderSvg;
}