Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add YaruInfoBadge, YaruInfoBox, YaruTranslucentBorderContainer #880

Merged
merged 10 commits into from
Mar 9, 2024
28 changes: 28 additions & 0 deletions example/lib/example_page_items.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:yaru/yaru.dart';
import 'code_snippet_button.dart';
import 'pages/autocomplete_page.dart';
import 'pages/banner_page.dart';
import 'pages/border_container_page.dart';
import 'pages/carousel_page.dart';
import 'pages/checkbox_page.dart';
import 'pages/choice_chip_bar_page.dart';
Expand All @@ -17,6 +18,7 @@ import 'pages/expansion_panel_page.dart';
import 'pages/full_color_icons_page.dart';
import 'pages/icon_button_page.dart';
import 'pages/icons_page/icons_page.dart';
import 'pages/info_page.dart';
import 'pages/navigation_page.dart';
import 'pages/option_button_page.dart';
import 'pages/page_indicator.dart';
Expand Down Expand Up @@ -332,4 +334,30 @@ final examplePageItems = <PageItem>[
? const Icon(YaruIcons.colors_filled)
: const Icon(YaruIcons.colors),
),
PageItem(
title: 'YaruInfo',
pageBuilder: (context) {
return const InfoPage();
},
iconBuilder: (context, selected) => selected
? const Icon(YaruIcons.information_filled)
: const Icon(YaruIcons.information),
floatingActionButtonBuilder: (_) => const CodeSnippedButton(
snippetUrl:
'https://raw.githubusercontent.com/ubuntu/yaru.dart/main/example/lib/pages/info_page.dart',
),
),
PageItem(
title: 'YaruBorderContainer',
pageBuilder: (context) {
return const BorderContainerPage();
},
iconBuilder: (context, selected) => selected
? const Icon(YaruIcons.cloud_filled)
: const Icon(YaruIcons.cloud),
floatingActionButtonBuilder: (_) => const CodeSnippedButton(
snippetUrl:
'https://raw.githubusercontent.com/ubuntu/yaru.dart/main/example/lib/pages/border_container_page.dart',
),
),
].sortedBy((page) => page.title);
57 changes: 57 additions & 0 deletions example/lib/pages/border_container_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
import 'package:yaru/yaru.dart';

class BorderContainerPage extends StatefulWidget {
const BorderContainerPage({super.key});

@override
State<BorderContainerPage> createState() => _BorderContainerPageState();
}

class _BorderContainerPageState extends State<BorderContainerPage> {
int _take = 80;

@override
Widget build(BuildContext context) {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(kYaruPagePadding),
child: Slider(
value: _take.toDouble(),
min: 1,
max: _lorem.characters.length.toDouble(),
onChanged: (v) => setState(() => _take = v.toInt()),
),
),
Expanded(
child: ListView.separated(
padding: const EdgeInsets.all(kYaruPagePadding),
itemCount: YaruInfoType.values.length,
itemBuilder: (context, index) {
return YaruBorderContainer(
padding: const EdgeInsets.all(8),
child: Text(_lorem.characters.take(_take).toString()),
);
},
separatorBuilder: (context, index) {
final info = YaruInfoType.values[index];

return Padding(
padding: const EdgeInsets.symmetric(vertical: kYaruPagePadding),
child: YaruTranslucentContainer(
padding: const EdgeInsets.all(8),
color: info.getColor(context),
child: Text(_lorem.characters.take(_take).toString()),
),
);
},
),
),
],
);
}
}

const _lorem =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';
137 changes: 137 additions & 0 deletions example/lib/pages/info_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:yaru/yaru.dart';

class InfoPage extends StatefulWidget {
const InfoPage({super.key});

@override
State<InfoPage> createState() => _InfoPageState();
}

