Skip to content

Commit

Permalink
ready for review
Browse files Browse the repository at this point in the history
  • Loading branch information
Daviiddoo committed Jul 23, 2024
1 parent c2e3d97 commit 30e361e
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 11 deletions.
31 changes: 31 additions & 0 deletions docs/pages/docs/avatar.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Tabs } from 'nextra/components';
import { Widget } from "../../components/widget";

# Avatar
An image element with a fallback for representing the user.

<Tabs items={['Preview', 'Code']}>
<Tabs.Tab>
<Widget name='avatar' query={{}}/>
</Tabs.Tab>
<Tabs.Tab>
```dart
FAvatar(
image: const NetworkImage('https://picsum.photos/250?image=9'),
placeholderBuilder: (_) => const Text('MN'),
),
```
</Tabs.Tab>
</Tabs>

## Usage

### `FAvatar(...)`

```dart
FAvatar(
image: const NetworkImage('https://picsum.photos/250?image=9'),
placeholderBuilder: (_) => const Text('MN'),
),
```

7 changes: 3 additions & 4 deletions forui/example/lib/example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,9 @@ class _ExampleState extends State<Example> {
const SizedBox(height: 100),
FProgress(value: 0.9),
const SizedBox(height: 10),
const FAvatar(
image: NetworkImage('https://picsum.photos/'),
//backgroundImage: NetworkImage('https://picsum.photos/250?image=9'),
placeholder: Text('DC'),
FAvatar(
image: const NetworkImage('https://picsum.photos/'),
placeholderBuilder: (_) => const Text('DC'),
),
const SizedBox(height: 20),
],
Expand Down
37 changes: 30 additions & 7 deletions forui/lib/src/widgets/avatar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,29 @@ import 'package:forui/forui.dart';
/// 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.
/// If [image] fails then [placeholderBuilder] is used.
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.
/// If the [FAvatar] is to have the user's initials, use [placeholderBuilder] 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]
/// provided using a [Text] widget in the [placeholderBuilder] with a [FAvatarStyle.backgroundColor]
///
/// If the [FAvatar] is to have an image, use [image] instead.
final Widget? placeholder;
final Widget Function(BuildContext)? placeholderBuilder;

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

Expand All @@ -49,11 +49,33 @@ class FAvatar extends StatelessWidget {
clipBehavior: Clip.hardEdge,
child: Center(
child: Image(
filterQuality: FilterQuality.medium,
image: image,
errorBuilder: (context, exception, stacktrace) => DefaultTextStyle(
style: style.text,
child: placeholder ?? const _Placeholder(),
child: placeholderBuilder != null ? placeholderBuilder!(context) : const _Placeholder(),
),
frameBuilder: (context, child, frame, wasSynchronouslyLoaded) {
if (wasSynchronouslyLoaded) {
return child;
}
if (frame == null) {
return DefaultTextStyle(
style: style.text,
child: placeholderBuilder != null ? placeholderBuilder!(context) : const _Placeholder(),
);
}
return child;
},
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) {
return child;
}
return DefaultTextStyle(
style: style.text,
child: placeholderBuilder != null ? placeholderBuilder!(context) : const _Placeholder(),
);
},
fit: BoxFit.cover,
),
),
Expand All @@ -65,7 +87,8 @@ class FAvatar extends StatelessWidget {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty('style', style))
..add(DiagnosticsProperty('image', image));
..add(DiagnosticsProperty('image', image))
..add(ObjectFlagProperty<Widget Function(BuildContext p1)?>.has('placeholderBuilder', placeholderBuilder));
}
}

Expand Down
Binary file added forui/test/golden/avatar/zinc-dark-with-image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added forui/test/resources/pante.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
54 changes: 54 additions & 0 deletions forui/test/src/widgets/avatar_golden_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
@Tags(['golden'])
library;

import 'dart:io';

import 'package:flutter/material.dart';

import 'package:flutter_test/flutter_test.dart';

import 'package:forui/forui.dart';
import '../test_scaffold.dart';

void main() {
group(
'FAvatar',
() {
for (final (name, theme, _) in TestScaffold.themes) {
testWidgets('$name with image', (tester) async {
final testWidget = MaterialApp(
home: TestScaffold(
data: theme,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: FAvatar(
image: FileImage(File('./test/resources/pante.jpg')),
placeholderBuilder: (_) => const Text('MN'),
),
),
),
);

/// current workaround for flaky image asset testing.
/// https://github.com/flutter/flutter/issues/38997
await tester.runAsync(() async {
await tester.pumpWidget(testWidget);
for (final element in find.byType(Image).evaluate()) {
final Image widget = element.widget as Image;
final ImageProvider image = widget.image;
await precacheImage(image, element);
await tester.pumpAndSettle();
}
});
await expectLater(
find.byType(TestScaffold),
matchesGoldenFile('avatar/$name-with-image.png'),
);
});

/// We will not be testing for the fallback behavior due to this issue on flutter
/// https://github.com/flutter/flutter/issues/107416
}
},
);
}
4 changes: 4 additions & 0 deletions samples/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ class _AppRouter extends $_AppRouter {
path: '/alert/default',
page: AlertRoute.page,
),
AutoRoute(
path: '/avatar/default',
page: AvatarRoute.page,
),
AutoRoute(
path: '/badge/default',
page: BadgeRoute.page,
Expand Down
26 changes: 26 additions & 0 deletions samples/lib/widgets/avatar.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import 'dart:io';

import 'package:flutter/material.dart';

import 'package:auto_route/auto_route.dart';
import 'package:forui/forui.dart';

import 'package:forui_samples/sample_scaffold.dart';

@RoutePage()
class AvatarPage extends SampleScaffold {
AvatarPage({
@queryParam super.theme,
});

@override
Widget child(BuildContext context) => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FAvatar(
image: const NetworkImage('https://picsum.photos/250?image=9'),
placeholderBuilder: (_) => const Text('MN'),
),
],
);
}

0 comments on commit 30e361e

Please sign in to comment.