I'm trying to replicate the Eclipse Google Home Screensaver in Jetpack Compose.
However I'm completely stuck at how I can achieve the clipping effect as seen on the example.
Currently I tried following using a Row and some weighted Text composables.
#Composable
fun Eclipse() {
Row(
modifier = Modifier
.height(IntrinsicSize.Min)
.width(IntrinsicSize.Min)
.background(Color.Black)
) {
EclipseIntTile(0, modifier = Modifier.weight(1F))
EclipseIntTile(4, modifier = Modifier.weight(1F))
EclipseIntTile(5, modifier = Modifier.weight(1F))
EclipseIntTile(2, modifier = Modifier.weight(1F))
}
}
#Composable
fun EclipseIntTile(i: Int, modifier: Modifier = Modifier) {
Text("$i", modifier = modifier, style = TextStyle(fontSize = 140.sp, color = Color.White))
}
Which yields following
How can I configure the Row to stop wrapping the Text's?
Is it possible to do this without hard coding a size on the Row?
A decent solution would be to use a BoxWithConstraints which exposes a size value. Now, you can place your snippet inside this Box, then use the size value to set the widths of the Texts, dividing the space equally among the children. Then, you can use the same size value to set the font size as well. Now you see this is not really hard-coding since the sizes will adjust to various screen dimensions. So, this could be one workaround.
On a different version, you could use a Layout Composable, which exposes constraints. Layout is the official Compose way of building custom widgets like this, so... Best of luck
Related
please see the image below for two examples of what is to be achived
the alignment should be on the Center Y of the first lines of each UILabels and should work regardless of font size or font. currently we have implemented this with different constraints to the top of the super view for different font and font size combinations.
the constraint to align the center of the two UILabels does not work since the text of the second UILabel is not fixed and can have several lines.
also the text is dynamic, so it is not known where the text will wrap to create the first line, thus it cannot be shown in an one line UILabel with the rest of the text in another one below.
currently this is implemented using UIKit, but if there is an easy solution in SwiftUI we can put these two labels in a SwiftUI component. so a SwiftUI solution would also be welcomed.
Your comments said "it should be on the glyphs" ... but, without additional information, my guess is that "real world" usage would not really need that level of precision.
For example:
While the glyphs are not perfectly center-Y aligned, it seems unlikely you'd run into a case where the first line of the "rightLabel" is " ' " ' " or . , . , ..
This layout can be easily done with only a few constraints - no need to do any calculations:
The "Positioning" label would, of course, be set .hidden = true so it would never be seen.
If you really, really want glyph-precision, you'll need to calculate
the Glyph bounding box for the left-label
the Glyph bounding box for first line of the right-label
calculate the "character box" offsets to align the Glyph Y-centers
and then position the two labels accordingly, or use Core Text to draw the text (instead of using UILabel).
Probably more work than necessary -- unless your actual use-case demands it.
That's an interesting problem! You can try using the centerYAnchor for the label on the left, and the firstBaselineAnchor for the label on the right... that will align the center Y with the text baseline, which isn't quite what you want.
To find the correct offset to apply, you can use the information from UIFont about the size of the characters. I'd probably start with capHeight * 0.5 and see if that looks or feels right. Something like:
leftLabel.centerYAnchor.constraint(equalTo: rightLabel.firstBaseLineAnchor, constant: rightFont.capHeight * 0.5)
This is a more difficult problem in SwiftUI, I think, because resolved font metrics aren't directly available to you.
In CSS there is padding and margin, the former adds empty spaces inside border, and the latter adds spaces outside. Currently I am learning SwiftUI, and I found the .padding modifier, which is equivalent to CSS's padding property, but cannot find margin's correspondence.
Does it exist or am I to create a wrapper view to achieve this goal?
SwiftUI's layout modifiers can be a bit confusing if you're coming from HTML and CSS.
Where CSS has a huge list of properties to choose from, SwiftUI tries to keep things simpler. To do that, it takes advantage of the ability to "chain" modifiers together.
For example, if you had a rectangle that needs internal padding of 8 pixels, a border of 1 pixel width and external margins of 10 pixels, you might express it in CSS like this:
.my-rectangle {
padding: 8px;
margin: 10px;
border: 1px solid red;
}
And in CSS it doesn't matter what order those properties occur within the double braces; each has a specific meaning and is applied in the same way, even if the border property is at the end or the beginning.
In SwiftUI, though, styling is done by applying a modifier to a View, giving you a new view with the modification applied. You can then apply another modifier to that, and another, and so on. And because each modifier is adapting what has gone before, the order is very important.
So if we have a view to which we need to add the same spacing as the example above, we'd structure it something like:
MyView()
.padding(8) // <-- internal padding
.border(Color.red, width: 1) <<-- apply the border
.padding(10) // <-- apply more padding outside the border
Building views up gradually like this is what allows SwiftUI to keep the number of modifiers you need for most views reasonably small and focussed.
As you start building up more complicated views, you'll come across stacks and grids, which also have a concept of spacing between children. Depending on your design, it might be more useful to think about using spacing instead of external padding, especially if you start breaking your design up into reusable components.
But thinking about applying changes in a sequence, instead of all at once like CSS, is the key to constructing great SwiftUI views.
I have a range slider in my compose app that stretches edge to edge horizontally but accessing the thumbs near the edges triggers the system back navigation gesture.
How can I disable the system back gesture only for the range slider's thumb region on the edges?
I could find solutions like below when using view but none for compose.
https://developer.android.com/develop/ui/views/touch-and-input/gestures/gesturenav#conflicting-gestures
https://medium.com/androiddevelopers/gesture-navigation-handling-gesture-conflicts-8ee9c2665c69
Is this not available for Jetpack Compose yet? Or am I missing something?
You can use Modifier.systemGestureExclusion() to exclude a layout's rectangle from system gestures. Docs
There is also a version that will pass you in the measured rect of the parent layout. You could do something like
Box(
modifier = Modifier.systemGestureExclusion { layoutCoords ->
Rect(x = 0, y = 0, width = 10.dp, height = layoutCoords.size.height)
}
) { ... }
to exclude the left 10pts of a Box.
I want to create an app, that shows sometimes shorter, somertimes longer text in a Container. This container should not change in size, after the widget is build to fit with the size of the screen. The text itself comes from a database and I don't know, how long it can be.
When I print the text in the container, sometimes it fits, sometimes there is many space above and below, when the text is short, or sometime the text overflows and is not visible.
I want, that the text size is automatically changed to fit into the container. Container with overflow
The flutter package Auto-Size-Text is not working for me, because I only could resize the text to fit the width or set the maximum line, which I don't know either.
This is my code simplified:
AutoSizeText autotext = AutoSizeText(text, style: style, textAlign: TextAlign.center, presetFontSizes: [25.0,22.0,20.0,18.0], semanticsLabel: text);
Container(
child: autotext
);
TL;DR:
Your AutoSizeText widget is probably provided with unbounded constraints.
This can be the case if it's wrapped in a Column, Row, ListView or something similar.
To counter this issue, wrap it in an Expanded widget.
In order to answer that question, we need to understand how Flutter performs the layout.
Basically, the top widget's height and width (called its "dimensions") are requested by the framework, which provides a min and max width and height (called "constraints").
In order to calculate its dimensions, the widgets ask their children what dimensions they want to have based on some constraints.
So, every widget gets told constraints and returns concrete dimensions within these constraints.
Widgets like ListView or Column don't want to influence their children's dimensions too much - they should simply be as small as possible.
In order to communicate that, they are given a maximum width/height of double.infinity, telling them that they should default to their "natural"/default dimensions. A constraint of this type is called "unbounded".
Here's a Google Tech Talk video about Flutter's rendering pipeline, where all of this is described in greater detail.
With this process in mind, let's have a look at how your widgets perform during layout:
In a comment to your post, you said there's a Column widget, so I assume there's a Column containing a Container, which in turn contains the AutoSizeText widget.
If instead of a Column, there's a Row or ListView or something similar, the process is basically the same.
The Column wants its children to be as small as possible, so it passes unbounded constraints to all of its children.
Being one of those children, the Container receives those unbounded constraints.
Because the Container itself is just a wrapper class, it simply passes these constraints on to its child, the AutoSizeText.
The AutoSizeText receives unbounded constraints.
Looking at its source code, we can see that it starts with a default text size and checks if it fits (calling _checkTextFits).
If it doesn't fit, the text size is decreased and this process is repeated until the text is small enough to fit into the given constraints.
Because in your case, the constraints are unbound, the text fits on the first try - that means the widget simply uses the default size.
Having determined the text size, it can calculate the text's dimensions and returns them up the tree.
The Container receives those dimensions.
Because it's just a wrapper class, it simply returns them to its parent in turn.
Finally, the Column receives the dimensions and sizes itself around that (probably adding the dimensions of other children).
Solution
You don't want to pass unbounded constraints to an AutoSizeText widget, because that defeats the purpose of the widget in the first place - fitting text into bounded constraints.
You can confirm this is the problem by hardcoding the Container's width and height, that should make the AutoSizeText widget adjust the text size properly.
If the Container is placed inside a Column or something similar, you can always bound the constraints by wrapping it in an Expanded.
Why the solution works
The Column checks which of its children are Expanded widgets.
It first lays out all the other widgets.
Only then, it divides the remaining space among the Expanded widgets according to their flex factors (that's a whole other topic for itself).
That means the Expandeds' children will receive bounded constraints.
If you wrap your Container in an Expanded and it's the only expanded in the Column, it should take all the remaining space, and fit the text perfectly within itself.
I want to section off one area of a layout from another visually in my Vaadin Flow layout using the Java API.
I want something like the hr horizontal rule found in HTML. I would also want the equivalent, a vertical rule (which was never defined in HTML).
Is there some easy way to have a visual indicator of a thematic shift between parts of a layout?
Hr class
For an <hr> there is the Hr class.
verticalLayout.add(new Span("First"), new Hr(), new Span("Second"));
Roll-your-own
Another option is to create classes for the dividers, there are a few different ways of doing this, here's an example
public class Divider extends Span {
public Divider() {
getStyle().set("background-color", "blue");
getStyle().set("flex", "0 0 2px");
getStyle().set("align-self", "stretch");
}
}
And used as such
horizontalLayout.add(new Span("First"), new Divider(), new Span("Second"));
Using align-self and flex will only work in flex layouts, which includes HorizontalLayout and VerticalLayout. The beauty of this approach is that the same class will work in both. The flex: 0 0 2px tells it to be 2 pixels wide in the direction of the container, and not grow or shrink. The align-self: stretch will tell it to take the full size of the container in the perpendicular direction.
I write this answer as follow-up to my comment on Tazavoo's answer, which is great! I love their custom Divider class, and it has been asked whether this divider can be customized/styled further, something like it is done in this gradient borders page.
Of course this divider can be styled further! But the difference between the divider and the elements in the link is that in the link, the borders of an element is styled, while we need to style the actual element itself here.
CSS attribute in the linked page: border-image. CSS attribute for the Divider background-image.
(I am not familiar enough with CSS -webkit attrributes, so I don't know if you need more than just background-image for a good visualisation in all browsers)
The linked page makes the linear-gradient go in the direction to bottom. We could use that too, but then using the Divider horizontally would look different than using it vertically. That is why we need to set the direction to a diagonal, so both usages of the divider have a similar gradient. See proof of concept in w3schools' TryIt Editor
Here is how I set up the Divider class with a gradient:
public class Divider extends Span {
public Divider(){
getStyle().set("background-image", "linear-gradient(135deg, #777 , rgba(0, 0, 0, 0))");
getStyle().set("flex", "0 0 2px");
getStyle().set("align-self", "stretch");
}
}
To customize the linear gradient even more, please see the docs on w3schools
All the credits of the divider class go to #Tazavoo. Please go upvote their answer