class _InfoPageState extends State<InfoPage> {
int _take = 80;
bool _idea = false;

@override
Widget build(BuildContext context) {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(kYaruPagePadding),
child: Row(
children: [
IconButton(
tooltip: 'Custom icons and colors are possible',
isSelected: _idea,
onPressed: () => setState(() => _idea = !_idea),
icon: const Icon(
YaruIcons.light_bulb_off,
size: 30,
),
selectedIcon: const Icon(
YaruIcons.light_bulb_on,
size: 30,
),
),
const SizedBox(
width: 5,
),
Expanded(
child: Slider(
value: _take.toDouble(),
min: 1,
max: _lorem.characters.length.toDouble(),
onChanged: (v) => setState(() => _take = v.toInt()),
),
),
],
),
),
Expanded(
child: ListView.builder(
padding: const EdgeInsets.all(kYaruPagePadding),
itemCount: YaruInfoType.values.length,
itemBuilder: (context, index) {
final info = YaruInfoType.values[index];
return Column(
mainAxisSize: MainAxisSize.min,
children: [
YaruInfoBox(
icon: info == YaruInfoType.information && _idea
? const Icon(YaruIcons.light_bulb_on)
: null,
color: info == YaruInfoType.information && _idea
? YaruColors.magenta
: null,
yaruInfoType: info,
title: Text(info.name.capitalize()),
subtitle: Text(_lorem.characters.take(_take).toString()),
trailing: info == YaruInfoType.information && _idea
? const _CopyButton(
text: _lorem,
)
: null,
),
Padding(
padding:
const EdgeInsets.symmetric(vertical: kYaruPagePadding),
child: Row(
children: [
YaruInfoBadge(
yaruInfoType: info,
title: Text(info.name.capitalize()),
),
],
),
),
],
);
},
),
),
],
);
}
}

class _CopyButton extends StatelessWidget {
const _CopyButton({required this.text});

final String text;

@override
Widget build(BuildContext context) {
return OutlinedButton(
style: OutlinedButton.styleFrom(
minimumSize: const Size.square(kYaruTitleBarItemHeight),
maximumSize: const Size.square(kYaruTitleBarItemHeight),
fixedSize: const Size.square(kYaruTitleBarItemHeight),
side: BorderSide(
width: 1,
color: YaruColors.magenta.withOpacity(0.5),
),
padding: EdgeInsets.zero,
),
onPressed: () {
Clipboard.setData(ClipboardData(text: text));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Copied')),
);
},
child: const Icon(
YaruIcons.copy,
color: YaruColors.magenta,
),
);
}
}

extension _StringExtension on String {
String capitalize() {
return '${this[0].toUpperCase()}${substring(1).toLowerCase()}';
}
}

const _lorem =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';
89 changes: 89 additions & 0 deletions lib/src/widgets/yaru_border_container.dart
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,92 @@ class YaruBorderContainer extends StatelessWidget {
);
}
}

/// A container with a rounded Yaru-style border and translucent background color
/// derived from the border color.

class YaruTranslucentContainer extends StatelessWidget {
/// Creates a [YaruTranslucentContainer].
const YaruTranslucentContainer({
super.key,
required this.color,
this.opacity = 0.1,
this.child,
this.alignment,
this.padding,
this.width,
this.height,
this.constraints,
this.margin,
this.transform,
this.transformAlignment,
this.clipBehavior = Clip.none,
this.border,
this.borderRadius,
});

/// See [Container.child].
final Widget? child;

/// See [Container.alignment].
final AlignmentGeometry? alignment;

/// See [Container.padding].
final EdgeInsetsGeometry? 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;

/// The opacity value used to derive the background color from the border
final double opacity;

@override
Widget build(BuildContext context) {
return YaruBorderContainer(
alignment: alignment,
padding: padding,
color: color.withOpacity(opacity),
border: Border.all(color: color),
width: width,
height: height,
constraints: constraints,
margin: margin,
transform: transform,
transformAlignment: transformAlignment,
clipBehavior: clipBehavior,
borderRadius: borderRadius,
child: child,
);
}
}