Add flattenedToList and flattenedToSet (#328)

For iterables which are known to be exhausted after flattening
performance is better than the `sync*` implementation using a collection
literal.

Add `flattenedToList` as a performance improvement over `flattened.`
Add `flattenedToSet` as new behavior for flattening to unique elements.

Originally implemented in
https://github.com/dart-lang/sdk/commit/8d3b6ce54ccb09dd970cd45fc3cded4a35beac31
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f93bdbc..d4b8cf7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,9 @@
   generic.
 - `CanonicalizedMap`: added constructor `fromEntries`.
 - Mark "mixin" classes as `mixin`.
+- `extension IterableIterableExtension<T> on Iterable<Iterable<T>>`
+  - Add `flattenedToList` as a performance improvement over `flattened.`
+  - Add `flattenedToSet` as new behavior for flattening to unique elements.
 - Deprecate `transitiveClosure`. Consider using `package:graphs`.
 - Deprecate `whereNotNull()` from `IterableNullableExtension`. Use `nonNulls`
   instead - this is an equivalent extension available in Dart core since
diff --git a/lib/src/iterable_extensions.dart b/lib/src/iterable_extensions.dart
index 0d52fbb..e205006 100644
--- a/lib/src/iterable_extensions.dart
+++ b/lib/src/iterable_extensions.dart
@@ -892,6 +892,26 @@
       yield* elements;
     }
   }
+
+  /// The sequential elements of each iterable in this iterable.
+  ///
+  /// Iterates the elements of this iterable.
+  /// For each one, which is itself an iterable,
+  /// all the elements of that are added
+  /// to the returned list, before moving on to the next element.
+  List<T> get flattenedToList => [
+        for (final elements in this) ...elements,
+      ];
+
+  /// The unique sequential elements of each iterable in this iterable.
+  ///
+  /// Iterates the elements of this iterable.
+  /// For each one, which is itself an iterable,
+  /// all the elements of that are added
+  /// to the returned set, before moving on to the next element.
+  Set<T> get flattenedToSet => {
+        for (final elements in this) ...elements,
+      };
 }
 
 /// Extensions that apply to iterables of [Comparable] elements.
diff --git a/test/extensions_test.dart b/test/extensions_test.dart
index 6fa6e4a..9940e1d 100644
--- a/test/extensions_test.dart
+++ b/test/extensions_test.dart
@@ -1064,6 +1064,63 @@
               [1, 2, 3, 4]);
         });
       });
+      group('.flattenedToList', () {
+        var empty = iterable(<int>[]);
+        test('empty', () {
+          expect(iterable(<Iterable<int>>[]).flattenedToList, []);
+        });
+        test('multiple empty', () {
+          expect(iterable([empty, empty, empty]).flattenedToList, []);
+        });
+        test('single value', () {
+          expect(
+              iterable(<Iterable>[
+                iterable([1])
+              ]).flattenedToList,
+              [1]);
+        });
+        test('multiple', () {
+          expect(
+              iterable(<Iterable>[
+                iterable([1, 2]),
+                empty,
+                iterable([3, 4])
+              ]).flattenedToList,
+              [1, 2, 3, 4]);
+        });
+      });
+      group('.flattenedToSet', () {
+        var empty = iterable(<int>[]);
+        test('empty', () {
+          expect(iterable(<Iterable<int>>[]).flattenedToSet, <int>{});
+        });
+        test('multiple empty', () {
+          expect(iterable([empty, empty, empty]).flattenedToSet, <int>{});
+        });
+        test('single value', () {
+          expect(
+              iterable(<Iterable>[
+                iterable([1])
+              ]).flattenedToSet,
+              {1});
+        });
+        test('multiple', () {
+          expect(
+              iterable(<Iterable>[
+                iterable([1, 2]),
+                empty,
+                iterable([3, 4])
+              ]).flattenedToSet,
+              {1, 2, 3, 4});
+          expect(
+              iterable(<Iterable>[
+                iterable([1, 2, 3]),
+                empty,
+                iterable([2, 3, 4])
+              ]).flattenedToSet,
+              {1, 2, 3, 4});
+        });
+      });
     });
     group('of comparable', () {
       group('.min', () {