diff --git a/lib/src/utilities/yaru_border_container.dart b/lib/src/utilities/yaru_border_container.dart new file mode 100644 index 000000000..d8fd4269b --- /dev/null +++ b/lib/src/utilities/yaru_border_container.dart @@ -0,0 +1,100 @@ +import 'package:flutter/material.dart'; + +import '../constants.dart'; + +/// A container with a rounded Yaru-style border. +class YaruBorderContainer extends StatelessWidget { + /// Creates a [YaruBorderContainer]. + const YaruBorderContainer({ + super.key, + this.alignment, + this.padding, + this.color, + this.width, + this.height, + this.constraints, + this.margin, + this.transform, + this.transformAlignment, + this.child, + this.clipBehavior = Clip.none, + this.border, + this.borderRadius, + }); + + /// See [Container.child]. + final Widget? child; + + /// See [Container.alignment]. + final AlignmentGeometry? alignment; + + /// See [Container.padding]. + final EdgeInsets? padding; + + /// See [Container.color]. + final Color? color; + + /// See [Container.width]. + final double? width; + + /// See [Container.height]. + final double? height; + + /// See [Container.constraints]. + final BoxConstraints? constraints; + + /// See [Container.margin]. + final EdgeInsetsGeometry? margin; + + /// See [Container.transform]. + final Matrix4? transform; + + /// See [Container.transformAlignment]. + final AlignmentGeometry? transformAlignment; + + /// See [Container.clipBehavior]. + final Clip clipBehavior; + + /// The border. + /// + /// The default border is 1px wide and the color is [ThemeData.dividerColor]. + final BoxBorder? border; + + /// The border radius. + /// + /// The default border is circular with the radius of `kYaruContainerRadius`. + final BorderRadiusGeometry? borderRadius; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final effectiveBorder = border ?? + Border.all(color: DividerTheme.of(context).color ?? theme.dividerColor); + final effectiveBorderRadius = + borderRadius ?? BorderRadius.circular(kYaruContainerRadius); + + return Container( + alignment: alignment, + constraints: constraints, + margin: margin, + transform: transform, + transformAlignment: transformAlignment, + clipBehavior: clipBehavior, + padding: padding, + height: height, + width: width, + foregroundDecoration: BoxDecoration( + border: effectiveBorder, + borderRadius: effectiveBorderRadius, + ), + decoration: BoxDecoration( + color: color, + borderRadius: effectiveBorderRadius, + ), + child: Material( + color: Colors.transparent, + child: child, + ), + ); + } +} diff --git a/lib/yaru_widgets.dart b/lib/yaru_widgets.dart index 184ec4040..d11170506 100644 --- a/lib/yaru_widgets.dart +++ b/lib/yaru_widgets.dart @@ -33,6 +33,7 @@ export 'src/pages/yaru_tabbed_page.dart'; export 'src/pages/yaru_tile.dart'; // Utilities export 'src/utilities/yaru_banner.dart'; +export 'src/utilities/yaru_border_container.dart'; export 'src/utilities/yaru_carousel.dart'; export 'src/utilities/yaru_draggable.dart'; export 'src/utilities/yaru_expandable.dart'; diff --git a/test/utilities/goldens/yaru_border_container-clip-dark.png b/test/utilities/goldens/yaru_border_container-clip-dark.png new file mode 100644 index 000000000..d0d3f28f8 Binary files /dev/null and b/test/utilities/goldens/yaru_border_container-clip-dark.png differ diff --git a/test/utilities/goldens/yaru_border_container-clip-light.png b/test/utilities/goldens/yaru_border_container-clip-light.png new file mode 100644 index 000000000..4181d0c44 Binary files /dev/null and b/test/utilities/goldens/yaru_border_container-clip-light.png differ diff --git a/test/utilities/goldens/yaru_border_container-custom-dark.png b/test/utilities/goldens/yaru_border_container-custom-dark.png new file mode 100644 index 000000000..573ceafd0 Binary files /dev/null and b/test/utilities/goldens/yaru_border_container-custom-dark.png differ diff --git a/test/utilities/goldens/yaru_border_container-custom-light.png b/test/utilities/goldens/yaru_border_container-custom-light.png new file mode 100644 index 000000000..e951e0821 Binary files /dev/null and b/test/utilities/goldens/yaru_border_container-custom-light.png differ diff --git a/test/utilities/goldens/yaru_border_container-default-dark.png b/test/utilities/goldens/yaru_border_container-default-dark.png new file mode 100644 index 000000000..c241899e7 Binary files /dev/null and b/test/utilities/goldens/yaru_border_container-default-dark.png differ diff --git a/test/utilities/goldens/yaru_border_container-default-light.png b/test/utilities/goldens/yaru_border_container-default-light.png new file mode 100644 index 000000000..e73f5a1fe Binary files /dev/null and b/test/utilities/goldens/yaru_border_container-default-light.png differ diff --git a/test/utilities/goldens/yaru_border_container-margin-clip-dark.png b/test/utilities/goldens/yaru_border_container-margin-clip-dark.png new file mode 100644 index 000000000..0cf8245fc Binary files /dev/null and b/test/utilities/goldens/yaru_border_container-margin-clip-dark.png differ diff --git a/test/utilities/goldens/yaru_border_container-margin-clip-light.png b/test/utilities/goldens/yaru_border_container-margin-clip-light.png new file mode 100644 index 000000000..76af8ac90 Binary files /dev/null and b/test/utilities/goldens/yaru_border_container-margin-clip-light.png differ diff --git a/test/utilities/goldens/yaru_border_container-margin-dark.png b/test/utilities/goldens/yaru_border_container-margin-dark.png new file mode 100644 index 000000000..072a7b251 Binary files /dev/null and b/test/utilities/goldens/yaru_border_container-margin-dark.png differ diff --git a/test/utilities/goldens/yaru_border_container-margin-light.png b/test/utilities/goldens/yaru_border_container-margin-light.png new file mode 100644 index 000000000..d9d780569 Binary files /dev/null and b/test/utilities/goldens/yaru_border_container-margin-light.png differ diff --git a/test/utilities/goldens/yaru_border_container-padding-clip-dark.png b/test/utilities/goldens/yaru_border_container-padding-clip-dark.png new file mode 100644 index 000000000..90e3f3bab Binary files /dev/null and b/test/utilities/goldens/yaru_border_container-padding-clip-dark.png differ diff --git a/test/utilities/goldens/yaru_border_container-padding-clip-light.png b/test/utilities/goldens/yaru_border_container-padding-clip-light.png new file mode 100644 index 000000000..08348de43 Binary files /dev/null and b/test/utilities/goldens/yaru_border_container-padding-clip-light.png differ diff --git a/test/utilities/goldens/yaru_border_container-padding-dark.png b/test/utilities/goldens/yaru_border_container-padding-dark.png new file mode 100644 index 000000000..90e3f3bab Binary files /dev/null and b/test/utilities/goldens/yaru_border_container-padding-dark.png differ diff --git a/test/utilities/goldens/yaru_border_container-padding-light.png b/test/utilities/goldens/yaru_border_container-padding-light.png new file mode 100644 index 000000000..08348de43 Binary files /dev/null and b/test/utilities/goldens/yaru_border_container-padding-light.png differ diff --git a/test/utilities/goldens/yaru_border_container-padding-margin-clip-dark.png b/test/utilities/goldens/yaru_border_container-padding-margin-clip-dark.png new file mode 100644 index 000000000..ef2032978 Binary files /dev/null and b/test/utilities/goldens/yaru_border_container-padding-margin-clip-dark.png differ diff --git a/test/utilities/goldens/yaru_border_container-padding-margin-clip-light.png b/test/utilities/goldens/yaru_border_container-padding-margin-clip-light.png new file mode 100644 index 000000000..4435b0d0c Binary files /dev/null and b/test/utilities/goldens/yaru_border_container-padding-margin-clip-light.png differ diff --git a/test/utilities/goldens/yaru_border_container-padding-margin-dark.png b/test/utilities/goldens/yaru_border_container-padding-margin-dark.png new file mode 100644 index 000000000..ef2032978 Binary files /dev/null and b/test/utilities/goldens/yaru_border_container-padding-margin-dark.png differ diff --git a/test/utilities/goldens/yaru_border_container-padding-margin-light.png b/test/utilities/goldens/yaru_border_container-padding-margin-light.png new file mode 100644 index 000000000..4435b0d0c Binary files /dev/null and b/test/utilities/goldens/yaru_border_container-padding-margin-light.png differ diff --git a/test/utilities/yaru_border_container_test.dart b/test/utilities/yaru_border_container_test.dart new file mode 100644 index 000000000..4a1a202aa --- /dev/null +++ b/test/utilities/yaru_border_container_test.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:yaru_icons/yaru_icons.dart'; +import 'package:yaru_widgets/yaru_widgets.dart'; + +import '../yaru_golden_tester.dart'; + +void main() { + testWidgets( + 'golden images', + (tester) async { + final variant = goldenVariant.currentValue!; + + final controller = ScrollController(initialScrollOffset: 72); + + await tester.pumpScaffold( + Padding( + padding: const EdgeInsets.all(10), + child: YaruBorderContainer( + color: variant.value!['color'], + border: variant.value!['border'], + borderRadius: variant.value!['borderRadius'], + margin: variant.value!['margin'] ?? EdgeInsets.zero, + padding: variant.value!['padding'] ?? EdgeInsets.zero, + clipBehavior: variant.value!['clipBehavior'] ?? Clip.none, + child: ListView.builder( + controller: controller, + itemBuilder: (context, index) => ListTile( + leading: const Icon(YaruIcons.star_filled), + title: Text('Tile $index'), + onTap: () {}, + ), + ), + ), + ), + themeMode: variant.themeMode, + size: const Size(200, 200), + ); + await tester.pumpAndSettle(); + + await tester.down(find.text('Tile 1')); + await tester.pumpAndSettle(); + + await expectLater( + find.byType(YaruBorderContainer), + matchesGoldenFile('goldens/yaru_border_container-${variant.label}.png'), + ); + }, + variant: goldenVariant, + tags: 'golden', + ); +} + +final goldenVariant = ValueVariant({ + ...goldenThemeVariants('default', {}), + ...goldenThemeVariants('clip', { + 'clipBehavior': Clip.antiAlias, + }), + ...goldenThemeVariants('padding', { + 'padding': const EdgeInsets.all(10), + }), + ...goldenThemeVariants('padding-clip', { + 'padding': const EdgeInsets.all(10), + 'clipBehavior': Clip.antiAlias, + }), + ...goldenThemeVariants('margin', { + 'margin': const EdgeInsets.all(10), + }), + ...goldenThemeVariants('margin-clip', { + 'margin': const EdgeInsets.all(10), + 'clipBehavior': Clip.antiAlias, + }), + ...goldenThemeVariants('padding-margin', { + 'padding': const EdgeInsets.all(10), + 'margin': const EdgeInsets.all(10), + }), + ...goldenThemeVariants('padding-margin-clip', { + 'padding': const EdgeInsets.all(10), + 'margin': const EdgeInsets.all(10), + 'clipBehavior': Clip.antiAlias, + }), + ...goldenThemeVariants('custom', { + 'color': Colors.blue, + 'border': Border.all(color: Colors.red, width: 2), + 'borderRadius': BorderRadius.circular(20), + 'clipBehavior': Clip.antiAlias, + }), +});