Skip to content

Commit 45f14e0

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 fdefca4 commit 45f14e0

File tree

1 file changed

+38
-18
lines changed

1 file changed

+38
-18
lines changed

lib/widgets/compose_box.dart

+38-18
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import '../model/store.dart';
1616
import 'autocomplete.dart';
1717
import 'dialog.dart';
1818
import 'icons.dart';
19+
import 'inset_shadow.dart';
1920
import 'store.dart';
2021
import 'text.dart';
2122
import 'theme.dart';
@@ -286,30 +287,49 @@ class _ContentInput extends StatelessWidget {
286287

287288
@override
288289
Widget build(BuildContext context) {
289-
ColorScheme colorScheme = Theme.of(context).colorScheme;
290+
const verticalPadding = 8.0;
291+
const contentLineHeight = 22.0;
290292

291-
return InputDecorator(
292-
decoration: const InputDecoration(),
293-
child: ConstrainedBox(
293+
final designVariables = DesignVariables.of(context);
294+
295+
return ComposeAutocomplete(
296+
narrow: narrow,
297+
controller: controller,
298+
focusNode: focusNode,
299+
fieldViewBuilder: (context) => ConstrainedBox(
294300
constraints: const BoxConstraints(
295-
// TODO constrain this adaptively (i.e. not hard-coded 200)
296-
maxHeight: 200,
297-
),
298-
child: ComposeAutocomplete(
299-
narrow: narrow,
300-
controller: controller,
301-
focusNode: focusNode,
302-
fieldViewBuilder: (context) {
303-
return TextField(
301+
// Reserve space to fully show the first 7th lines and just partially
302+
// clip the 8th line, where the height matches the spec of 178 logical
303+
// pixels. The partial line hints that the content input is
304+
// scrollable. We do not expect this to work the same way with all
305+
// font sizes.
306+
maxHeight: verticalPadding + contentLineHeight * 7 + contentLineHeight * 0.727),
307+
child: ClipRect(
308+
child: InsetShadowBox(
309+
top: verticalPadding, bottom: verticalPadding,
310+
color: designVariables.composeBoxBg,
311+
child: TextField(
304312
controller: controller,
305313
focusNode: focusNode,
306-
style: TextStyle(color: colorScheme.onSurface),
307-
decoration: InputDecoration.collapsed(hintText: hintText),
314+
// Let the content show through the `contentPadding` so that
315+
// our [InsetShadowBox] can fade it smoothly there.
316+
clipBehavior: Clip.none,
317+
style: TextStyle(
318+
fontSize: 17,
319+
height: (contentLineHeight / 17),
320+
color: designVariables.textInput),
321+
// The [TextField] will be a bit taller than the spec in height,
322+
// because the bottom [contentPadding] is necessary for it to be
323+
// fully scrolled down, so that the content passes the bottom
324+
// shadow.
325+
minLines: 2,
308326
maxLines: null,
309327
textCapitalization: TextCapitalization.sentences,
310-
);
311-
}),
312-
));
328+
decoration: InputDecoration(
329+
contentPadding: const EdgeInsets.symmetric(vertical: verticalPadding),
330+
hintText: hintText,
331+
hintStyle: TextStyle(
332+
color: designVariables.textInput.withValues(alpha: 0.5))))))));
313333
}
314334
}
315335

0 commit comments

Comments
 (0)