diff --git a/catalyst_voices/lib/widgets/common/columns_row.dart b/catalyst_voices/lib/widgets/common/columns_row.dart index e26b0cb85a2..28f16653b0a 100644 --- a/catalyst_voices/lib/widgets/common/columns_row.dart +++ b/catalyst_voices/lib/widgets/common/columns_row.dart @@ -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'; @@ -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((e) => Expanded(child: e)) + .separatedBy(SizedBox(width: mainAxisSpacing)) .toList(), ); } @@ -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(), ); } } diff --git a/catalyst_voices/lib/widgets/indicators/voices_status_indicator.dart b/catalyst_voices/lib/widgets/indicators/voices_status_indicator.dart index d9d0a4116cb..23ab44f899e 100644 --- a/catalyst_voices/lib/widgets/indicators/voices_status_indicator.dart +++ b/catalyst_voices/lib/widgets/indicators/voices_status_indicator.dart @@ -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: @@ -52,21 +52,14 @@ class VoicesStatusIndicator extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20), child: Column( mainAxisSize: MainAxisSize.min, - children: [ + children: [ _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(), ), ); } diff --git a/catalyst_voices/packages/catalyst_voices_shared/lib/src/catalyst_voices_shared.dart b/catalyst_voices/packages/catalyst_voices_shared/lib/src/catalyst_voices_shared.dart index 6f00b494ce6..24eebf7fd6a 100644 --- a/catalyst_voices/packages/catalyst_voices_shared/lib/src/catalyst_voices_shared.dart +++ b/catalyst_voices/packages/catalyst_voices_shared/lib/src/catalyst_voices_shared.dart @@ -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'; diff --git a/catalyst_voices/packages/catalyst_voices_shared/lib/src/utils/iterable_ext.dart b/catalyst_voices/packages/catalyst_voices_shared/lib/src/utils/iterable_ext.dart new file mode 100644 index 00000000000..1fd71195fed --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_shared/lib/src/utils/iterable_ext.dart @@ -0,0 +1,38 @@ +extension IterableExt on Iterable { + /// Syntax sugar for [separatedByIndexed] when index or value does not + /// matter. + /// + /// One common example is Column children separated by SizedBox. + /// + /// ```dart + /// Column( + /// children: [ + /// const Text('Title'), + /// const Text('Subtitle'), + /// const Text('Body'), + /// ].separatedBy(const SizedBox(height: 8)).toList(), + /// ); + /// ``` + Iterable 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 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)]; + } + } + } +} diff --git a/catalyst_voices/packages/catalyst_voices_shared/test/src/utils/iterable_ext_test.dart b/catalyst_voices/packages/catalyst_voices_shared/test/src/utils/iterable_ext_test.dart new file mode 100644 index 00000000000..7725e045470 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_shared/test/src/utils/iterable_ext_test.dart @@ -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 = []; + const separator = 99; + const expectedList = []; + + // When + final separatedSource = source.separatedBy(separator); + + // Then + expect(separatedSource, expectedList); + }); + + test('adds nothing when source has one item', () { + // Given + const source = [1]; + const separator = 99; + const expectedList = [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 = []; + const separator = 99; + const expectedList = []; + + // When + final separatedSource = source.separatedByIndexed((_, __) => separator); + + // Then + expect(separatedSource, expectedList); + }); + + test('adds nothing when source has one item', () { + // Given + const source = [1]; + const separator = 99; + const expectedList = [1]; + + // When + final separatedSource = source.separatedByIndexed((_, __) => separator); + + // Then + expect(separatedSource, expectedList); + }); + }); +} diff --git a/catalyst_voices/uikit_example/lib/examples/voices_buttons_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_buttons_example.dart index 8beec56fbc6..49aabcb89ea 100644 --- a/catalyst_voices/uikit_example/lib/examples/voices_buttons_example.dart +++ b/catalyst_voices/uikit_example/lib/examples/voices_buttons_example.dart @@ -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 { @@ -100,7 +100,7 @@ class _ButtonRow extends StatelessWidget { Widget build(BuildContext context) { return Row( children: _ButtonState.values - .map((state) { + .map((state) { return _buildButton( type, state, @@ -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(), ); } diff --git a/catalyst_voices/uikit_example/lib/examples/voices_indicators_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_indicators_example.dart index 9bea262884e..217af014376 100644 --- a/catalyst_voices/uikit_example/lib/examples/voices_indicators_example.dart +++ b/catalyst_voices/uikit_example/lib/examples/voices_indicators_example.dart @@ -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 { @@ -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, @@ -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: [ @@ -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: [ @@ -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(), ), ); } diff --git a/catalyst_voices/uikit_example/pubspec.yaml b/catalyst_voices/uikit_example/pubspec.yaml index 0f70d48a57d..01c7b6c8b85 100644 --- a/catalyst_voices/uikit_example/pubspec.yaml +++ b/catalyst_voices/uikit_example/pubspec.yaml @@ -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: