How to perform deep copy for a list to another list without affecting the original one in Flutter - flutter

I have two lists of Type object, _types and _filteredTypes:
List<Type> _types = []; // Original list
List<Type> _filteredTypes = []; // Where i bind filter the contents
a Type object is:
class Type extends Equatable {
final int id;
final String title;
List<SubType>? subTypes;
Type({
required this.id,
required this.title,
this.subTypes,
});
}
a SubType object is:
class SubType extends Equatable {
final int id;
final String title;
Type({
required this.id,
required this.title,
});
}
I need to filter the list based on search text, so whenever user types a letter the _filteredTypes being updated.
I do the filter on _onSearchChangedEvent() within setState() like this:
_filteredTypes = List.from(_types); // To get the original list without filter
for(var i = 0; i < _filteredTypes.length; i++) {
// This is where i filter the search result when a subType.title matches the search query:
_filteredTypes[i].subTypes = List.from(_types[i].subTypes!.where((element) => element.title.toLowerCase().contains(query!.toLowerCase())).toList());
}
// This is where i filter the search result and remove any type doesn't match any subType.title:
_filteredTypes.removeWhere((element) => element.subTypes!.length == 0);
bindTypes(); // To refresh the list widget
The problem is when i need get the original list i get the main type but type.subTypes is still filtered based on the previous search not the original one! even it is copied without reference _filteredTypes = List.from(_types);
It seems like a bug in Flutter, any idea guys?

List.from does not provide you with a deep copy of _types- it gives you a shallow copy. Meaning both _filteredTypes and _types share the same subTypes. It's similar in that behavior to this example
var sharedList = [1];
final listOne = [1, sharedList];
final listTwo = [1, sharedList];
sharedList[0] = 2;
Changing sharedList will change the value in both listOne and listTwo. If shared list was just an integer, changing that integer would not produce the same effect. Like in this example:
var sharedInteger = 1;
final listOne = [1, sharedInteger];
final listTwo = [1, sharedInteger];
sharedInteger = 2;
When you create an instance of a class may it be built in like List or your own custom class, what you get returned is a reference or a pointer to that instance/object. The object itself is allocated on the heap memory area rather than on the stack which means that this object can be referenced outside of functions. As in its life (object life) is not bound by the scope of the function so when you reach the end } of the function the object still exists, and its memory is freed by a special program called the garbage collector.
In dart as in many modern programming languages garbage collectors are used and objects are automatically allocated on the heap. In languages such as C++ for example you can allocate objects on the stack, and you have to be explicit about heap allocation, and deallocate any objects on the heap when you are done with them.
All of the above you can look up the gist of it is since subtypes is a list, it's a reference type so both _filteredTypes and _types have that reference. If you want a deep copy you can do that as well, and I'll leave it for you to look that up.

This is how to perform a deep copy for a list has sub-list, thanks moneer alhashim, your answer guided me.
I'm posting it as an answer to help someone else find the solution easy.
So, the key here is to map the original list types.map(...) and fill it manually instead of using List.from(), and that will create a new instance for deep objects.
First, i declared one function for each list:
//For the parent list:
List<Types> getNewTypesInstance {
// This function allows to perform a deep copy for the list.
return types.map((e) =>
Type(id: e.id, title: e.title, subTypes: e.subTypes))
.toList();
}
// For the child list:
List<SubType> getNewSubTypesInstance(List<SubType> lst) {
// This function allows to perform a deep copy for the list:
return lst.map((e) =>
SubType(id: e.id, title: e.title))
.toList();
}
And if you have more deep list(s), you will need third function to obtain it as new instance and so on.
Finally, the way how to call them is to write this code within setState():
_filteredTypes = getNewTypesInstance
for(var i = 0; i < _filteredTypes.length; i++) {
// This is where i filter the search result when a subType.title matches the search query:
_filteredTypes[i].subTypes = List.from(getNewSubTypesInstance(_types[i].subTypes!.where((element) => element.title.toLowerCase().contains(query!.toLowerCase())).toList()));
}
// This is where i filter the search result and remove any type doesn't match any subType.title:
_filteredTypes.removeWhere((element) => element.subTypes!.length == 0);
bindTypes(); // To refresh the list widget

Related

New list still references old list after being cloned

