Skip to content

Commit ccd496a

Browse files
committed
compose_box: Implement content input redesign
`ClipRect`'s size is determined by the `ConstrainedBox`. This is mainly for showing the content through the `contentPadding` of the `TextField`, so that our `InsetShadowBox` can fade it smoothly there. The shadow is always there, but it is only visible when the `TextField` is long enough to be scrollable. See also: - zulip#928 (comment), which elaborates on the issue we intend to solve with the `ClipRect` setup. - https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=3954-13395 - https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=3862-14350 Fixes: zulip#915 Signed-off-by: Zixuan James Li <[email protected]>
1 parent 99cfed8 commit ccd496a

File tree

1 file changed

+53
-20
lines changed

1 file changed

+53
-20
lines changed

lib/widgets/compose_box.dart

+53-20
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import 'autocomplete.dart';
1717
import 'color.dart';
1818
import 'dialog.dart';
1919
import 'icons.dart';
20+
import 'inset_shadow.dart';
2021
import 'store.dart';
2122
import 'text.dart';
2223
import 'theme.dart';
@@ -287,30 +288,62 @@ class _ContentInput extends StatelessWidget {
287288

288289
@override
289290
Widget build(BuildContext context) {
290-
ColorScheme colorScheme = Theme.of(context).colorScheme;
291-
292-
return InputDecorator(
293-
decoration: const InputDecoration(),
294-
child: ConstrainedBox(
295-
constraints: const BoxConstraints(
296-
// TODO constrain this adaptively (i.e. not hard-coded 200)
297-
maxHeight: 200,
298-
),
299-
child: ComposeAutocomplete(
300-
narrow: narrow,
301-
controller: controller,
302-
focusNode: focusNode,
303-
fieldViewBuilder: (context) {
304-
return TextField(
291+
const verticalPadding = 8.0;
292+
const contentLineHeight = 22.0;
293+
final textScaler = MediaQuery.textScalerOf(context).clamp(maxScaleFactor: 1.5);
294+
295+
final designVariables = DesignVariables.of(context);
296+
297+
return ComposeAutocomplete(
298+
narrow: narrow,
299+
controller: controller,
300+
focusNode: focusNode,
301+
fieldViewBuilder: (context) => ConstrainedBox(
302+
constraints: BoxConstraints(
303+
// Reserve space to fully show the first 7th lines and just partially
304+
// clip the 8th line, where the height matches the spec at
305+
// https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=3960-5147&node-type=text&m=dev
306+
// > Maximum size of the compose box is suggested to be 178px. Which
307+
// > has 7 fully visible lines of text
308+
//
309+
// The partial line hints that the content input is scrollable.
310+
maxHeight: verticalPadding + textScaler.scale(
311+
contentLineHeight * 7 + contentLineHeight * 0.727)),
312+
child: ClipRect(
313+
child: InsetShadowBox(
314+
top: verticalPadding, bottom: verticalPadding,
315+
color: designVariables.composeBoxBg,
316+
child: TextField(
305317
controller: controller,
306318
focusNode: focusNode,
307-
style: TextStyle(color: colorScheme.onSurface),
308-
decoration: InputDecoration.collapsed(hintText: hintText),
319+
// Let the content show through the `contentPadding` so that
320+
// our [InsetShadowBox] can fade it smoothly there.
321+
clipBehavior: Clip.none,
322+
style: TextStyle(
323+
fontSize: 17,
324+
height: (contentLineHeight / 17),
325+
color: designVariables.textInput),
326+
// From the spec at
327+
// https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=3960-5147&node-type=text&m=dev
328+
// > Compose box has the height to fit 2 lines. This is [done] to
329+
// > have a bigger hit area for the user to start the input. […]
330+
minLines: 2,
309331
maxLines: null,
310332
textCapitalization: TextCapitalization.sentences,
311-
);
312-
}),
313-
));
333+
decoration: InputDecoration(
334+
// This padding ensures that the user can always scroll long
335+
// content entirely out of the top or bottom shadow if desired.
336+
// With this and the `minLines: 2` above, an empty content input
337+
// gets 60px vertical distance between the top of the top shadow
338+
// and the bottom of the bottom shadow (with no text-size
339+
// scaling). That's a bit more than the 54px given in the Figma,
340+
// and we can revisit if needed, but it's tricky to get that
341+
// 54px distance while also making the scrolling work like this
342+
// and offering two lines of touchable area.
343+
contentPadding: const EdgeInsets.symmetric(vertical: verticalPadding),
344+
hintText: hintText,
345+
hintStyle: TextStyle(
346+
color: designVariables.textInput.withValues(alpha: 0.5))))))));
314347
}
315348
}
316349

0 commit comments

Comments
 (0)