Skip to content

Commit

Permalink
feat: Iterable separatedBy/separatedByIndexed extension (#705)
Browse files Browse the repository at this point in the history
* feat: IterableExt separatedBy and separatedByIndexed

* feat: use separatedBy extension

* chore: use extension for widget spacings

---------

Co-authored-by: Dominik Toton <[email protected]>
  • Loading branch information
damian-molinski and dtscalac authored Aug 21, 2024
1 parent 9e13fe9 commit 9ed3380
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 47 deletions.
19 changes: 4 additions & 15 deletions catalyst_voices/lib/widgets/common/columns_row.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:catalyst_voices/widgets/seed_phrase/seed_phrases_completer.dart';
import 'package:catalyst_voices/widgets/seed_phrase/seed_phrases_picker.dart';
import 'package:catalyst_voices/widgets/seed_phrase/seed_phrases_viewer.dart';
import 'package:catalyst_voices_shared/catalyst_voices_shared.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';

Expand Down Expand Up @@ -49,13 +50,8 @@ class ColumnsRow extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: columns
.map((e) => _Column(spacing: crossAxisSpacing, children: e))
.map((e) => Expanded(child: e))
.expandIndexed(
(index, element) => [
if (index != 0) SizedBox(width: mainAxisSpacing),
element,
],
)
.map<Widget>((e) => Expanded(child: e))
.separatedBy(SizedBox(width: mainAxisSpacing))
.toList(),
);
}
Expand All @@ -75,14 +71,7 @@ class _Column extends StatelessWidget {
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: children
.expandIndexed(
(index, element) => [
if (index != 0) SizedBox(height: spacing),
element,
],
)
.toList(),
children: children.separatedBy(SizedBox(height: spacing)).toList(),
);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:catalyst_voices_brands/catalyst_voices_brands.dart';
import 'package:collection/collection.dart';
import 'package:catalyst_voices_shared/catalyst_voices_shared.dart';
import 'package:flutter/material.dart';