I have a list of maps which are initialized as follow
List<dynamic> _people = [];
Map<String, dynamic> _person = {
'image' : null,
'name': null,
'age': null
};
On one of the methods inside the program I intend to copy the value inside _people to a new List variable so I can make changes to it.
List _peopleCopy = List.from(_people);
for (var i=0; i < _people.length; i++) {
if (_people[i]['image'] != null) {
List<num> img = new List.from(_people[i]['image'].readAsBytesSync());
_peopleCopy[i]['image'] = img;
}
}
Now the problem here is that everytime I assign the value of img to _peopleCopy, it would change the respective value on _people list as well even though I did List.from() method to clone it. Obviously, I wanted to preserve the original content of _people list. What did I do wrong here?
For such case what I do is, I store the originalJson of the object inside a variable in the object class like below,
class People {
int? age;
String? image;
String? name;
Map<String, dynamic> originalJson = {};
People({this.age, this.image, this.name});
People.fromJson(Map<String, dynamic> json) {
originalJson = json;
age = json['age']?.toInt();
image = json['image']?.toInt();
name = json['name']?.toString();
}
Map<String, dynamic> toJson() {
'age' = age;
'image' = image;
'name' = name;
}
}
So when you are parsing the http reponse, now all you have to do is,
People.fromJson(json.decode(response.body));
This way you can preserve the json and while cloning the list of People, all you have to do is,
// With the following line _people will now be new list with new objects but with mutated _people data
_people = _people.map((e) => People.fromJson(e.toJson())).toList();
// With the following line _people will now be new list with new objects but with data which we have recieved from the API call
_people = _people.map((e) => People.fromJson(e.originalJson)).toList();
This way of maintaining the class should for sure help you out..
You have successfully created a new list. But the list contains the references to the same maps. So changing a map will change it in both lists since it's the same element.
You need to copy the elements, too.
Since your map also contains a dynamic type that you probably want to deep copy, I would suggest to just jsonEncode/jsonDecode your whole structure. Serializing and Deserializing is not the most efficient way, but it will create a deep copy of whatever structure is in there.
That said, Dart is a real programming language. It helps a lot to use it that way. If you use actual types, instead of dynamic, it becomes less guessing, praying and duct-typing and more solid, fact based programming. Since this seem to be JSON structures, use model classes. Then you can have your own deep copy methods and have compiler support that you just don't get when you use dynamic.

Searching for a solution for more memory friendly way while adding elements to a list of class in flutter/dart

I'm trying to produce a runtime table. Below class and codes are simplified version of my final purpose.
class AppModel {
int appID;
String appName;
AppModel({this.appID, this.appName});
}
I'm calculating, fetching some another data and trying to fill the following object like this:
// _newApps value is between 1-30 mostly but not limited
List<AppModel> theList = [];
for (int i = 0; i < _newApps; i++) {
AppModel _newRecord = AppModel();
_newRecord.appID = _getNewAppID();
_newRecord.appName = _getNewAppName();
theList.add(_newRecord);
}
So the question is the code creates a new AppModel instance for only adding the element to the list for every iteration inside the for loop. According to my program logic, this event can be repeated 100-150 times sometimes.
Is it normal or is there any more memory efficient way to do so?
Thank you in advance.
I would like to point out (a better approach) that instead of for Loop you could have used the map method on the Apps List you have. And instead of creating a object every time in the Loop create a constructor for returning the object instance using the required details.
Hope you find it useful.

Is there no simple way to make a deep of lists in flutter?

I tried List.from and [...orginalObject], but neither does a deep copy. In both cases it's clearly just copying the references. Which means if I change any data in the list in which I copied the data, the original data gets changed too.
It seems to me that the only way to do a for loop and define each of the entries the copied list with a new operator and the data from each of corresponding entries from the original list. Something as shown in the following image.
Seems like quite a tedious approach. Is there any simpler approach?
Thanks.
In vanilla Dart, not really, since this would require the ability to copy the leaf objects, and there is no way for the language to know how to do that for arbitrary objects.
However, the built_value and built_collection packages may solve your issue. One of their main features is deep immutability. First, you write your classes like this:
class Item {
final String foo;
final int bar;
Item(this.foo, this.bar);
}
// becomes
part 'item.g.dart';
abstract class Item extends Built<Item, ItemBuilder> {
Item._();
factory Item([void Function(ItemBuilder) updates]) = _$Item;
// boilerplate, can use tooling to auto-generate
String get foo;
int get bar;
}
This lets you create an immutable object using a mutable Builder and then finalizing it, e.g.:
final item1 = Item((b) {
// here b is an ItemBuilder, the mutable version of Item
b.foo = 'Hello';
b.bar = 123;
});
final item2 = item1.rebuild((b) => b.foo = 'New Value'); // Item(foo: 'New Value', bar: 123)
You can then use built_collection to work with deeply immuatble collections of built values, e.g.:
final list = BuiltList<Item>([item1, item2, item3]);
final deepCopy = list.rebuild((b) {}); // rebuild with no changes
final withNewElement = list.rebuild((b) => b.add(item4));
The important thing with all of these is that they are all immutable, and are "value objects", meaning (among other things) that they are considered equal if all their corresponding values are equal. You can consider each rebuild call to be returning you a brand new list with all the values copied over.
In fact, the library is smart enough to only create new objects for values that have actually changed, but because it is immutable, it acts like a deep copy, without the potential performance costs.
The main downside with these libraries is that they rely on code generation, so it requires a bit more setup than just putting it in your dependencies. In return, you get a lot of useful features in addition to having deeply immutable objects (like json serialization, null checking in pre-null-safety code, #memoized to cache results of expensive computations`, etc)
Dart does not have copy constructors nor have required clone() methods on objects, so there is no way to copy an arbitrary object, and therefore there is no way to make a deep copy of a collection of arbitrary objects.
cameron1024's answer suggesting the use of package:built_value and package:built_collection is good. If they're not suitable for you, however, you could make a function that copies elements via a callback:
List<T> copyList<T>(Iterable<T> items, T Function(T element) copier) {
return [
for (var item in items) copier(item),
];
}
class Foo {
int i;
String s;
Foo(this.i, this.s);
#override
String toString() => 'Foo($i, "$s")';
}
void main() {
var list = [Foo(1, 'one'), Foo(2, 'two'), Foo(3, 'three')];
print('Original: $list');
var copy = copyList<Foo>(list, (foo) => Foo(foo.i, foo.s));
list[0].i = 100;
print('Mutated: $list');
print('Copy: $copy');
}

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.