Dart assert not working when use List.length - flutter

I want to check List length when initialize class.
I tried but it did not work as expected.
My code is here.
#immutable
class TestClass {
const TestClass(this.values)
: assert(values.length >= 0 && values.length <= 4);
final List<int> values;
}
const a = TestClass(<int>[1, 1, 1]); // assert
How can I get it to behave as expected?
Edit 1
Now the assertion no longer works.
Edit 2
Edit 3
// OK
final ok = TestClass(const <int>[1]);
// NG
final ng = TestClass(const <int>[1, 1, 1, 1, 1]);
Edit 4
#immutable
class TestClass2 {
const TestClass2(
this.value,
) : assert(value.length == 3);
final String value;
}
final a = TestClass2('abcde');
I think T.length doesn't work in assert.
Edit 5
Edit 6

I don't have enough reputation to add a comment, so I'll add it as an answer.
Apparently, this is working as expected. Found this issue on the Dart SDK repository which is similar. In a comment someone referenced the following from the language specification:
You cannot get the length of a list in a constant expression. The language specification says so here:
\item An expression of the form \code{$e$.length} is potentially constant
if $e$ is a potentially constant expression.
It is further constant if $e$ is a constant expression that
evaluates to an instance of \code{String}.
So it must be rejected at compile-time. This is one of many situations where it's tempting to make Dart constant expressions just a little bit more expressive, but it is quite costly (in so many ways) so it's not very likely to happen. One issue is that you can write a class MyList extends List<String> {...} and make the length getter run arbitrary code, and still have a const constructor in MyList, and constant expressions will not run arbitrary code, so you'd need to find the right borderline between the lists where .length is OK and the lists where it isn't, and the type system wouldn't be able to prevent the latter from occurring where a List or List<T> for some T is specified.
Not sure if there's a way around this, but I hope this helps a bit.

#immutable
class TestClass {
const TestClass(this.values) : assert(values.length == 0 || values.length > 4);
final List<int> values;
}
I edited my answer you will get a warning if values ​​variable has no elements or has more than 4 elements

Related

What does " : " after Board.fromDatabase(DataSnapshot snapshot) mean. I want to undestand how " : " works here and what is it. If possible by exmaple [duplicate]

