Location>code7788 >text

Flutter implementation of the skeleton screen

Popularity:598 ℃/2024-09-30 17:46:38

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

  1. 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.

  2. 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.

  3. 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:

  1. 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.

  2. 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.

  3. 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: PictureLine 2: Card TitleRow 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 Titleuser nicknamevolume 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 wholeGridView 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