I have a number of classes that all extend StatefulWidget. I want each class to provide its own version of a method bool foo(), so that I can iterate over a collection of objects of these classes, calling foo() on each. What is the best/correct way to do this in Dart/Flutter? Mixins? What would the type of that collection be?
The functionality described can be achieved with Interfaces in Dart
class Widget {
}
abstract class MyCustomWidget extends Widget {
String foo( String argName);
}
class Widget1 implements MyCustomWidget {
String foo( String argName) {
return argName;
}
}
class Widget2 implements MyCustomWidget {
String foo( String argName) {
return '$argName$argName';
}
}
void main() {
Widget1 w = new Widget1();
Widget2 w2 = new Widget2();
var widgets = [w, w2];
for (int i = 0; i < widgets.length; i++) {
print(widgets[i].foo('hello ${i + 1}'));
}
}
That said, flutter recommends composition over inheritance.
See: Flutter StatefulWidget - State class inheritance?
Related
Description:
I have already tested methodA() and methodB() so I can be sure that they are covered.
What are the ways to test methodToBeTested() by mocking methodA() and methodB() that are in the same file? The parameters are passed through the methodToBeTested() to the methodA() and methodB() to properly test these methods using injection.
Note: They are cannot be extracted to a different class since it is a related logic of the calculation service and these methods are already atomically is separated.
Code:
class ClassForTesting {
int methodToBeTested(String a, String b) {
// Calculation in this method also is a bit more difficult
return methodA() + methodB();
}
int methodA(String a) {
int value = 1;
// Here is calculation logic that has been tested
return value;
}
int methodB(String b) {
int value = 2;
// Here is calculation logic that has been tested
return value;
}
}
What has been done:
I have tried several approaches from Mockito, but it doesn't allow to do such a trick:
#GenerateMocks - is creating a mock and requires me to stub each method using when(), even methodToBeTested().
By extending Fake using the next construction:
class Mock extends Fake implements PasswordValidatorService {}
But in this way, I'm only inheriting the PasswordValidatorService's behavior instead of instead implementation and each non-overridden method throws UnimplementedError. Thus, I'm not able to override methodToBeTested() and call its super implementation.
I found that Mockito for Java has #Spy construction that would be perfect in this case but unfortunately it is not available for Dart and Flutter.
The only way I currently came is to create my own Mock:
class MockClassForTesting extends ClassForTesting {
#override
int methodA() {
return 2;
}
#override
int methodB() {
return 5;
}
}
But this implementation doesn't allow me to use Mockito's flexibility of when() construction since I must have different methodA() and methodB() returns.
This fact forces me to have additional variables in my MockClassForTesting to achieve when() construction functionality.
The questions:
What would be the best way to achieve my purposes?
Can be the same mocking approach to be used during the Widget testing?
One approach would be to use a hybrid approach where you create your own derived class but where some of its overrides delegate to a Mock implementation. For example:
class ClassForTesting {
int methodToBeTested(String a, String b) {
// Calculation in this method also is a bit more difficult
return methodA(a) + methodB(b);
}
int methodA(String a) {
int value = 1;
// Here is calculation logic that has been tested
return value;
}
int methodB(String b) {
int value = 2;
// Here is calculation logic that has been tested
return value;
}
}
class PartialMockClassForTesting extends ClassForTesting {
final mock = MockClassForTesting();
#override
int methodA(String a) => mock.methodA(a);
#override
int methodB(String b) => mock.methodB(b);
}
#GenerateMocks([ClassForTesting])
void main() {
test('Test partial mock', () {
var partialMock = PartialMockClassForTesting();
when(partialMock.methodA('hello')).thenReturn(42);
when(partialMock.methodA('goodbye')).thenReturn(-42);
when(partialMock.methodB('world')).thenReturn(10);
expect(partialMock.methodToBeTested('hello', 'world'), 52);
expect(partialMock.methodToBeTested('goodbye', 'world'), -32);
});
}
If you want to conditionally mock certain methods, you could have your overrides check boolean flags to conditionally call either the mock or the real implementation. For example:
class PartialMockClassForTesting extends ClassForTesting {
final mock = MockClassForTesting();
final shouldMock = <Function, bool>{};
#override
int methodA(String a) =>
shouldMock[methodA] ?? false ? mock.methodA(a) : super.methodA(a);
#override
int methodB(String b) =>
shouldMock[methodB] ?? false ? mock.methodB(b) : super.methodB(b);
}
#GenerateMocks([ClassForTesting])
void main() {
test('Test partial mock', () {
var partialMock = PartialMockClassForTesting();
partialMock.shouldMock[partialMock.methodA] = true;
partialMock.shouldMock[partialMock.methodB] = true;
...
Let's say I have several classes which extend an abstract class. Now I want to pass a default value to a function argument, where the type of the argument is the abstract class. Dart expects a const value, and I couldn't create a const constructor for an abstract class. How can I pass a default value of the abstract class?
Sample code is as following:
class Main {
late A objOfA;
Main({ A nObjOfA = const B() }); // <===== Error here
}
abstract class A {
abstract String name;
abstract int id;
}
class B extends A {
#override
String name = "B";
#override
int id = 1;
}
class C extends A {
#override
String name = "C";
#override
int id = 1;
}
Here, how can I pass a default value of nObjOfA in the constructor of Main?
Dart expects a const value, and I couldn't create a const constructor for an abstract class.
There is no rule that you cannot create a const constructor for an abstract class. Abstract classes can have constructors; you just can't call them directly to instantiate an abstract class. The following code is legal:
class Main {
A objOfA;
Main({this.objOfA = const B()});
}
abstract class A {
const A();
abstract final String name;
abstract final int id;
}
class B extends A {
const B();
#override
final String name = "B";
#override
final int id = 1;
}
class C extends A {
#override
String name = "C";
#override
int id = 1;
}
Note that I needed to add final qualifiers since const objects must be immutable. (Unrelated, but I also fixed the inappropriate use of the late keyword.)
In general, there will be cases where you cannot create a const constructor. If you want to use an instance of that type as a default argument in such cases, you can use null as a default value and assign a non-const value later.
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.
Is there way to overriding method in Dart like JAVA, for example:
public class A {
public void handleLoad() {
}
}
And when overriding:
A a = new A() {
#Override
public void handleLoad() {
// do some code
}
};
No, Dart does not have anonymous classes. You have to create a class that extends A and instantiate it.
No but it much less useful in Dart because you can just reassign function:
typedef void PrintMsg(msg);
class Printer {
PrintMsg foo = (m) => print(m);
}
main() {
Printer p = new Printer()
..foo('Hello') // Hello
..foo = ((String msg) => print(msg.toUpperCase()))
..foo('Hello'); //HELLO
}
However you will need some extra boilerplate to access instance.
Use type Function:
class A {
final Function h
A(this.h);
void handleLoad(String loadResult) { h(loadResult); }
}
Or
class A {
final Function handleLoad;
A(this.handleLoad);
}
A a = new A((String loadResult){
//do smth.
});
I have class A:
class A{
String title;
String content;
IconData iconData;
Function onTab;
A({this.title, this.content, this.iconData, this.onTab});
}
How can i create class B that extends class A with additional variable like following:
class B extends A{
bool read;
B({this.read});
}
Tried with this but not working
let o = new B(
title: "New notification",
iconData: Icons.notifications,
content: "Lorem ipsum doro si maet 100",
read: false,
onTab: (context) => {
});
You have to define the constructor on the child class.
class B extends A {
bool read;
B({title, content, iconData, onTab, this.read}) : super(title: title, content: content, iconData: iconData, onTab: onTab);
}
Just to update for 2023, since Dart 2.17 we have Super Initializers - described in detail by Michael Thomsen here
You no longer need to make the explicit call to super.
Example:
class B extends A {
bool read;
B({super.title, super.content, super.iconData, super.onTab, this.read});
}
You can inherit from or extend a class using the extends keyword. This allows you share properties and methods between classes that are similar, but not exactly the same. Also, it allows different subtypes to share a common runtime type so that static analysis doesn't fail. (More on this below);
The classic example is using different types of animals.
class Animal {
Animal(this.name, this.age);
int age;
String name;
void talk() {
print('grrrr');
}
}
class Cat extends Animal {
// use the 'super' keyword to interact with
// the super class of Cat
Cat(String name, int age) : super(name, age);
void talk() {
print('meow');
}
}
class Dog extends Animal {
// use the 'super' keyword to interact with
// the super class of Cat
Dog(String name, int age) : super(name, age);
void talk() {
print('bark');
}
}
void main() {
var cat = Cat("Phoebe",1);
var dog = Dog("Cowboy", 2);
dog.talk();
cat.talk();
}