How to listen for changes to an object's property inside a View Model using Provider in flutter? - flutter

Let's say I have the following view model class
class Foo extends ChangeNotifier{
//Object variable to which changes will be made
Object _fooObject = Object();
// Getter and Setter of Object
Object get fooObject => _fooObject;
set fooObject(Object value) {
_fooObject = value;
notifyListeners();
}
}
And let's say this is my Object Class
class Object{
int? property1;
String? property2;
Object(this.property1,this.property2);
}
Now If I assign a new Object() to fooObject the changes will easily reflect because I have called notifyListeners() in the setter but what If I change a single property in the fooObject let's say I do
Provider.of<Foo>(context).fooObject.property1 = 2;
How do I exactly detect these changes in realtime? do I have to make a separate getter setter for each property? What's the best solution to call notifyListeners() on property1's change?

you can determine using the identical(a , b) function.
set fooObject(Object value) {
if(identical(_fooObject , value)){
_fooObject = value;
notifyListeners();
}
}

Related

Flutter : create new instance of a class which no have default constructor and change some properties

I have a class with name "RecognisedText" in one of my packages which is required in my Flutter application. This class is:
class RecognisedText {
RecognisedText._(this.text, this.blocks);
factory RecognisedText.fromMap(Map<dynamic, dynamic> map) {
var resText = map["text"];
var textBlocks = <TextBlock>[];
for (var block in map["blocks"]) {
var textBlock = TextBlock.fromMap(block);
textBlocks.add(textBlock);
}
return RecognisedText._(resText, textBlocks);
}
///String containing all the text identified in a image.
final String text;
///All the blocks of text present in image.
final List<TextBlock> blocks;
}
I want to define a variable from this class:
RecognisedText myobject = RecognisedText(mytext,myblocks);
but it raise this error:
The class 'RecognisedText' doesn't have a default constructor
I tried to define myobject in different way:
RecognisedText myobject;
myobject.text = mytext;
myobject.blocks = myblocks;
but it raise this error:
'text' can't be used as a setter because it's final.
How i can define my new variable from RecognisedText class and set properties? I can't change any part of class RecognisedText because it is a remote class (added from pubspec.yaml)
Edit:
I finally solved my problem with this code:
RecognisedText myobject = RecognisedText.fromMap({"text": mytext, "blocks": myblocks});
Your Class can't create its instance so try using YourClassName.fromMap constructor.
Class RecognisedText is designed in the way that you can not create its instance other than using its fromMap constructor, which accepts map.
So the only way to create it is to create map with values you need and pass it to RecognisedText.fromMap constructor.
If you already have your block and your text as variables, you can modify you class like this to accept a default constructor :
class RecognisedText {
//----Replace this line ----
//RecognisedText._(this.text, this.blocks);
//----By this line ----
RecognisedText.(this.text, this.blocks);
factory RecognisedText.fromMap(Map<dynamic, dynamic> map) {
var resText = map["text"];
var textBlocks = <TextBlock>[];
for (var block in map["blocks"]) {
var textBlock = TextBlock.fromMap(block);
textBlocks.add(textBlock);
}
return RecognisedText._(resText, textBlocks);
}
///String containing all the text identified in a image.
final String text;
///All the blocks of text present in image.
final List<TextBlock> blocks;
}
To complete Alex's answer, the constructor fromMap takes a map as a parameter. It should look like :
RecogniserdText.fromMap({'text': myText, 'blocks': myblock1, 'blocks': myblock2, 'blocks': myblock3});

How to make model fields observable in Flutter

I have a model called category , which has two fields, User has a list of categories and what to update the category name, for State Management getx is used
class Category {
String? categoryName;
bool status;
Category(this.categoryName,this.status);
}
I have a observable list called catList which is used in List widget
var catList = <Category>[].obs;
when I update the category field it doest not update
catList[index].categoryName = categoryChangedName.value;
but If I update the item in object and then assign the object to catList then It changed
catList[index] = Category(categoryChangedName.value, catList[index].status );
My question is how to make model fields observable, if we have more fields changes then this is not proper way.
As of GetX documentation you need to update values using method and call update(); method inside custom object !
Ex:
class Controller extends GetxController {
int counter = 0;
void increment() {
counter++;
update(); // look here!
}
}
Your use case might be like....
class Category {
String? categoryName;
bool status;
Category(this.categoryName,this.status);
void updateCategoryName(name){
this.categoryName = name;
update();
}
}
//Use like..
catList[index].updateCaetgoryName = categoryChangedName.value;

Copying objects bug in Flutter BLoC

