Skip to content

Commit e137566

Browse files
chrisbobbegnprice
authored andcommitted
emoji [nfc]: Add reusable EmojiWidget; use it in ReactionChip
There are several places where we switch on EmojiDisplay, mostly with TODOs to factor the switch out to something central. Here's a widget to accomplish that factoring, used first in ReactionChip.
1 parent cc69cce commit e137566

File tree

2 files changed

+94
-16
lines changed

2 files changed

+94
-16
lines changed

lib/widgets/emoji.dart

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,93 @@ import '../api/model/model.dart';
55
import '../model/emoji.dart';
66
import 'content.dart';
77

8+
/// A widget showing an emoji.
9+
class EmojiWidget extends StatelessWidget {
10+
const EmojiWidget({
11+
super.key,
12+
required this.emojiDisplay,
13+
required this.squareDimension,
14+
this.squareDimensionScaler = TextScaler.noScaling,
15+
this.imagePlaceholderStyle = EmojiImagePlaceholderStyle.square,
16+
this.neverAnimateImage = false,
17+
this.buildCustomTextEmoji,
18+
});
19+
20+
final EmojiDisplay emojiDisplay;
21+
22+
/// The base width and height to use for the emoji square.
23+
///
24+
/// This will be scaled by [squareDimensionScaler].
25+
///
26+
/// This is ignored when using the plain-text emoji style.
27+
final double squareDimension;
28+
29+
/// A [TextScaler] to apply to [squareDimension].
30+
///
31+
/// Defaults to [TextScaler.noScaling].
32+
///
33+
/// This is ignored when using the plain-text emoji style.
34+
final TextScaler squareDimensionScaler;
35+
36+
final EmojiImagePlaceholderStyle imagePlaceholderStyle;
37+
38+
/// Whether to show an animated emoji in its still (non-animated) variant
39+
/// only, even if device settings permit animation.
40+
///
41+
/// Defaults to false.
42+
final bool neverAnimateImage;
43+
44+
/// An optional callback to specify a custom plain-text emoji style.
45+
///
46+
/// If this is not passed, a simple [Text] widget with no added styling
47+
/// is used.
48+
final Widget Function()? buildCustomTextEmoji;
49+
50+
Widget _buildTextEmoji() {
51+
return buildCustomTextEmoji?.call()
52+
?? Text(textEmojiForEmojiName(emojiDisplay.emojiName));
53+
}
54+
55+
@override
56+
Widget build(BuildContext context) {
57+
final emojiDisplay = this.emojiDisplay;
58+
return switch (emojiDisplay) {
59+
ImageEmojiDisplay() => ImageEmojiWidget(
60+
emojiDisplay: emojiDisplay,
61+
size: squareDimension,
62+
textScaler: squareDimensionScaler,
63+
errorBuilder: (_, _, _) => switch (imagePlaceholderStyle) {
64+
EmojiImagePlaceholderStyle.square =>
65+
SizedBox.square(dimension: squareDimensionScaler.scale(squareDimension)),
66+
EmojiImagePlaceholderStyle.nothing => SizedBox.shrink(),
67+
EmojiImagePlaceholderStyle.text => _buildTextEmoji(),
68+
},
69+
neverAnimate: neverAnimateImage),
70+
UnicodeEmojiDisplay() => UnicodeEmojiWidget(
71+
emojiDisplay: emojiDisplay,
72+
size: squareDimension,
73+
textScaler: squareDimensionScaler),
74+
TextEmojiDisplay() => _buildTextEmoji(),
75+
};
76+
}
77+
}
78+
79+
/// In [EmojiWidget], how to present an image emoji when we don't have the image.
80+
enum EmojiImagePlaceholderStyle {
81+
/// A square of [EmojiWidget.squareDimension]
82+
/// scaled by [EmojiWidget.squareDimensionScaler].
83+
square,
84+
85+
/// A [SizedBox.shrink].
86+
nothing,
87+
88+
/// A plain-text emoji.
89+
///
90+
/// See [EmojiWidget.buildCustomTextEmoji] for how plain-text emojis are
91+
/// styled.
92+
text,
93+
}
94+
895
class UnicodeEmojiWidget extends StatelessWidget {
996
const UnicodeEmojiWidget({
1097
super.key,
@@ -91,7 +178,6 @@ class UnicodeEmojiWidget extends StatelessWidget {
91178
}
92179
}
93180

94-
95181
class ImageEmojiWidget extends StatelessWidget {
96182
const ImageEmojiWidget({
97183
super.key,

lib/widgets/emoji_reaction.dart

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -224,22 +224,14 @@ class ReactionChip extends StatelessWidget {
224224
emojiName: emojiName,
225225
).resolve(store.userSettings);
226226

227-
final emoji = switch (emojiDisplay) {
228-
UnicodeEmojiDisplay() => UnicodeEmojiWidget(
229-
size: _squareEmojiSize,
230-
textScaler: _squareEmojiScalerClamped(context),
231-
emojiDisplay: emojiDisplay),
232-
ImageEmojiDisplay() => ImageEmojiWidget(
233-
size: _squareEmojiSize,
234-
// Unicode and text emoji get scaled; it would look weird if image emoji didn't.
235-
textScaler: _squareEmojiScalerClamped(context),
236-
emojiDisplay: emojiDisplay,
237-
errorBuilder: (context, _, _) => _TextEmoji(
238-
emojiName: emojiName, selected: selfVoted),
239-
),
240-
TextEmojiDisplay() => _TextEmoji(
227+
final emoji = EmojiWidget(
228+
emojiDisplay: emojiDisplay,
229+
squareDimension: _squareEmojiSize,
230+
squareDimensionScaler: _squareEmojiScalerClamped(context),
231+
imagePlaceholderStyle: EmojiImagePlaceholderStyle.text,
232+
buildCustomTextEmoji: () => _TextEmoji(
241233
emojiName: emojiName, selected: selfVoted),
242-
};
234+
);
243235

244236
Widget result = Material(
245237
color: backgroundColor,

0 commit comments

Comments
 (0)