This code is from flutter gallery and i'm trying to understanding and adapting it. I would know what this syntax means:
class DemoItem<T> {
DemoItem({
this.valueName,
this.hintName,
this.valueSurname,
this.hintSurname,
this.builder,
this.valueToString
}) : textController = new TextEditingController(text: valueToString(valueName));
Especially i would know what means the colon after the constructor and if there is a way to define another TextEditingController, in addition to the one already defined.
The part after : is called "initializer list". It is a ,-separated list of expressions that can access constructor parameters and can assign to instance fields, even final instance fields. This is handy to initialize final fields with calculated values.
The initializer list is also used to call other constructors like : ..., super('foo').
Since Dart version 2.0 the initializer list also supports assert(...) which is handy to check parameter values.
The initializer list can't read from this because the super constructors need to be completed before access to this is valid, but it can assign to this.xxx.
Pointing out as mentioned in the comments by user693336:
This also means the initializer list is executed before the constructor body. Also the initializer lists of all superclasses are executed before any of the constructor bodies are executed.
Example (copied from https://github.com/dart-lang/language/issues/1394):
class C {
final int x;
final int y;
C(this.x) : y = x + 1;
}
To elaborate on other answers and to complete the syntax, it is also possible to have a real body for the constructor along with initializer code
NonNegativePoint(this.x, this.y) : assert(x >= 0), assert(y >= 0) {
print('I just made a NonNegativePoint: ($x, $y)');
}
^ Here the assertions happen before the execution of the body
Another use case is to assign values to final fields before body executes
final num x;
final num y;
Point.fromJson(Map<String, num> json) : x = json['x'], y = json['y'] {
print('In Point.fromJson(): ($x, $y)');
}

How to make something variable something constant?

I have a variable list in flutter that won't change anymore once called. The question is: can I make this list constant?
Here is the code:
final number = new List<int>.generate(300, (i) => i + 1);
var rng = new Random();
final place = new List.generate(300, (_) => rng.nextInt(3));
final noteListStart = new List<Note>.generate(number.length, (i) => Note(number[i],place[i]));
final List<Note> noteListEnd = [
Note(300, -1), Note(301, -1),Note(302, -1),Note(303, -1)
];
final initList = List<Note>.from(noteListStart)..addAll(noteListEnd);
List<Note> initNotes1() {
return
initList;
}
In the example above, initNotes1() needs to be constant after being called so I can use it easely somewhere else in the code.
Any help would be appreciated.
At this point, it is unclear what your actual question is.
Taken at face value, you are asking how you can compute a list at runtime and then, once the list is populated, convert it into a constant. Well the answer is this: you can't.
Constants are explicit values that are defined before the program has even compiled. By definition, you cannot create a constant from a computed or generated value (unless it is evaluated from simple expressions involving other values are themselves constant). This means you can't create a constant list full of random values - it is antithetical to the whole concent of what a "constant" is.
(Note: This explanation is a bit specific to Dart, but it is also common among compiled languages. This is different to the definition of a "constant" for an interpreted language such as Javascript, which uses the const keyword to merely refer to an immutable variable.)
If you didn't mean "constant" and merely meant "immutable", then you would mark your list as final which would achieve the same thing. As an extra added measure, you can create the list using List.unmodifiable to make it so its elements couldn't be changed either.
final rng = Random();
final _noteListStart = List.generate(startLength, (i) => Note(i + 1, rng.nextInt(3)));
final _noteListEnd = [
Note(300, -1), Note(301, -1),Note(302, -1),Note(303, -1)
];
List<Note> noteList = List.unmodifiable([..._noteListStart, ..._noteListEnd]);
However, what it appears you are asking is not how to make a variable constant, but instead how to make a variable global. This is a question that is both easier and harder to answer.
It's easier because doing so is quite simple. Take the above code, rearrange it a bit, and put it into its own dart file which you can then import and use wherever you wanted:
// notes_list.dart
final _noteListEnd = [
Note(300, -1), Note(301, -1),Note(302, -1),Note(303, -1)
];
List<Note> _initList(int startLength) {
final rng = Random();
final _noteListStart = List.generate(startLength, (i) => Note(i + 1, rng.nextInt(3)));
return List.unmodifiable([..._noteListStart, ..._noteListEnd]);
}
final List<Note> noteList = _initList(300);
// other_file.dart
import '/path/to/notes_list.dart';
void main() {
print(noteList);
}
(Note: The import is mandatory - you cannot make anything _truly_ global in Dart and eliminate the need to import it.)
On the flip side, this question is harder to answer because the practice of making global variables is frowned upon by many programmers. It belongs to a class of practices that leads to tightly coupled and difficult-to-test code which in turn results in programs that are near impossible to maintain and evolve. In many cases, global variables can be replaced entirely by another practice, such as dependency injection.

why does my indexOf output -1 in flutter?

class Task{
String name;
bool isDone;
Task({this.name, this.isDone = false});
}
class TaskData{
List<Task> tasks = [
Task(name: 'Buy milk'),
Task(name: 'Buy eggs'),
Task(name: 'Buy bread')
];
}
void main(){
print(TaskData().tasks.indexOf(Task(name: 'Buy milk')));
}
why does it print -1 ?
Flutter(Dart) can not compare objects directly as we compare variables like int, strings and other.
We have to do it manually by checking each and every variables are same or not then we can say that both object are same or not.
However, we can compare class object as other variable by using Equatable package.
Add Equatable package in pubspec.yaml file and then change your class as below. now you will get desire outputs.
class Task extends Equatable {
String name;
bool isDone;
Task({this.name, this.isDone = false});
#override
List<Object> get props => [name, isDone];
}
Your program prints -1 becuase the Task argument you sent to indexOf() does not exist in tasks. The following explanation is dart-related more than it is flutter-related.
By default, instance of an object in Dart is considered equal to another instance only if those are the same instance. Here, you created two different instances, perhaps with the same properties but they are actually independent of each other. In this case both tasks are logically equal but there is no way Dart could know that. Your options are:
Use a const constructor
Const constructors in Dart let you create instances that are compile-time constants. If you use such a constructor, Dart will know for sure that after you create a Task its properties can never change. Thus, it will be comfortable making the assumption that Task(name: 'Buy milk') will always have the same properties of another Task(name: 'Buy milk') no matter what (They are equal). Under the hood, Dart uses the same instance for both. To implement this you'll have to change your code as follows:
class Task {
final String name;
final bool isDone;
const Task({this.name, this.isDone = false});
}
Override the hashCode and the == operator
If you don't want Task to be immutable, you should override the current primitive equality check of Task. from Dart's documentation:
The default hash code implementation provides an identity hash—two
objects generally only have the same hash code if they are the exact
same object. Likewise, the default behavior for == is identity.
If you are overriding ==, it implies you may have different objects
that are considered “equal” by your class. Any two objects that are
equal must have the same hash code. Otherwise, maps and other
hash-based collections will fail to recognize that the two objects are
equivalent.
The default hashCode represents the identity of an instance, but we want it to represent its properties in this case. Note that hashCode should not end up being the same for instances with different properties. It generally does not matter how it's implemented as long as this principle is preserved. I'll use the example provided here to give a possible implementation:
#override
int get hashCode {
int result = 17;
result = 37 * result + name.hashCode;
result = 37 * result + isDone.hashCode;
return result;
}
Now override the == operator to reflect the changes:
#override
bool operator ==(dynamic other) {
if (other is! Task) return false;
Task task = other;
return (task.name == name &&
task.isDone == isDone);
}
}
Note that Dart genrally recommends to avoid defining custom equality for mutable classes, and, following this design rule, the first solution I've listed is the easiest to implement.

Multi if statement in class parameters setting

I know that in the latest version of dart we can use if else statements inside the build method. Does anyone know if we can use also if else statement when we setting class parameters? I know I can do inline statement there but inline is a bit hard to read when there are multiple conditions
const int i = 0;
class Person {
// NewClass n = NewClass(a: i == 0 ? 'a' : 'b'); //<- inline statement working
NewClass n = NewClass(a: if(i == 0) 'a' else 'b'); //<- if statement doesn't
}
class NewClass {
final String a;
const NewClass({this.a});
}
Edit:
Basically in my case I've got an TextField widget where I set its's type parameter from enum (Type.text, Type.numeric...) According to this parameter I want to set The textField parameters (textCapitalization, maxLength and so on)
As per your comment, you are already creating an enum for specifying the type of the fields.
enum Type {text, numeric}
Now for specifying the properties of that particular type, you can add an extension on this enum, as shown below:
extension TextFieldProperties on Type {
int get maxLength {
if (this == Type.text) {
return 10;
}
return 12;
}
}
So in your field class you already have a type defined, you can use that type variable to get the properties of that particular type of field.
Type type = Type.text;
print(type.maxLength); // Will print 10
type = Type.numeric;
print(type.maxLength); // Will print 12
Note: It will work only in Dart 2.7 and above
You want the conditional expression (?:), not the conditional statement or literal entry (if), as you have already discovered.
The reason if doesn't work is that if only works as a statement or as a collection literal entry. It doesn't work in arbitrary expressions.
The reason for the distinction is that the if syntax allows you to omit the else branch. That only makes sense in places where "nothing" is a valid alternative. For a statement, "doing nothing" is fine. For a collection, "adding nothing" is also fine.
In an expression context, you must evaluate to a value or throw. There is no reasonable default that we can use instead of "nothing", so an if is not allowed instead of an expression.
Doesn't work because this syntax doesn't exist in Dart. The only way to do what you would like to do is to use the ternary operator.
If you try it in the DartPad you will get an error.
I suggest you to use a function to return the right value.

Understanding constructors and final [duplicate]

class X extends Y {
X(int a, int b) : super(a,b);
}
Can someone give me an explanation about the syntax meaning of the colon :?
This feature in Dart is called "initializer list".
It allows you to initialize fields of your class, make assertions and call the super constructor.
This means that it is not the same as the constructor body. As I said, you can only initialize variables and only access static members. You cannot call any (non-static) methods.
The benefit is that you can also initialize final variables, which you cannot do in the constructor body. You also have access to all parameters that are passed to the constructor, which you do not have when initializing the parameters directly in the parentheses.
Additionally, you can use class fields on the left-hand of an assignment with the same name as a parameter on the right-hand side that refers to a parameter. Dart will automatically use the class field on the left-hand side.
Here is an example:
class X {
final int number;
X(number) : number = number ?? 0;
}
The code above assigns the parameter named number to the final field this.number if it is non-null and otherwise it assigns 0. This means that the left-hand number of the assignment actually refers to this.number. Now, you can even make an assertion that will never fail (and is redundant because of that, but I want to explain how everything works together):
class X {
final int number;
X(number): number = number ?? 0, assert(number != null);
}
Learn more.
It's ok to access non static member in initializer list.
class Point {
num x, y;
Point(this.x, this.y);
Point.origin(): this.x = 10, this.y = 10;
}
main() {
Point p = Point.origin();
print(p.x); // 10
}