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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avatar #103

Merged
merged 19 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions forui/example/lib/example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ class _ExampleState extends State<Example> {
const SizedBox(height: 100),
FProgress(value: 0.9),
const SizedBox(height: 10),
FAlert(
title: const Text('Heads Up! Forui is coming to flutter!'),
subtitle: const Text('You can add components dfijsoi djfosfj to your app using the cli.'),
)
const FAvatar(
image: NetworkImage('https://picsum.photos/'),
//backgroundImage: NetworkImage('https://picsum.photos/250?image=9'),
placeholder: Text('DC'),
),
const SizedBox(height: 20),
],
);
}
12 changes: 11 additions & 1 deletion forui/lib/src/theme/theme_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ final class FThemeData with Diagnosticable {
/// The alert styles.
final FAlertStyles alertStyles;

/// The avatar styles.
Daviiddoo marked this conversation as resolved.
Show resolved Hide resolved
final FAvatarStyle avatarStyle;

/// The badge styles.
final FBadgeStyles badgeStyles;

Expand Down Expand Up @@ -78,12 +81,13 @@ final class FThemeData with Diagnosticable {
/// widget styles.
FThemeData({
required this.colorScheme,
required this.alertStyles,
required this.avatarStyle,
required this.badgeStyles,
required this.bottomNavigationBarStyle,
required this.buttonStyles,
required this.calendarStyle,
required this.cardStyle,
required this.alertStyles,
required this.checkboxStyle,
required this.dialogStyle,
required this.headerStyle,
Expand All @@ -110,6 +114,7 @@ final class FThemeData with Diagnosticable {
typography: typography,
style: style,
alertStyles: FAlertStyles.inherit(colorScheme: colorScheme, typography: typography, style: style),
avatarStyle: FAvatarStyle.inherit(colorScheme: colorScheme, typography: typography),
badgeStyles: FBadgeStyles.inherit(colorScheme: colorScheme, typography: typography, style: style),
bottomNavigationBarStyle: FBottomNavigationBarStyle.inherit(colorScheme: colorScheme, typography: typography),
buttonStyles: FButtonStyles.inherit(colorScheme: colorScheme, typography: typography, style: style),
Expand Down Expand Up @@ -149,6 +154,7 @@ final class FThemeData with Diagnosticable {
FTypography? typography,
FStyle? style,
FAlertStyles? alertStyles,
FAvatarStyle? avatarStyle,
FBadgeStyles? badgeStyles,
FBottomNavigationBarStyle? bottomNavigationBarStyle,
FButtonStyles? buttonStyles,
Expand All @@ -169,6 +175,7 @@ final class FThemeData with Diagnosticable {
typography: typography ?? this.typography,
style: style ?? this.style,
alertStyles: alertStyles ?? this.alertStyles,
avatarStyle: avatarStyle ?? this.avatarStyle,
badgeStyles: badgeStyles ?? this.badgeStyles,
bottomNavigationBarStyle: bottomNavigationBarStyle ?? this.bottomNavigationBarStyle,
buttonStyles: buttonStyles ?? this.buttonStyles,
Expand All @@ -193,6 +200,7 @@ final class FThemeData with Diagnosticable {
..add(DiagnosticsProperty('typography', typography, level: DiagnosticLevel.debug))
..add(DiagnosticsProperty('style', style, level: DiagnosticLevel.debug))
..add(DiagnosticsProperty('alertStyles', alertStyles, level: DiagnosticLevel.debug))
..add(DiagnosticsProperty('avatarStyle', avatarStyle))
..add(DiagnosticsProperty('badgeStyles', badgeStyles, level: DiagnosticLevel.debug))
..add(DiagnosticsProperty('bottomNavigationBarStyle', bottomNavigationBarStyle, level: DiagnosticLevel.debug))
..add(DiagnosticsProperty('buttonStyles', buttonStyles, level: DiagnosticLevel.debug))
Expand All @@ -218,6 +226,7 @@ final class FThemeData with Diagnosticable {
typography == other.typography &&
style == other.style &&
alertStyles == other.alertStyles &&
avatarStyle == other.avatarStyle &&
badgeStyles == other.badgeStyles &&
bottomNavigationBarStyle == other.bottomNavigationBarStyle &&
buttonStyles == other.buttonStyles &&
Expand All @@ -239,6 +248,7 @@ final class FThemeData with Diagnosticable {
typography.hashCode ^
style.hashCode ^
alertStyles.hashCode ^
avatarStyle.hashCode ^
badgeStyles.hashCode ^
bottomNavigationBarStyle.hashCode ^
buttonStyles.hashCode ^
Expand Down
158 changes: 158 additions & 0 deletions forui/lib/src/widgets/avatar.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';

import 'package:meta/meta.dart';

import 'package:forui/forui.dart';

/// An image element with a fallback for representing the user.
///
/// Typically used with a user's profile image, or, in the absence of
/// such an image, the user's initials.
///
/// If [image] fails then [placeholder] is used.
Daviiddoo marked this conversation as resolved.
Show resolved Hide resolved
class FAvatar extends StatelessWidget {
/// The style. Defaults to [FThemeData.avatarStyle].
final FAvatarStyle? style;

/// The background image of the circle.
///
/// If the [FAvatar] is to have the user's initials, use [placeholder] instead.
final ImageProvider image;

/// The fallback widget if [image] cannot be displayed.
///
/// If the avatar is to just have the user's initials, they are typically
/// provided using a [Text] widget as the [placeholder] with a [FAvatarStyle.backgroundColor]
///
/// If the [FAvatar] is to have an image, use [image] instead.
final Widget? placeholder;

/// Creates an [FAvatar].
const FAvatar({
required this.image,
this.style,
this.placeholder,
super.key,
});

@override
Widget build(BuildContext context) {
final style = this.style ?? context.theme.avatarStyle;

return Container(
constraints: style.constraints,
decoration: BoxDecoration(
color: style.backgroundColor,
shape: BoxShape.circle,
),
clipBehavior: Clip.hardEdge,
child: Center(
child: Image(
image: image,
errorBuilder: (context, exception, stacktrace) => DefaultTextStyle(
style: style.text,
child: placeholder ?? const _Placeholder(),
),
fit: BoxFit.cover,
),
),
);
}

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty('style', style))
..add(DiagnosticsProperty('image', image));
}
}

/// [FAvatar]'s style.
final class FAvatarStyle with Diagnosticable {
/// The background color.
Daviiddoo marked this conversation as resolved.
Show resolved Hide resolved
final Color backgroundColor;

/// The box constraints.
final BoxConstraints constraints;

/// The text style for the placeholder text.
final TextStyle text;

/// Creates a [FAvatarStyle].
FAvatarStyle({
required this.backgroundColor,
required this.constraints,
required this.text,
});

/// Creates a [FCardStyle] that inherits its properties from [colorScheme] and [typography].
FAvatarStyle.inherit({required FColorScheme colorScheme, required FTypography typography})
: backgroundColor = colorScheme.muted,
constraints = const BoxConstraints(minHeight: 40.0, minWidth: 40.0, maxHeight: 40.0, maxWidth: 40.0),
text = typography.base.copyWith(
color: colorScheme.mutedForeground,
height: 0,
);

/// Returns a copy of this [FAvatarStyle] with the given properties replaced.
///
/// ```dart
/// final style = FAvatarStyle(
/// backgroundColor: ...,
/// constraints: ...,
/// );
///
/// final copy = style.copyWith(constraints: ...);
///
/// print(style.backgroundColor == copy.backgroundColor); // true
/// print(style.constraints == copy.constraints); // false
/// ```
@useResult
FAvatarStyle copyWith({
Color? backgroundColor,
BoxConstraints? constraints,
TextStyle? text,
}) =>
FAvatarStyle(
backgroundColor: backgroundColor ?? this.backgroundColor,
constraints: constraints ?? this.constraints,
text: text ?? this.text,
);

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(ColorProperty('backgroundColor', backgroundColor))
..add(DiagnosticsProperty('constraints', constraints))
..add(DiagnosticsProperty('text', text));
}

@override
bool operator ==(Object other) =>
identical(this, other) ||
other is FAvatarStyle &&
runtimeType == other.runtimeType &&
backgroundColor == other.backgroundColor &&
constraints == other.constraints &&
text == other.text;

@override
int get hashCode => backgroundColor.hashCode ^ constraints.hashCode ^ text.hashCode;
}

class _Placeholder extends StatelessWidget {
const _Placeholder();

@override
Widget build(BuildContext context) {
final style = context.theme;

return FAssets.icons.userRound(
height: style.avatarStyle.constraints.maxHeight / 2,
colorFilter: ColorFilter.mode(style.colorScheme.mutedForeground, BlendMode.srcIn),
);
}
}
1 change: 1 addition & 0 deletions forui/lib/widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ library forui.widgets;
import 'package:forui/forui.dart';

export 'src/widgets/alert/alert.dart' hide Variant;
export 'src/widgets/avatar.dart';
export 'src/widgets/badge/badge.dart' hide Variant;
export 'src/widgets/bottom_navigation_bar/bottom_navigation_bar.dart';
export 'src/widgets/button/button.dart' hide Variant;
Expand Down
Loading