What's the equivalent to this[x] in Dart? - dart

For instance, in Javascript I can do something like:
class Foo {
x = 'baz';
bar() {
const someVar = 'x';
console.log(this[someVar]);
// Output: 'baz';
}
}
Hopefully that's relatively clear - it boils down to accessing a member variable by another variable's contents. How is this achieved in Dart?

This is not trivial in Dart. Dart doesn't have a syntax to access class properties with [].
There are a couple of approaches though:
Mirrors:
https://api.dartlang.org/stable/2.6.1/dart-mirrors/dart-mirrors-library.html
Basically you have access to everything and offers the biggest freedom. You can check what properties a class has, access them via names and so on. Big disadvantage is that the generated JS (if targeting web) will be huge. Flutter doesn't support it at all.
Reflectable
To deal with the large generated JS, you can use package:reflectable. Never tried it with Flutter. It's a bit more to set up and start using bit it works.
Dart only solution 1
You can overload [] operator on a class:
class Foo {
final _backing = <String, String>{
'foo': 'bar'
};
operator [](String val) {
return _backing[val];
}
}
void main() {
final inst = Foo();
print(inst['foo']);
}
Dart only solution 2
Just use a map :) Well sort of... If you are dealing with complex types and you want to add some extra functionality to your map, you can do something like this:
import 'dart:collection';
class StringMap extends Object with MapMixin<String, String> {
final _backing = <String, String>{};
#override
String operator [](Object key) {
return _backing[key];
}
#override
void operator []=(String key, String value) {
_backing[key] = value;
}
#override
void clear() {
_backing.clear();
}
#override
Iterable<String> get keys => _backing.keys;
#override
String remove(Object key) {
return _backing.remove(key);
}
}

Related

NoSuchMethodEror: tried to call a non-function, such as null:

I have this code which works well on android emulator but gives error on web.
import 'package:parivaar/components/screens/home/Home.dart';
typedef T Constructor<T>();
final Map<String, Constructor<Object>> _constructors =
<String, Constructor<Object>>{};
void register<T>(Constructor<T> constructor) {
_constructors[T.toString()] = constructor;
}
class ClassBuilder {
static void registerClasses() {
register<Home>(() => Home());
}
static dynamic fromString(String type) {
return _constructors[type]();
}
}
And i am calling that function as follows:
class _MyHomePageState extends State {
KFDrawerController _drawerController;
#override
void initState() {
super.initState();
_drawerController = KFDrawerController(
initialPage: ClassBuilder.fromString('Home'),
.
..
...
....
You are probably assuming that T.toString() returns the source name of the type as a string. Nobody ever promised that.
It works on native, but on the web you often optimize for size and "minification" turns all the long names into shorter names. With that, the name of the type Home is no longer the string "Home".
I generally do not recommend depending on the string representation of types (or Type objects for that matter).
Consider changing register and fromString to:
void register<T>(Constructor<T> constructor) {
_constructors[T] = constructor;
}
and
static T fromType<T>() => _constructors[T]();
That relies on Type object equality, which is a well-defined operation.
Not perfect, but still better than going through strings.
If you need to create the objects dynamically from strings, where you don't know the type, then I'd instead require you to provide the key string on registration, changing register to:
void register<T>(String key, Constructor<T> constructor) {
_constructors[key] = constructor;
}
and register types like:
static void registerClasses() {
register<Home>("Home", () => Home());
}

Avoid repetition in BLoCs and RxDart

hopefully I can make myself clear.
After video and tutorials, I found this way to have some widgets to input data to the bloc (valueSetting) and some others to get this data (value).
What I am asking is if there is a better way (there has to be..). I want to avoid the need to have 4 variables for just 1 real value shared between widgets.
import 'dart:async';
import 'package:rxdart/subjects.dart';
class BlocExample {
final _valueSettingController = StreamController<bool>();
// object use by widget to push data
Sink<bool> get valueSetting => _valueSettingController.sink;
final _value = BehaviorSubject<bool>(seedValue: false);
// object used by widget to get data
Stream<bool> get value => _value.stream;
BlocExample() {
_valueSettingController.stream.listen(_value.add);
}
void dispose() {
_value.close();
_valueSettingController.close();
}
}
First of, let me say that you can remove the private variables by using a custom factory constructor. Here's an example:
class MyBloc {
final Sink<bool> input;
final Stream<bool> output;
final VoidCallback _dispose;
MyBloc._({this.input, this.output, VoidCallback dispose}) : _dispose = dispose;
factory MyBloc() {
final mainController = BehaviorSubject(seedValue: false);
return MyBloc._(
input: mainController.sink,
output: mainController.stream,
dispose: () {
mainController.close();
},
);
}
void dispose() {
_dispose();
}
}
Secondly, the problem you're trying to solve is actually not a problem. While it seems at first that there's a lot of duplicates; in reality they serve different purposes.
In many situations, your Stream will be more than just _controller.stream. For example, for whatever reason you may want to transform the value before exposing it:
final mainController = BehaviorSubject(seedValue: false);
final Stream<bool> output = mainController.map((foo) => !foo);
This code makes that the output stream reverses the value of everything passed to mainController.sink
But in my situation this is not the case. So why 3 variables that point to the same thing?
The fact that in your situation, your controller is both the sink and stream without transformation is an implementation detail and may be subject to changes.
By exposing Sink/Stream as done before, you actually abstract this implementation detail. So that in the future if your stream needs custom operations; no change will be required by your UI.
This is not necessary. But recommended.
You can do something like this :)
enum STREAM_GROUP {
TYPE1,TYPE2,TYPE3
}
class BlocExample {
Map<STREAM_GROUP, StreamController<bool>> groups = new Map();
Stream<bool> getValue(STREAM_GROUP type){
return groups[type].stream;
}
Sink<bool> getValueSetting(STREAM_GROUP type){
return groups[type].sink;
}
BlocExample() {
groups[STREAM_GROUP.TYPE1] = StreamController<bool>();
groups[STREAM_GROUP.TYPE2] = StreamController<bool>();
groups[STREAM_GROUP.TYPE3] = StreamController<bool>();
groups.forEach((groupType, streamController){
final currentValue = BehaviorSubject<bool>(seedValue: false);
streamController.stream.listen(currentValue.add);
});
}
void dispose() {
groups.forEach((groupType, streamController){
streamController.close();
});
}
}

How do I add Methods or Values to Enums in Dart?

In Java when you are defining an enum, you can do something similar to the following, i.e. add members to an enum. Is this possible in Dart?
enum Foo {
one(1), two(2);
final num value;
Foo(this.value);
}
Starting with Dart 2.6 you can define extensions on classes (Enums included).
enum Cat {
black,
white
}
extension CatExtension on Cat {
String get name {
switch (this) {
case Cat.black:
return 'Mr Black Cat';
case Cat.white:
return 'Ms White Cat';
default:
return null;
}
}
void talk() {
print('meow');
}
}
Example:
Cat cat = Cat.black;
String catName = cat.name;
cat.talk();
Here's one more live example (uses a constant map instead of a switch):
https://dartpad.dartlang.org/c4001d907d6a420cafb2bc2c2507f72c
Dart Enhanced Enum Classes
Starting with Dart 2.17, the Enhanced Enum Classes feature has been introduced. With that, the example from the question would look like this:
enum Foo {
one(1),
two(2);
const Foo(this.value);
final num value;
}
Now, you can just use the enum class like this:
void main() {
const foo = Foo.one;
print(foo.value); // 1
}
Note that you need to update your SDK constraint as the feature requires Dart 2.17:
environment:
sdk: '>=2.17.0-0 <3.0.0'
Adding members
With enhanced enums, you can add any member to your enum as long as the constructor is const.
This also means that you can add getters or methods to existing enums, for example:
enum Cake {
cherry,
apple,
strawberry;
String get description => '$name cake';
}
Generics
Enhanced enum classes also enable you to use generics for you enums. If you combine this with members, you can do the following:
enum Bar<T extends Object> {
number<int>(42),
name<String>('creativecreatorormaybenot'),
baz(true); // Note that type inference also works.
const Bar(this.value);
final T value;
}
Mixins and interfaces
In addition to declaring members, you can also mixin mixins and implement interfaces with enhanced enums and override any missing implementations.
mixin Foo {
int get n;
}
abstract class Bar {
void printNumber();
}
enum Baz with Foo implements Bar {
one(1),
two(2);
const Baz(this.n);
#override
final int n;
#override
void printNumber() => print(n);
}
Multiple arguments
Finally note that even if I did not make use of it in any of the examples above, it is possible to have an arbitrary number of arguments (and an initializer list):
enum Foo {
bar(42, description: 'The answer to life, the universe, and everything.'),
baz(0, enabled: false, description: 'noop');
const Foo(
int number, {
this.enabled = true,
required this.description,
}) : n = number;
final int n;
final bool enabled;
final String description;
}
Dart enums are used only for the simplest cases. If you need more powerful or more flexible enums, use classes with static const fields like shown in https://stackoverflow.com/a/15854550/217408
This way you can add whatever you need.
Nope. In Dart, enums can only contain the enumerated items:
enum Color {
red,
green,
blue
}
However, each item in the enum automatically has an index number associated with it:
print(Color.red.index); // 0
print(Color.green.index); // 1
You can get the values by their index numbers:
print(Color.values[0] == Color.red); // True
See: https://www.dartlang.org/guides/language/language-tour#enums
It may not be "Effective Dart" , I add a static method inside a Helper class ( there is no companion object in Dart) .
In your color.dart file
enum Color {
red,
green,
blue
}
class ColorHelper{
static String getValue(Color color){
switch(color){
case Color.red:
return "Red";
case Color.green:
return "Green";
case Color.blue:
return "Blue";
default:
return "";
}
}
}
Since the method is in the same file as the enum, one import is enough
import 'package:.../color.dart';
...
String colorValue = ColorHelper.getValue(Color.red);
extension is good, but it cannot add static methods. If you want to do something like MyType.parse(string), consider using a class with static const fields instead (as Günter Zöchbauer suggested before).
Here is an example
class PaymentMethod {
final String string;
const PaymentMethod._(this.string);
static const online = PaymentMethod._('online');
static const transfer = PaymentMethod._('transfer');
static const cash = PaymentMethod._('cash');
static const values = [online, transfer, cash];
static PaymentMethod parse(String value) {
switch (value) {
case 'online':
return PaymentMethod.online;
break;
case 'transfer':
return PaymentMethod.transfer;
break;
case 'cash':
return PaymentMethod.cash;
default:
print('got error, invalid payment type $value');
return null;
}
}
#override
String toString() {
return 'PaymentMethod.$string';
}
}
I found this much handier than using a helper function.
final method = PaymentMethod.parse('online');
assert(method == PaymentMethod.online);
I did this (inspired form the accepted answer by #vovahost)
enum CodeVerifyFlow {
SignUp, Recovery, Settings
}
extension CatExtension on CodeVerifyFlow {
String get name {
return ["sign_up", "recovery", "settings"][index];
}
}
// use it like
CodeVerifyFlow.SignUp.name
thank me later!
There's an upcoming feature in Dart known as enhanced enums, and it allows for enum declarations with many of the features known from classes. For example:
enum Blah {
one(1), two(2);
final num value;
const Blah(this.value);
}
The feature is not yet released (and note that several things are not yet working), but experiments with it can be performed with a suitably fresh version of the tools by passing --enable-experiment=enhanced-enums.
The outcome is that Blah is an enum declaration with two values Blah.one and Blah.two, and we have Blah.one.value == 1 and Blah.two.value == 2. The current bleeding edge handles this example in the common front end (so dart and dart2js will handle it), but it is not yet handled by the analyzer.
As an improvement on the other suggestions of using Extensions, you can define your assigned values in a list or map, and the extension will be concise.
enum Numbers {
one,
two,
three,
}
// Numbers.one.value == 1
// Numbers.two.value == 2
// Numbers.three.value == 3
example with list
extension NumbersExtensionList on Numbers {
static const values = [1, 2, 3];
int get value => values[this.index];
}
example with map
extension NumbersExtensionMap on Numbers {
static const valueMap = const {
Numbers.one: 1,
Numbers.two: 2,
Numbers.three: 3,
};
int get value => valueMap[this];
}
Note: This approach has the limitation that you can not define a static factory method on the Enum, e.g. Numbers.create(1) (as of Dart 2.9). You can define this method on the NumbersExtension, but it would need to be called like NumbersExtension.create(1)
For String returns :
enum Routes{
SPLASH_SCREEN,
HOME,
// TODO Add according to your context
}
String namedRoute(Routes route){
final runtimeType = '${route.runtimeTypes.toString()}.';
final output = route.toString();
return output.replaceAll(runtimeType, "");
}
You can add extra fields and methods with my package enum_extendable.
It generates extensions on enum, so you can use your enum values in the similar way to instances of a regular Dart class.
For example, if you have enum MathOperator { plus, minus } the symbol and calculate(...) can be added to it.
So, the enum can be used in such way:
final n1 = 1;
final n2 = 2.0;
MathOperator.values.forEach((operator) {
print('$n1 ${operator.symbol} $n2 = ${operator.calculate(n1, n2)}');
});
Usage:
Add dependencies to pubspec.yaml:
dependencies:
enum_extendable_annotation:
dev_dependencies:
build_runner:
enum_extendable_generator:
Install these dependencies:
# Dart
pub get
# Flutter
flutter packages get
Add imports to your enum file:
import 'package:enum_extendable_annotation/enum_extendable_annotation.dart';
part '<your enum file name>.enum_extendable.g.dart';
Create a PODO class with fields and methods you wanted.
Create a map with instances of this PODO class for each enum value.
Annotate elements:
the enum with #ExtendableEnum();
the PODO class - #ExtendableEnumPodo();
the map of PODO instances - #ExtendableEnumValues().
Run code generator:
if your package depends on Flutter:
flutter pub run build_runner build
if your package does not depend on Flutter:
dart pub run build_runner build
The file with extensions should be generated.
Example of the enum file:
import 'package:enum_extendable_annotation/enum_extendable_annotation.dart';
part 'math_operator.enum_extendable.g.dart';
#ExtendableEnum()
enum MathOperator { plus, minus }
#ExtendableEnumPodo()
class _MathOperatorPodo {
final String symbol;
final num Function(num, num) calculate;
_MathOperatorPodo(
this.symbol,
this.calculate,
);
#ExtendableEnumValues()
static final Map<MathOperator, _MathOperatorPodo> _values = {
MathOperator.plus: _MathOperatorPodo(
'+',
(n1, n2) => n1 + n2,
),
MathOperator.minus: _MathOperatorPodo(
'-',
(n1, n2) => n1 - n2,
),
};
}

Creating an instance of a generic type in DART

I was wondering if is possible to create an instance of a generic type in Dart. In other languages like Java you could work around this using reflection, but I'm not sure if this is possible in Dart.
I have this class:
class GenericController <T extends RequestHandler> {
void processRequest() {
T t = new T(); // ERROR
}
}
I tried mezonis approach with the Activator and it works. But it is an expensive approach as it uses mirrors, which requires you to use "mirrorsUsed" if you don't want to have a 2-4MB js file.
This morning I had the idea to use a generic typedef as generator and thus get rid of reflection:
You define a method type like this: (Add params if necessary)
typedef S ItemCreator<S>();
or even better:
typedef ItemCreator<S> = S Function();
Then in the class that needs to create the new instances:
class PagedListData<T>{
...
ItemCreator<T> creator;
PagedListData(ItemCreator<T> this.creator) {
}
void performMagic() {
T item = creator();
...
}
}
Then you can instantiate the PagedList like this:
PagedListData<UserListItem> users
= new PagedListData<UserListItem>(()=> new UserListItem());
You don't lose the advantage of using generic because at declaration time you need to provide the target class anyway, so defining the creator method doesn't hurt.
You can use similar code:
import "dart:mirrors";
void main() {
var controller = new GenericController<Foo>();
controller.processRequest();
}
class GenericController<T extends RequestHandler> {
void processRequest() {
//T t = new T();
T t = Activator.createInstance(T);
t.tellAboutHimself();
}
}
class Foo extends RequestHandler {
void tellAboutHimself() {
print("Hello, I am 'Foo'");
}
}
abstract class RequestHandler {
void tellAboutHimself();
}
class Activator {
static createInstance(Type type, [Symbol constructor, List
arguments, Map<Symbol, dynamic> namedArguments]) {
if (type == null) {
throw new ArgumentError("type: $type");
}
if (constructor == null) {
constructor = const Symbol("");
}
if (arguments == null) {
arguments = const [];
}
var typeMirror = reflectType(type);
if (typeMirror is ClassMirror) {
return typeMirror.newInstance(constructor, arguments,
namedArguments).reflectee;
} else {
throw new ArgumentError("Cannot create the instance of the type '$type'.");
}
}
}
I don't know if this is still useful to anyone. But I have found an easy workaround. In the function you want to initialize the type T, pass an extra argument of type T Function(). This function should return an instance of T. Now whenever you want to create object of T, call the function.
class foo<T> {
void foo(T Function() creator) {
final t = creator();
// use t
}
}
P.S. inspired by Patrick's answer
2022 answer
Just came across this problem and found out that although instantiating using T() is still not possible, you can get the constructor of an object easier with SomeClass.new in dart>=2.15.
So what you could do is:
class MyClass<T> {
final T Function() creator;
MyClass(this.creator);
T getGenericInstance() {
return creator();
}
}
and when using it:
final myClass = MyClass<SomeOtherClass>(SomeOtherClass.new)
Nothing different but looks cleaner imo.
Here's my work around for this sad limitation
class RequestHandler {
static final _constructors = {
RequestHandler: () => RequestHandler(),
RequestHandler2: () => RequestHandler2(),
};
static RequestHandler create(Type type) {
return _constructors[type]();
}
}
class RequestHandler2 extends RequestHandler {}
class GenericController<T extends RequestHandler> {
void processRequest() {
//T t = new T(); // ERROR
T t = RequestHandler.create(T);
}
}
test() {
final controller = GenericController<RequestHandler2>();
controller.processRequest();
}
Sorry but as far as I know, a type parameter cannot be used to name a constructor in an instance creation expression in Dart.
Working with FLutter
typedef S ItemCreator<S>();
mixin SharedExtension<T> {
T getSPData(ItemCreator<T> creator) async {
return creator();
}
}
Abc a = sharedObj.getSPData(()=> Abc());
P.S. inspired by Patrick
simple like that.
import 'dart:mirrors';
void main(List<String> args) {
final a = A<B>();
final b1 = a.getInstance();
final b2 = a.getInstance();
print('${b1.value}|${b1.text}|${b1.hashCode}');
print('${b2.value}|${b2.text}|${b2.hashCode}');
}
class A<T extends B> {
static int count = 0;
T getInstance() {
return reflectClass(T).newInstance(
Symbol(''),
['Text ${++count}'],
{Symbol('value'): count},
).reflectee;
}
}
class B {
final int value;
final String text;
B(this.text, {required this.value});
}
Inspired by Patrick's answer, this is the factory I ended up with.
class ServiceFactory<T> {
static final Map<Type, dynamic> _cache = <String, dynamic>{};
static T getInstance<T>(T Function() creator) {
String typeName = T.toString();
return _cache.putIfAbsent(typeName, () => creator());
}
}
Then I would use it like this.
final authClient = ServiceFactory.getInstance<AuthenticationClient>(() => AuthenticationClient());
Warning: Erik made a very good point in the comment below that the same type name can exist in multiple packages and that will cause issues. As much as I dislike to force the user to pass in a string key (that way it's the consumer's responsibility to ensuring the uniqueness of the type name), that might be the only way.

How do I extend a List in Dart?

I want to create a more specialized list in dart. I can't directly extend List. What are my options?
To make a class implement List there are several ways :
Extending ListBase and implementing length, operator[], operator[]= and length= :
import 'dart:collection';
class MyCustomList<E> extends ListBase<E> {
final List<E> l = [];
MyCustomList();
void set length(int newLength) { l.length = newLength; }
int get length => l.length;
E operator [](int index) => l[index];
void operator []=(int index, E value) { l[index] = value; }
// your custom methods
}
Mixin ListMixin and implementing length, operator[], operator[]= and length= :
import 'dart:collection';
class MyCustomList<E> extends Base with ListMixin<E> {
final List<E> l = [];
MyCustomList();
void set length(int newLength) { l.length = newLength; }
int get length => l.length;
E operator [](int index) => l[index];
void operator []=(int index, E value) { l[index] = value; }
// your custom methods
}
Delegating to an other List with DelegatingList from the quiver package:
import 'package:quiver/collection.dart';
class MyCustomList<E> extends DelegatingList<E> {
final List<E> _l = [];
List<E> get delegate => _l;
// your custom methods
}
Delegating to an other List with DelegatingList from the collection package:
import 'package:collection/wrappers.dart';
class MyCustomList<E> extends DelegatingList<E> {
final List<E> _l;
MyCustomList() : this._(<E>[]);
MyCustomList._(l) :
_l = l,
super(l);
// your custom methods
}
Depending on your code each of those options has their advantages. If you wrap/delegate an existing list you should use the last option. Otherwise, use one of the two first options depending on your type hierarchy (mixin allowing to extend another Object).
There is a ListBase class in dart:collection. If you extend this class, you only need to implement:
get length
set length
[]=
[]
Here is an example:
import 'dart:collection';
class FancyList<E> extends ListBase<E> {
List innerList = new List();
int get length => innerList.length;
void set length(int length) {
innerList.length = length;
}
void operator[]=(int index, E value) {
innerList[index] = value;
}
E operator [](int index) => innerList[index];
// Though not strictly necessary, for performance reasons
// you should implement add and addAll.
void add(E value) => innerList.add(value);
void addAll(Iterable<E> all) => innerList.addAll(all);
}
void main() {
var list = new FancyList();
list.addAll([1,2,3]);
print(list.length);
}
A new way of extending classes was introduced with Dart 2.6.
You can now create an extension of List like this:
extension MyCustomList<T> on List<T> {
// Any methods you want can be added here.
}
The methods you add can be used implicitly, i.e. you can just use them on any List when you have your extension imported.
Here is an example from the feature specification:
extension MyFancyList<T> on List<T> {
int get doubleLength => this.length * 2;
List<T> operator-() => this.reversed.toList();
List<List<T>> split(int at) =>
<List<T>>[this.sublist(0, at), this.sublist(at)];
List<T> mapToList<R>(R Function(T) convert) => this.map(convert).toList();
}
You can use these new members on any List, e.g. like this:
const list = <String>['some', 'elements'];
list.doubleLength; // Evaluates to 4.
The answers to this are pretty outdated, and I'm in the process of doing this for my own project, so I thought I'd help some people out by posting a really clean answer that doesn't involve any overriding or implementing of things.
The quiver package has an extendable List class called DelegatingList that makes extending a list trivial.
class FruitList extends DelegatingList<Fruit> {
final List<Fruit> _fruits = [];
List<Fruit> get delegate => _fruits;
// custom methods
}
Hopefully this helps someone who comes across this question like I did!
Following on from the answer above, you can create an immutable list like this:
class ImmutableList<E> extends ListBase<E> {
late final List<E> innerList;
ImmutableList(Iterable<E> items) {
innerList = List<E>.unmodifiable(items);
}
#override
int get length => innerList.length;
#override
set length(int length) {
innerList.length = length;
}
#override
void operator []=(int index, E value) {
innerList[index] = value;
}
#override
E operator [](int index) => innerList[index];
}
//list is your given List and iterable is any object in dart that can be iterated
list.addAll(Iterable)

Resources