/// Enum representing the two possible types of status indicators:
Expand Down Expand Up @@ -52,21 +52,14 @@ class VoicesStatusIndicator extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
children: <Widget>[
_StatusContainer(
type: type,
child: status,
),
_TitleContainer(child: title),
_BodyContainer(child: body),
]
.expandIndexed(
(index, element) => [
if (index != 0) const SizedBox(height: 16),
element,
],
)
.toList(),
].separatedBy(const SizedBox(height: 16)).toList(),
),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export 'platform_aware_builder/platform_aware_builder.dart';
export 'responsive/responsive_builder.dart';
export 'responsive/responsive_child.dart';
export 'responsive/responsive_padding.dart';
export 'utils/iterable_ext.dart';
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
extension IterableExt<T> on Iterable<T> {
/// Syntax sugar for [separatedByIndexed] when index or value does not
/// matter.
///
/// One common example is Column children separated by SizedBox.
///
/// ```dart
/// Column(
/// children: <Widget>[
/// const Text('Title'),
/// const Text('Subtitle'),
/// const Text('Body'),
/// ].separatedBy(const SizedBox(height: 8)).toList(),
/// );
/// ```
Iterable<T> separatedBy(T value) => separatedByIndexed((_, __) => value);

/// Inserts a value generated by the provided [builder] function between each
/// element of the iterable.
///
/// The builder function receives the index of the current element and
/// the element itself.
/// Returns a new iterable containing the original elements separated by the
/// values generated by the builder function.
Iterable<T> separatedByIndexed(T Function(int index, T value) builder) sync* {
for (var index = 0; index < length; index++) {
final value = elementAt(index);
final isLast = index == length - 1;

// Yield the current value and the separator if it's not the last element.
if (isLast) {
yield* [value];
} else {
yield* [value, builder(index, value)];
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import 'package:catalyst_voices_shared/src/catalyst_voices_shared.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
group('separatedBy', () {
test('adds given separator between each item in the list', () {
// Given
const source = [1, 2, 3, 4];
const separator = 99;
const expectedList = [1, separator, 2, separator, 3, separator, 4];

// When
final separatedSource = source.separatedBy(separator);

// Then
expect(separatedSource, expectedList);
});

test('adds nothing when source is empty', () {
// Given
const source = <int>[];
const separator = 99;
const expectedList = <int>[];

// When
final separatedSource = source.separatedBy(separator);

// Then
expect(separatedSource, expectedList);
});

test('adds nothing when source has one item', () {
// Given
const source = <int>[1];
const separator = 99;
const expectedList = <int>[1];

// When
final separatedSource = source.separatedBy(separator);

// Then
expect(separatedSource, expectedList);
});
});

group('separatedByIndexed', () {
test('inserts correctly separator base on index', () {
// Given
const source = [1, 2, 3, 4];
const expectedList = [1, 0, 2, 1, 3, 2, 4];

// When
final separatedSource = source.separatedByIndexed((index, _) => index);

// Then
expect(separatedSource, expectedList);
});

test('adds nothing when source is empty', () {
// Given
const source = <int>[];
const separator = 99;
const expectedList = <int>[];

// When
final separatedSource = source.separatedByIndexed((_, __) => separator);

// Then
expect(separatedSource, expectedList);
});

test('adds nothing when source has one item', () {
// Given
const source = <int>[1];
const separator = 99;
const expectedList = <int>[1];

// When
final separatedSource = source.separatedByIndexed((_, __) => separator);

// Then
expect(separatedSource, expectedList);
});
});
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:catalyst_voices/widgets/widgets.dart';
import 'package:catalyst_voices_brands/catalyst_voices_brands.dart';
import 'package:collection/collection.dart';
import 'package:catalyst_voices_shared/catalyst_voices_shared.dart';
import 'package:flutter/material.dart';

enum _ButtonType {
Expand Down Expand Up @@ -100,7 +100,7 @@ class _ButtonRow extends StatelessWidget {
Widget build(BuildContext context) {
return Row(
children: _ButtonState.values
.map<Widget>((state) {
.map((state) {
return _buildButton(
type,
state,
Expand All @@ -112,12 +112,7 @@ class _ButtonRow extends StatelessWidget {
: null,
);
})
.expandIndexed(
(index, element) => [
if (index != 0) const SizedBox(width: 16),
element,
],
)
.separatedBy(const SizedBox(width: 16))
.toList(),
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:catalyst_voices/widgets/common/affix_decorator.dart';
import 'package:catalyst_voices/widgets/widgets.dart';
import 'package:catalyst_voices_shared/catalyst_voices_shared.dart';
import 'package:flutter/material.dart';

class VoicesIndicatorsExample extends StatelessWidget {
Expand All @@ -17,7 +18,6 @@ class VoicesIndicatorsExample extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 42, vertical: 24),
children: const [
Text('Status Indicator'),
SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
Expand Down Expand Up @@ -53,25 +53,15 @@ class VoicesIndicatorsExample extends StatelessWidget {
),
],
),
SizedBox(height: 22),
Text('Process Stepper Indicator'),
SizedBox(height: 8),
_Steps(),
SizedBox(height: 22),
Text('Linear - Indeterminate'),
SizedBox(height: 8),
VoicesLinearProgressIndicator(),
SizedBox(height: 16),
VoicesLinearProgressIndicator(showTrack: false),
SizedBox(height: 22),
Text('Linear - Fixed'),
SizedBox(height: 8),
VoicesLinearProgressIndicator(value: 0.25),
SizedBox(height: 16),
VoicesLinearProgressIndicator(value: 0.25, showTrack: false),
SizedBox(height: 22),
Text('Circular - Indeterminate'),
SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expand All @@ -80,9 +70,7 @@ class VoicesIndicatorsExample extends StatelessWidget {
VoicesCircularProgressIndicator(showTrack: false),
],
),
SizedBox(height: 22),
Text('Circular - Fixed'),
SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expand All @@ -91,7 +79,15 @@ class VoicesIndicatorsExample extends StatelessWidget {
VoicesCircularProgressIndicator(value: 0.75, showTrack: false),
],
),
],
].separatedByIndexed(
(index, value) {
return switch (value.runtimeType) {
Text => const SizedBox(height: 8),
VoicesLinearProgressIndicator => const SizedBox(height: 16),
_ => const SizedBox(height: 22),
};
},
).toList(),
),
);
}
Expand Down
2 changes: 2 additions & 0 deletions catalyst_voices/uikit_example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ dependencies:
path: ../packages/catalyst_voices_brands
catalyst_voices_localization:
path: ../packages/catalyst_voices_localization
catalyst_voices_shared:
path: ../packages/catalyst_voices_shared
collection: ^1.18.0
cupertino_icons: ^1.0.6
flutter:
Expand Down

0 comments on commit 9ed3380

Please sign in to comment.