<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Flutter 實現骨架屏

      什么是骨架屏

      在客戶端開發中,我們總是需要等待拿到服務端的響應后,再將內容呈現到頁面上,那么在用戶發起請求到客戶端成功拿到響應的這段時間內,應該在屏幕上呈現點什么好呢?

      答案是:骨架屏

      那么什么是骨架屏呢,來問下 GPT:

      骨架屏(Skeleton Screen)是一種現代的用戶界面設計技術,用于提升應用或網站在加載內容時的用戶體驗。在內容的完全加載和呈現之前,骨架屏提供了一種模糊的預覽,形似最終內容的空白版,通常用灰色的塊、線條或元素占位符表示。這種設計方法可以有效減少用戶的感知等待時間,增強用戶的交互感。

      功能和用途

      1. 提高感知性能:骨架屏通過立即顯示頁面的基本結構(而非旋轉的加載圖標或完全空白的屏幕),給用戶一種內容即將呈現的感覺,這可以使等待時間感覺上更短。

      2. 改善用戶體驗:使用骨架屏可以減少用戶在加載過程中的焦慮,用戶看到界面元素已經在逐步加載,會有更多的耐心等待最終內容的呈現。

      3. 提供內容加載的視覺提示:骨架屏體現了頁面內容加載的進度,可以讓用戶知道哪些內容即將出現,這樣用戶就不會感到突然或困惑。

      實現方式

      骨架屏的實現通常包括以下幾個步驟:

      1. 設計:設計與最終內容布局相似的基本框架,使用灰色或淺色塊代表將要加載的各種元素,如文本行、圖片、按鈕等。

      2. 前端實現:在前端代碼中,可以使用HTML和CSS來創建這些占位符。對于復雜的動態加載內容,可以使用JavaScript或前端框架如React、Vue等來動態控制骨架屏的顯示和隱藏。

      3. 數據加載后的處理:一旦相應的數據加載完成,骨架屏應被實際內容替換。這通常涉及到監聽數據加載的完成事件,然后更新UI。

      示例

      在一個簡單的網頁應用中,如果你正在加載一個包含標題、幾段文本和圖片的文章,骨架屏可能包括:

      • 一個灰色的矩形塊預留給圖片。
      • 幾個灰色的條形預留給文本標題和段落。

      隨著實際內容的逐漸加載到瀏覽器中,這些灰色占位符將被實際的圖片和文本內容替換。

      結論

      骨架屏是一種非常有效的用戶界面技術,尤其適用于網絡速度較慢或數據處理較慢的應用場景,能顯著提升用戶的等待體驗和整體滿意度。通過合理設計和實現,開發者可以利用骨架屏減少用戶流失,提升應用的專業感和友好感。

      我們要實現的效果

      上面是一個 內容展示的卡片,下面的是 加載中狀態的該卡片的骨架圖

      如何實現呢?

      1. 先定義卡片部分 ui 代碼

      一個 Column 中有三行元素,分別是 第一行: 圖片第二行: 卡片標題第三行: 頭像 昵稱 瀏覽量

      class StarCard extends StatelessWidget {
        const StarCard({super.key});
      
        @override
        Widget build(BuildContext context) {
          return Container(
            width: 180,
            clipBehavior: Clip.hardEdge,
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.circular(4),
            ),
            child: Column(
              children: [
                SizedBox(
                  width: 180,
                  child: AspectRatio(
                    aspectRatio: 9 / 11,
                    child: Image.network('https://pic1.zhimg.com/80/v2-fc35089cfe6c50f97324c98f963930c9_720w.jpg', fit: BoxFit.cover),
                  ),
                ),
                Padding(
                  padding: EdgeInsets.all(8),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        '魔法少女李知恩!!!',
                        style: TextStyle(fontSize: 15, color: Colors.black, fontWeight: FontWeight.w500),
                        maxLines: 2,
                        overflow: TextOverflow.ellipsis,
                      ),
                      SizedBox(height: 8),
                      Row(
                        children: [
                          ClipRRect(
                            borderRadius: BorderRadius.circular(10),
                            child: Image.network(
                              'https://pic1.zhimg.com/80/v2-1956eeb2c894f1785362411aa306f882_1440w.webp?source=1def8aca',
                              height: 20,
                              width: 20,
                              fit: BoxFit.cover,
                            ),
                          ),
                          SizedBox(width: 4),
                          Text('是 IU 吖', style: TextStyle(fontSize: 12, color: const Color(0xff86909C))),
                          const Spacer(),
                          Text('21 瀏覽', style: TextStyle(fontSize: 12, color: const Color(0xff86909C))),
                        ],
                      ),
                    ],
                  ),
                ),
              ],
            ),
          );
        }
      }
      
      

      2. 引入 shimmer

      pubspec.yaml 中加入

      dependencies:
        shimmer: ^3.0.0
      

      其作用是為我們的元素加上 閃動 流光 效果,類似于一道光照射到一把光滑的寶劍上,隨著寶劍角度發生變化 光的反射發生位移的現象

      我們使用它的 fromColors 構造器,先隨便放進去一個 Container 試試效果:

      Shimmer.fromColors(
        baseColor: Colors.orange,
        highlightColor: Colors.blue,
        child: Container(
      	color: Colors.white,
      	height: 180,
      	width: 180,
        ),
      )
      

      效果還不錯,就是配色有點丑

      調整下顏色,在用來包裹下我們剛剛定義的 StarCard :

      Shimmer.fromColors(
        baseColor: Colors.grey[300]!, // 骨架基色
        highlightColor: Colors.grey[100]!, // 骨架高亮色
        child: StarCard(),
      ),
      

      誒?怎么跟我們要實現的效果有點出入?

      這是因為 StarCard 的根組件是一個帶有顏色的 Container

      return Container(
        width: 180,
        clipBehavior: Clip.hardEdge,
        decoration: BoxDecoration(
      	color: Colors.white,
      	borderRadius: BorderRadius.circular(4),
        ),
        ...
      );
      

      而這樣 Shimmer 效果便會被加到整個跟組件上,child 也就看不到 Shimmer 了。那我們將根組件的 color 屬性移除試試呢:

      嗯...... 底層的元素確實展示出來了,不過我們所期望的并不是展示出文字和數據啊,況且這個時候我們還尚未拿到服務器返回給我的的數據

      3. Magic symbol

      這個時候我們就需要用到一個 魔法符號 ,不過在引入之前,先分離下組件:

      StarCard 需要一個構造函數,里面接收一個從服務端 反序列化 來的 model ;再新定義一個 StarCardSkeleton 組件,他有一個無參構造器,用作 StarCard 的骨架圖;也就是說在獲取到數據之前,我們使用一個 StarCardSkeleton 來占 StarCard 的位,獲取到數據之后使用 StarCard 來展示真實的數據,代碼如下:

      class StarCard extends StatelessWidget {
        const StarCard({super.key, required this.starModel});
      
        final StarModel starModel;
      
        @override
        Widget build(BuildContext context) {...}
      }
      
      class StarCardSkeleton extends StatelessWidget {
        const StarCardSkeleton({super.key});
      
        @override
        Widget build(BuildContext context) {...}
      }
      

      現在該回歸正題了,我們要引入的 魔法符號 就是:

      這是一個 全寬 純色 占位符,數個 連起來,配合加粗 fontWeight 可以實現我們想要的 一道長條色塊 的效果,且要比定義 SizedBox 少改動更多代碼,用它來代替Shimmer 文本再合適不過了

      我們先將 StarCard build 中的代碼全部拷貝到 StarCardSkeleton 里面,并改動 卡片標題用戶昵稱瀏覽量 Text 中的文字為自定義數量的

      class StarCardSkeleton extends StatelessWidget {
        const StarCardSkeleton({super.key});
      
        @override
        Widget build(BuildContext context) {
          return Container(
            width: 180,
            clipBehavior: Clip.hardEdge,
            decoration: BoxDecoration(
              // color: Colors.white,
              borderRadius: BorderRadius.circular(4),
            ),
            child: Column(
              children: [
                SizedBox(
                  width: 180,
                  child: AspectRatio(
                    aspectRatio: 9 / 11,
                    child: Container(color: Colors.white),
                  ),
                ),
                Padding(
                  padding: EdgeInsets.all(8),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        '▆▆▆▆▆▆',
                        style: TextStyle(fontSize: 15, fontWeight: FontWeight.w900),
                        maxLines: 2,
                        overflow: TextOverflow.ellipsis,
                      ),
                      SizedBox(height: 8),
                      Row(
                        children: [
                          Container(
                            width: 20,
                            height: 20,
                            decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.white),
                          ),
                          SizedBox(width: 4),
                          // NickNameText(articleData.nickName, views: articleData.playTimes),
                          Text('▆▆▆', style: TextStyle(fontSize: 13, fontWeight: FontWeight.w900)),
                          const Spacer(),
                          Text('▆▆', style: TextStyle(fontSize: 12, fontWeight: FontWeight.w900)),
                        ],
                      ),
                    ],
                  ),
                ),
              ],
            ),
          );
        }
      }
      

      如果想進一步優化渲染性能,可以把 Image 換成帶背景色的 Container

      再看下效果呢

      完美!簡直一模一樣!

      抽離出 Simmer 組件

      為了方便組件復用,可以將 Shimmer 封裝出來

      /// 骨架屏閃爍
      class BaseShimmer extends StatelessWidget {
        const BaseShimmer({super.key, required this.child});
      
        final Widget child;
      
        @override
        Widget build(BuildContext context) {
          return Shimmer.fromColors(
            baseColor: Colors.grey[300]!, // 骨架基色
            highlightColor: Colors.grey[100]!, // 骨架高亮色
            child: child,
          );
        }
      }
      

      再次用到可以直接:BaseShimmer(child: StarCardSkeleton())

      拓展

      這章的標題是 骨架屏 ,為什么從頭到尾一直再講怎么生成一個骨架圖呢?

      先別急罵標題黨!

      所謂的 骨架屏 不就是一張一張的骨架圖拼出一個屏幕,不就是一個骨架屏 嘛

      BaseShimmer(
      	child: MasonryGridView.count(
      		padding: EdgeInsets.only(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();
      		}),
      	)
      

      這里用了 flutter_staggered_grid_view 包,這個包還可以做出卡片高度不一的瀑布流布局效果

      注:使用 BaseShimmer 包裹整個 GridViewListView 比包裹單個的 Card 效果要更好喲~

      風險

      經過測試發現 在不同的設備上、或者使用了自定義字體,▆▆▆ 之間會出現微小間距,無論將 fontWeight 設置為多大都無法避免,這時只能將 方案換為帶顏色的 Container 來解決。不過 SkeletonScreen 在頁面停留的時間通常不會太長,這點就要看團隊內部的取舍了

      posted @ 2024-09-30 17:12  HuStoking  閱讀(1041)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产黄色一区二区三区四区| 亚洲人妻精品中文字幕| 国产黄色三级三级看三级| 精品国产成人国产在线观看| 九九热在线免费观看视频| 91久久精品美女高潮不断| 久久99精品久久久大学生| 久久99国产乱子伦精品免费| 91蜜臀国产自产在线观看| 国产美女高潮流白浆视频| 92精品国产自产在线观看481页| 国产毛片精品av一区二区| 国产日韩一区二区天美麻豆| 美女一区二区三区亚洲麻豆| 人妻少妇久久中文字幕| 无遮挡aaaaa大片免费看| 国产一区二区三区精美视频| 亚洲高清免费在线观看| 中文字幕无码免费不卡视频| 精品人妻日韩中文字幕| 欧美性猛交xxxx乱大交极品| 97视频精品全国免费观看| 北岛玲中文字幕人妻系列| 最新国产精品拍自在线观看| 91麻豆精品国产91久| 中文字幕热久久久久久久| 亚洲av中文久久精品国内| 中文字幕日韩有码av| VA在线看国产免费| 91热在线精品国产一区| 日韩秘 无码一区二区三区| 国产成人免费ā片在线观看 | 国产不卡免费一区二区| 亚洲人成网站在线播放2019| 国产99re热这里只有精品| 亚洲色大成网站WWW永久麻豆| 久久99国产一区二区三区| 无码人妻丰满熟妇奶水区码| 亚洲熟女国产熟女二区三区| 日韩av综合免费在线| 色婷婷日日躁夜夜躁|