What is Skeleton Screen
In client-side development, we always need to wait to get the server-side response, and then the content will be presented to the page, then the user initiated the request to the client to successfully get the response of the time, should be presented on the screen what is good?
The answer is:skeleton screen
So what is a skeleton screen, come and ask GPT:
Skeleton Screen is a modern user interface design technique used to enhance the user experience of an application or website when loading content. Before the content is fully loaded and rendered, the Skeleton Screen provides a blurry preview that resembles a blank version of the final content, often represented by gray blocks, lines, or element placeholders. This design approach can be effective in reducing the perceived wait time and enhancing the user's sense of interaction.
Functions and uses
Improved perceptual performance: Skeleton screens give users a sense that content is about to be presented by immediately displaying the basic structure of the page (rather than a rotating loading icon or a completely blank screen), which can make wait times feel shorter.
Improving the user experience: Using a skeleton screen reduces user anxiety during the loading process. Users will have more patience to wait for the final content to be presented when they see that the interface elements are already loading gradually.
Provide visual cues for content loading: The skeleton screen reflects the progress of the page content loading and lets the user know what is coming up so the user doesn't feel abrupt or confused.
implementation method
The realization of a skeleton screen usually consists of the following steps:
devise: Design a basic framework similar to the final content layout, using gray or light color blocks to represent the various elements that will be loaded, such as lines of text, images, buttons, and so on.
front-end implementation: In front-end code, these placeholders can be created using HTML and CSS. For complex dynamically loaded content, JavaScript or front-end frameworks such as React, Vue, etc. can be used to dynamically control the display and hiding of the skeleton screen.
Processing of data after loading: The skeleton screen should be replaced by the actual content once the corresponding data loading is complete. This usually involves listening for a data loading completion event and then updating the UI.
typical example
In a simple web application, if you're loading an article with a headline, a couple paragraphs of text, and an image, the skeleton screen might include:
- A gray rectangular block reserved for images.
- Several gray bars are reserved for text headings and paragraphs.
As the actual content is gradually loaded into the browser, these gray placeholders will be replaced by the actual image and text content.
reach a verdict
Skeleton screen is a very effective user interface technology, especially suitable for application scenarios with slower network speed or slower data processing, which can significantly improve users' waiting experience and overall satisfaction. Through reasonable design and implementation, developers can utilize skeleton screens to reduce user churn and enhance the professional and friendly feel of the application.
The effect we are trying to achieve
Above is a card displaying content, and below is a skeleton of the card in its loaded state.
How can this be achieved?
1. Define the card part of the ui code
anColumn
There are three rows of elements in theFirst row: Picture 、Line 2: Card Title 、Row 3: Avatar Nickname Views
class StarCard extends StatelessWidget {
const StarCard({});
@override
Widget build(BuildContext context) {
return Container(
width: 180,
clipBehavior: ,
decoration: BoxDecoration(
color: ,
borderRadius: (4),
),
child: Column(
children: [
SizedBox(
width: 180,
child: AspectRatio(
aspectRatio: 9 / 11,
child: ('/80/v2-fc35089cfe6c50f97324c98f963930c9_720w.jpg', fit: ),
),
),
Padding(
padding: (8),
child: Column(
crossAxisAlignment: ,
children: [
Text(
'Magic Girl Lee Ji-eun!!!',
style: TextStyle(fontSize: 15, color: , fontWeight: FontWeight.w500),
maxLines: 2,
overflow: ,
),
SizedBox(height: 8),
Row(
children: [
ClipRRect(
borderRadius: (10),
child: (
'/80/v2-1956eeb2c894f1785362411aa306f882_1440w.webp?source=1def8aca',
height: 20,
width: 20,
fit: ,
),
),
SizedBox(width: 4),
Text('be IU (antiseptic and disinfectant)', style: TextStyle(fontSize: 12, color: const Color(0xff86909C))),
const Spacer(),
Text('21 skim over', style: TextStyle(fontSize: 12, color: const Color(0xff86909C))),
],
),
],
),
),
],
),
);
}
}
2. Introductionshimmer
contract (to or for)
exist add in
dependencies:
shimmer: ^3.0.0
- The documentation for the package is here./packages/shimmer
Its function is to add a flickering streaming effect to our elements, similar to the phenomenon of a ray of light hitting a smooth sword, and as the angle of the sword changes, the reflection of the light is shifted.
We use itsfromColors
Constructors, put in a random one firstContainer
Try it on.
(
baseColor: ,
highlightColor: ,
child: Container(
color: ,
height: 180,
width: 180,
),
)
The result is not bad, but the color scheme is a bit ugly
Adjust the color, before using it to wrap around theStarCard
:
(
baseColor: [300]! , // skeleton base color
highlightColor: [100]! , // skeleton highlight color
child: StarCard(), ), // skeleton highlightColor: [100]!
), // Skeleton baseColor: [300]!
Eh? How come it's a bit different from what we're trying to achieve?
This is becauseStarCard
The root component is a color-codedContainer
return Container(
width: 180,
clipBehavior: ,
decoration: BoxDecoration(
color: ,
borderRadius: (4),
),
...
);
rather like thisShimmer The effect is then added to the entire heel assembly.child
And you can't see it.Shimmer up. Then we'll set the root component'scolor
What about property removal:
Hmmm ...... The underlying elements do show up, but we're not expecting text or data, and we haven't gotten the data back from the server yet.
3. Magic symbol
At this point we need to use amagic symbol , but separate the components before introducing them:
StarCard
A constructor is needed that takes a deserializedmodel ; then a new definition of aStarCardSkeleton
component, who has a parameterless constructor that is used as theStarCard
of the skeleton map; that is, before getting to the data, we use aStarCardSkeleton
(literary) occupy (a position)StarCard
bit, after getting the data use theStarCard
to show the real data, the code is as follows:
class StarCard extends StatelessWidget {
const StarCard({, required });
final StarModel starModel;
@override
Widget build(BuildContext context) {...}
}
class StarCardSkeleton extends StatelessWidget {
const StarCardSkeleton({});
@override
Widget build(BuildContext context) {...}
}
Now it's time to get back to business, and we're going to introduce themagic symbol That is:
▆
It's aFull width solid color Placeholders, several▆
Connect the dots to match the boldingfontWeight
can realize what we wantstrip of color effect, and to be more effective than definingSizedBox
Change more code less and use it instead ofShimmer The text couldn't be more appropriate.
We'll start with theStarCard
The code in the build is copied to theStarCardSkeleton
Inside and changeCard Title 、user nickname 、volume of website traffic Text
The text in is a customized amount of▆
class StarCardSkeleton extends StatelessWidget {
const StarCardSkeleton({});
@override
Widget build(BuildContext context) {
return Container(
width: 180,
clipBehavior: ,
decoration: BoxDecoration(
// color: ,
borderRadius: (4),
),
child: Column(
children: [
SizedBox(
width: 180,
child: AspectRatio(
aspectRatio: 9 / 11,
child: Container(color: ),
),
),
Padding(
padding: (8),
child: Column(
crossAxisAlignment: ,
children: [
Text(
'▆▆▆▆▆▆',
style: TextStyle(fontSize: 15, fontWeight: FontWeight.w900),
maxLines: 2,
overflow: ,
),
SizedBox(height: 8),
Row(
children: [
Container(
width: 20,
height: 20,
decoration: BoxDecoration(shape: , color: ),
),
SizedBox(width: 4),
// NickNameText(, views: ),
Text('▆▆▆', style: TextStyle(fontSize: 13, fontWeight: FontWeight.w900)),
const Spacer(),
Text('▆▆', style: TextStyle(fontSize: 12, fontWeight: FontWeight.w900)),
],
),
],
),
),
],
),
);
}
}
If you want to further optimize the rendering performance, you can set theImage
Change to one with a background colorContainer
Let's see how it looks.
Perfect! It's exactly the same!
separate outSimmer subassemblies
To facilitate component reuse, theShimmer encapsulate
/// Skeleton screen flickers
class BaseShimmer extends StatelessWidget {
const BaseShimmer({, required });
final Widget child;
@override
Widget build(BuildContext context) {
return (
baseColor: [300]!, // Skeleton base color
highlightColor: [100]!, // Skeleton High Gloss Color
child: child,
);
}
}
Used again can be direct:BaseShimmer(child: StarCardSkeleton())
expansion
The title of the chapter isskeleton screen Why do you keep going on and on about how to generate a skeleton diagram from start to finish?
Don't rush to call out the title party just yet!
so-calledskeleton screen It's not just a skeleton diagram that spells out a screen, it's just a skeleton screen.
BaseShimmer(
child: (
padding: (bottom: 10, left: 12, right: 12, top: 8),
physics: const NeverScrollableScrollPhysics(),
itemCount: 9,
mainAxisSpacing: 5,
crossAxisSpacing: 5,
crossAxisCount: 2,
itemBuilder: (BuildContext context, int index) {
return StarCardSkeleton();
}),
)
It's used here.flutter_staggered_grid_view
package, this package can also make the card height of the waterfall flow layout effects
Note: UseBaseShimmer wrap up the whole
GridView
maybeListView
than wrapping a singleCard
It's better.
exposures
Tests have shown that on different devices, or with customized fonts.▆▆▆
There will be a small spacing between whatever will befontWeight
Setting it to multiple sizes is unavoidable, and this can only be avoided by setting the▆
Scheme change to colorfulContainer
to fix it. HoweverSkeletonScreen
The time spent on the page is usually not very long, and this depends on the trade-offs within the team