Iterable & Iterator in Flutter & Dart
An in-depth explanation about Iterable & Iterator, Their `methods` & `properties` with Examples.
Iterable
- If you are familiar with Dart/Flutter you are knowingly or unknowingly using Iterable in your application or program.
- You might have come across objects like
List,Map, andSetwhen building an application. - These are nothing but Iterable. You can verify this by taking a look into the implementation of these collections.
- List :

- Set :

- An Iterable as the name suggests, it has something to do with iteration.
- The definition of Iterable given by Dart is :
An Iterable is a collection of elements that can be accessed sequentially
- If we look at the implementation of the Iterable, we can see that Iterable is an abstract class.

- That means we cannot instantiate the Iterable class.
- But we can create a new Iterable by creating List, Set, Map. For example :

- We cannot access the value by
index, likenumbers[index]. It's becauseIterabledoesn't have[ ]operator like List. - Let's try to access the element of the
Iterableby[ ], And see what error it throws: 
![[notdefine].png](https://cdn.hashnode.com/res/hashnode/image/upload/v1629450579721/Gf8Ke6z0i.png?auto=compress,format&format=webp)
- As expected it threw an error that says operator [ ] is not defined in Iterable class.
- Iterable has many useful properties and methods available.
- As
List,Setis Implementing Iterable , All the methods that are available in Interable are also accessible inListandSet. - A
Mapis different thanListandSet, because it containskey:valuepairs. We cannot directly loop through theMapvariable as we're doing inSetandList. BecauseMapis not implementingIterable 

- Map has
keysandvaluesIterables inside it. - Keys Iterable

- Values Iterable

- So to iterate over the
Mapwe can do something like : 
Iterator
- In simple terms Iterator is a single element of a collection that we can get one at a time.
- Iterator is an
Interface. Thefor-inloop we've used in the above examples is using thisIteratorinterface under the hood. - The thing is Iterable doesn't know how to iterate over the collection. That is why Iterable has an
Iteratorgetter inside it. 
- Its job is to move sequentially through all the elements of the iterable.
- All iterables have an iterator


- The Iterator has a method called
moveNext(), which returns a boolean value. - If it returns
truethen it means that iterable contains the next element. - if it returns
falsethen it means that there are no further elements. - Iterator has one very important getter called
current. Which gives thecurrent elementof the iterable. - Example :

Properties of Iterable :
first&last:firstreturns the first value/element from theIterable.lastreturns the last value/element from theIterable.firstExample :void main() { final List<String> listOfCar = ["Audi","BMW","Ferrari","Mercedes"]; print(listOfCar.first); }
- For accessing the
firstelement fromMap: void main() { final Map<String,dynamic> mapOfNumbers = {"0":"Zero","1":"One","2":"Two","3":"Three"}; print(mapOfNumbers.values.first); }
lastExample :final List<String> listOfCar = ["Audi","BMW","Ferrari","Mercedes"]; print(listOfCar.last);
isEmpty&isNotEmpty:isEmptyreturnstrueifIterableis empty, otherwisefalse.isNotEmptyreturnstrueifIterableis not empty, otherwisefalsevoid main() { final List<String> listOfCar = ["Audi","BMW","Ferrari","Mercedes"]; print(listOfCar.isEmpty); }
- Same for the
Map void main() { final Map<String,dynamic> mapOfNumbers = {"0":"Zero","1":"One","2":"Two","3":"Three"}; print(mapOfNumbers.isEmpty); // false }
length:- Returns the total number of elements/values present in the
Iterable. void main() { final Map<String,dynamic> mapOfNumbers = {"0":"Zero","1":"One","2":"Two","3":"Three"}; final List<String> listOfCar = ["Audi","BMW","Ferrari","Mercedes"]; print("List: "+listOfCar.length.toString()); print("Map: "+mapOfNumbers.length.toString()); }
runtimeType:- Returns the type of the object.
void main() { final Map<String,dynamic> mapOfNumbers = {"0":"Zero","1":"One","2":"Two","3":"Three"}; final List<String> listOfCar = ["Audi","BMW","Ferrari","Mercedes"]; print(listOfCar.runtimeType); print(mapOfNumbers.runtimeType); }
single:- If the Iterable has only one element inside it, then
singlereturns that value/element. void main() { final List<String> listOfCar = ["Audi"]; print(listOfCar.single); }
- If there are more than one element then it will throw an error.
Methods of Iterable :
elementAt(index):- As we've seen in the explanation of
Iterablethat we cannot access the element ofIterableby[ ]. BecauseIterabledoesn't have[ ]defined. - So we can use
elementAt()method to access any element from theIterable. void main() { final Map<String,dynamic> mapOfNumbers = {"0":"Zero","1":"One","2":"Two","3":"Three"}; final Iterable<String> listOfCar = ["Audi","BMW","Ferrari","Mercedes"]; print(listOfCar.elementAt(0)); print(mapOfNumbers.values.elementAt(0)); }
contains(element):- Returns
trueif theIterablecontains the element passed inside thecontains()method. void main() { final Map<String,dynamic> mapOfNumbers = {"0":"Zero","1":"One","2":"Two","3":"Three"}; final List<String> listOfCar = ["Audi","BMW","Ferrari","Mercedes"]; print(listOfCar.contains("BMW")); print(mapOfNumbers.values.contains("Five")); }
firstWhere()- This will return the first encountered element which satisfies the condition given to the function.
- The function must return
booleanvalue. - This method takes two things: 1. The function which returns the value if a certain condition is satisfied & 2.
orElsefunction which will be called when no condition is satisfied. void main() { final List<String> listOfCar = ["Audi","Audi A5","BMW","Ferrari","Mercedes"]; print(listOfCar.firstWhere((element)=> element.startsWith("Au"))); }
orElsevoid main() { final List<String> listOfCar = ["Audi","Audi A5","BMW","Ferrari","Mercedes"]; print(listOfCar.firstWhere((element)=> element.startsWith("Zu"),orElse: ()=> "No Element Found")); }
where()- Takes
booleanfunction. - This function will iterate over all the elements and checks if the condition given to the function is satisfied on that element.
void main() { final List<String> listOfCar = ["Audi","Audi A5","BMW","Ferrari","Mercedes"]; print(listOfCar.where((element)=> element.length > 4)); }
Map:void main() { final Map<String,dynamic> mapOfNumbers = {"0":"Zero","1":"One","2":"Two","3":"Three"}; print(mapOfNumbers.values.where((element)=> element.length > 3)); }
toSet():- Used to convert from type
TtoSet, whereTcan be :List,Mapetc. - The
Setwill remove duplicate element from previous iterable. void main() { final List listOfCar = ["Audi","Audi","BMW","Ferrari","Mercedes"]; print(listOfCar.toSet()); }
toList():- Used to convert any iterable to
List. - Takes one optional argument
growable. Iftruewe can furtheraddelements in list, otherwiseadd()will not work. void main() { final Set<String> setOfCar = {"Audi","BMW","Ferrari","Mercedes"}; print(setOfCar.toList()); }
growable : true(default) :void main() { final Set<String> setOfCar = {"Audi","BMW","Ferrari","Mercedes"}; final List listOfCar = setOfCar.toList(); listOfCar.add("Jeep"); print(listOfCar); }
growable : false:void main() { final Set<String> setOfCar = {"Audi","BMW","Ferrari","Mercedes"}; final List listOfCar = setOfCar.toList(growable: false); listOfCar.add("Jeep"); print(listOfCar); }
- As you can see
addis not working now.
takeWhile():- This will return every element in a list from index 0 to the element that first satisfies the condition.
void main() { final List<String> listOfCar = ["Audi","Audi A5","BMW","Ferrari","Mercedes","Audi A3"]; final fileteredList = listOfCar.takeWhile((element) => element.startsWith("Au")); print(fileteredList); }
take(int count):- This will return every element in a list from index 0 to the index specified in this method.
void main() { final List<String> listOfCar = ["Audi","Audi A5","BMW","Ferrari","Mercedes","Audi A3"]; final fileteredList = listOfCar.take(3); print(fileteredList); }
skipWhile():- Returns an iterable that skips leading elements while the provided predicate is satisfied.
void main() { final List<String> listOfCar = ["Audi","Ferrari","BMW","Mercedes","Audi A3"]; final fileteredList = listOfCar.skipWhile((element)=> element.endsWith("i")); print(fileteredList); }
skip(int count):- This will skip every element in a list from index 0 to the index specified in this method and returns the remaining element.
void main() { final List<String> listOfCar = ["Audi","Ferrari","BMW","Mercedes","Audi A3"]; final fileteredList = listOfCar.skip(3); print(fileteredList); }
singleWhere():- Returns the single element that satisfies a given condition.
- If no element is found this method will give an error. To handle this error using
orElse - This will also throw an error if any duplicate value is found.
void main() { final List<String> listOfCar = ["Audi","Ferrari","BMW","Mercedes"]; final fileteredList = listOfCar.singleWhere((element)=> element.startsWith("Au"),orElse:()=> "No Element Found"); print(fileteredList); }
reduce():- This will reduce a given
iterableto a single value by combining elements of theiterableusing a provided function. - The iterable must have at least one element. If it has only one element, that element is returned.
void main() { final List<int> listNumber = [1,2,3,4]; final reduced = listNumber.reduce((value,element){ print("value: "+value.toString()); print("element "+element.toString()); return value+element; }); print("total: "+reduced.toString()); }
map():- Returns a brand new list.
- The function defined in
mapwill run on each element. - After that new list will be returned.
void main() { final List<int> listNumber = [1,2,3,4]; final filteredList = listNumber.map((element)=> element += 1); print(filteredList); }
lastWhere():- Returns the last element that satisfies the given condition.
void main() { final List<int> listNumber = [1,2,3,4]; final fileteredElement = listNumber.lastWhere((element)=> element.isEven); print(fileteredElement); }
join():- Joins every element of the iterable with a given
separatorand returns it as String. void main() { final List<int> listNumber = [1,2,3,4]; final fileteredElement = listNumber.join(","); print(fileteredElement); }
forEach:- The function defined inside
forEachwill run on every element of the iterable. - The difference between
mapandforEachisforEachdoesn't return a new list whereasmapdoes. void main() { final List<int> listNumber = [1,2,3,4]; listNumber.forEach((element)=> print(element * 2)); }
followedBy():- If you want to add new
iterableinside your currentiterablethen you can usefollowdBy()by passing youriterableinside it. void main() { final List listOfFruits = ["Apple","Banana","Carrot"]; final newFruitList = listOfFruits.followedBy(["Dates","Emu"]); print(newFruitList); }
fold:- It is the same as the
reducemethod. - It reduces an
iterableto asinglevalue by iteratively combining each element of the collection with an existing value. void main() { final List<String> listOfFruits = ["Apple","Banana","Carrot"]; listOfFruits.fold(0, (value, element) { print("value: "+value.toString()); print("element: "+ element.toString()); return value + element.length; }); }
- Difference between
reduceandfoldis:reducecan only be used on non-empty collections with functions that return the same type as the types contained in the collection whereasfoldcan be used in all cases. - We cannot compute the sum of the length of all strings in a list with
reducebut you can usefoldto do that.
expand:- If you have nested
iterablesinside one mainiterableand you want to extract out those iterables into one single iterable then you can useexpandto do this. void main() { final nestedList = [["Apple","Banana","Carrot"],[1,2,3],[true,false]]; final flattenedList = nestedList.expand((list)=>list); print(flattenedList); }
void main() { final listOfFruit = ["Apple","Banana","Carrot"]; final duplicateFruits = listOfFruit .expand((element)=>[element,element]); print(duplicateFruits ); }
every():- This method returns a boolean depending upon whether all elements satisfies the condition or not.
void main() { final listOfFruits = ["Apple","A Banana","A Carrot"]; final filteredList = listOfFruits.every((element) => element.startsWith("A")); print(filteredList); }
any():- Returns
trueif any element satisfies the given condition, otherwisefalse. void main() { List<int> listOfNumbers = [11,23,3,45,5]; print(listOfNumbers.any((element)=> element.isEven)); }
cast():- This will return a new list with a given
castTypeso that you can use it in places where the analyzer expectsIterable<Type>. - This is a very rarely used method.
- Remember this will not cast an element of the
iterable. - It only gives an illusion to the analyzer that it got that
typethat it wanted. void main() { List<num> listOfNumbers = [5, 20, 12.5]; List<double> castedDoubleList = listOfNumbers.cast<double>(); print("casted list: "+castedDoubleList.toString()); print("casted list type: "+castedDoubleList.runtimeType.toString()); print("casted list element type: "); castedDoubleList.forEach((element)=> print(element.runtimeType)); }
- We can use this
castedDoubleListif any function requires it. For example: void main() { List<num> listOfNumbers = [5, 20, 12.5]; List<double> castedDoubleList = listOfNumbers.cast<double>(); reverseDoubleList(castedDoubleList); }reverseDoubleList(List<double> doubleList){ print(doubleList.reversed); }
Wrapping Up
- These all are very useful methods and properties which are very handy during the development.
- Hope you liked it. Thanks for reading.
- Comments and Feedbacks are welcomed ๐

ย