I am building an app with Flutter using BLOC Architecture with the flutter_bloc package.
I have a data class for an object, which looks like this example:
class MyClass {
int? id;
List<MyOtherClass> myOtherClasses = [];
MyClass();
MyClass._(this.id, this.myOtherClasses);
MyClass copyWith({int? id, List<MyOtherClass>? myOtherClasses}) {
return MyClass._(
id ?? this.id,
myOtherClasses ?? this.myOtherClasses,
);
}
}
class MyOtherClass {
int value;
MyOtherClass(this.value);
}
Now I a Screen that shows the values of the class, and a Dialog to edit it. To store the state, I am using a Cubit, that looks like this:
#immutable
abstract class MyClassState {
final MyClass myClass;
MyClassState(this.myClass);
}
class MyClassInitial extends MyClassState {
MyClassInitial() : super(MyClass());
}
class MyClassEditing extends MyClassState {
final MyClass editingMyClass;
MyClassEditing(MyClass myClass, this.editingMyClass) : super(myClass);
}
class MyClassChanged extends MyClassState {
MyClassChanged(MyClass myClass) : super(myClass);
}
class MyClassCubit extends Cubit<MyClassState> {
MyClassCubit() : super(MyClassInitial());
void editMyClass({int? id, List<MyOtherClass>? myOtherClasses}) {
emit(MyClassEditing(state.myClass,
state.myClass.copyWith(id: id, myOtherClasses: myOtherClasses)));
}
void saveChanges() {
if (state is MyClassEditing)
emit(MyClassChanged((state as MyClassEditing).editingMyClass));
}
void discardChanged() {
emit(MyClassChanged(state.myClass));
}
}
So, basically, what I am trying to achieve here is to story a backup of MyClass in the MyClassEditing state in order to be able to discard the changes I made to MyClass. When I call the constructor of MyClassEditing here in this line: emit(MyClassEditing(state.myClass, state.myClass.copyWith(id: id, myOtherClasses: myOtherClasses)));, the state should contain the initial instance of MyClass without any changes, and the copy of the initial MyClass instance with the changes applied. But somehow, both instances have the changes applied, and I just don't get why this happens. Am I doing something wrong copying the instance?
Probably, the issue might be lists, if you make shallow copy of them.

Declare a variable to store an object that is only constructed with a specific named constructor in dart

class ExampleClass {
//default constructor
ExampleClass() {
//do stuff
}
//named constructor
ExampleClass.namedConstructor() {
//do stuff
}
}
void main() {
//is there a way to create a variable with datatype to store an object that is constructed only with a specific constructor?
//I have tried something like this, but it returns an error
ExampleClass.namedConstructor variable_1;
}
Is there any way to do this or an alternative? because I need to be able to differentiate between an object that is constructed with the default constructor or with a named constructor.
You can add some identification to classes builded with different constructors and compare entities by unique parameters.
If instances of your classes creating once (Singleton design pattern), you can create entities as constants and compare it by reference:
const administrator = User.administrator();
class User {
final int id;
User(this.id);
factory User.administrator() {
return User(0);
}
factory User.administrator(int id) {
return User(id);
}
}

How to pass a parameter to a Mobx Controller (Flutter)

I am an Android Developer and new to Flutter. I really like the way Mobx works, because it remembers me Android's ViewModel. By the way, when I create a ViewModel, I like to create it passing the repository as a parameter, so I can test it with different Data Sources (ie. local or cloud).
So, this is my class right now.
import 'package:mobx/mobx.dart';
part 'create_bill_controller.g.dart';
class CreateBillController = _CreateBillControllerBase
with _$CreateBillController;
abstract class _CreateBillControllerBase with Store {
final appBarTitle = 'Criar Conta';
final criarConta = 'Criar conta';
final nomeDaConta = 'Nome da conta';
final seuNome = 'Seu nome';
#action
createBill(String billname, String userName) {
// here, dataSource should be given in a constructor
datasource.createBill(billName, userName);
}
}
How can I pass a DataSource (repository) as a parameter to this class?
What you need is to declare constructor for CreateBillController instead of _CreateBillControllerBase, because constructor is not inherited by child class in Dart. The simplest way is to assign the passed in datasource to the corresponding property in parent class in the constructor, as you can see in the below snippet. You can also implement a constructor for _CreateBillControllerBase as well and call super(datasource) in CreateBillController's constructor.
import 'package:mobx/mobx.dart';
part 'create_bill_controller.g.dart';
class CreateBillController extends _CreateBillControllerBase with _$CreateBillController {
// HERE! Implement constructor for CreateBillController
// Do this if you have a constructor for _CreateBillControllerBase
//
// CreateBillController(DataSource datasource) : super(datasource)
//
CreateBillController(DataSource datasource) {
super.datasource = datasource;
}
}
abstract class _CreateBillControllerBase with Store {
final appBarTitle = 'Criar Conta';
final criarConta = 'Criar conta';
final nomeDaConta = 'Nome da conta';
final seuNome = 'Seu nome';
// HERE! Declare datasource
DataSource datasource;
#action
createBill(String billname, String userName) {
datasource.createBill(billName, userName);
}
}