成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

在 Flutter 中實(shí)現(xiàn)最佳 UX 性能的 12 個(gè)圖像技巧和最佳實(shí)踐

開發(fā) 前端
僅僅展示圖片是不夠的!您還應(yīng)該為用戶提供他們需要的最佳體驗(yàn)!因此,我強(qiáng)烈建議您創(chuàng)建自己的自定義圖片 widget,將它們隨意組合并自由使用!

Image widget 是 Flutter 中最常用的 widget 之一,但我相信我們沒有充分利用它的功能,僅僅顯示一個(gè)圖片是不夠的,你還應(yīng)該給用戶他們需要的最佳體驗(yàn)!

在這篇文章中,我將談?wù)撘恍﹫D像技巧和最佳實(shí)踐,以獲得更好的性能和用戶體驗(yàn)。

這些技巧是:

  • 1.使用 WebP 而不是 JPG/PNG
  • 2.設(shè)置寬度和高度以保留 UI 空間
  • 3.降低圖片的顯示分辨率以減少內(nèi)存使用
  • 4.預(yù)加載/預(yù)緩存您的圖像,以便即時(shí)加載圖像
  • 5.加載時(shí)顯示進(jìn)度指示器
  • 6.加載時(shí)顯示進(jìn)度百分比指示器
  • 7.加載時(shí)顯示閃爍效果,以提高用戶體驗(yàn)
  • 8.顯示 blurhash 作為占位符
  • 9.使用漸變效果來提高用戶體驗(yàn)
  • 10.緩存圖像以減少網(wǎng)絡(luò)使用并提高性能
  • 11.注意非經(jīng)常性成本
  • 12.在失敗時(shí)顯示重試按鈕
  • 結(jié)語

1.使用 WebP 而不是 JPG/PNG

WebP 是下一代圖像格式,它比 PNG 和 JPEG 小約 25%,并且比其他格式快。

這意味著,你的應(yīng)用程序?qū)⑹褂酶俚膬?nèi)存,構(gòu)建速度更快。

這里有一些基準(zhǔn):

圖片圖片

圖片圖片

Image.asset(
  // 'image.jpg',
  'image.webp', // PREFER
);

2.設(shè)置寬度和高度以保留 UI 空間

它可以防止應(yīng)用程序出現(xiàn)布局偏移

圖片圖片

之前——之后

Image.network(
  imageUrl,
  width: 200,
  height: 150,
);

3.降低圖片的顯示分辨率以減少內(nèi)存使用

圖片圖片

圖片圖片

你的圖片可能會(huì)導(dǎo)致設(shè)備內(nèi)存膨脹,這是因?yàn)椋M管它們?cè)?UI 中占據(jù)相對(duì)較小的一部分,F(xiàn)lutter 還是會(huì)以全分辨率渲染它們,從而消耗大量?jī)?nèi)存。

為了避免這種問題,可以使用 cacheWidth 或 cacheHeight 參數(shù)對(duì)指定大小的圖像進(jìn)行解碼。

此外,我們可以使用 Flutter 開發(fā)者工具輕松檢測(cè)超大圖像。

如果圖像過大,它會(huì)反轉(zhuǎn)圖像,使其顛倒。

注意!緩存大小不應(yīng)該小于小部件的大小,否則,由于分辨率低,它看起來像素化!

Image.network(
  imageUrl,
  cacheWidth: 100,
  cacheHeight: 150,
);

4.預(yù)加載/預(yù)緩存您的圖像,以便即時(shí)加載圖像

如果你在顯示圖像之前緩存它們,F(xiàn)lutter 將跳過構(gòu)建的處理步驟并立即顯示它們。

圖片圖片

class MyImage extends StatefulWidget {
  const MyImage({super.key});

  @override
  State createState() => _MyImageState();
}

class _MyImageState extends State {
  late final Image myImage;

  @override
  void initState() {
    super.initState();
    myImage = Image.asset('path');
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    precacheImage(myImage.image, context);
  }

  @override
  Widget build(BuildContext context) {
    return myImage;
  }
}

5.加載時(shí)顯示進(jìn)度指示器

突然彈出圖像不是預(yù)期的行為,用戶可能會(huì)因?yàn)榫W(wǎng)絡(luò)連接不足而錯(cuò)過圖像并向下滾動(dòng),或者可能在屏幕上看到一些空白,等等。我們應(yīng)該始終通知用戶圖像正在加載。

圖片圖片

return Image.network(
  imageUrl,
  loadingBuilder: (_, child, event) {
    if (event == null) return child;
    return const Center(child: CircularProgressIndicator());
  },
);

6.加載時(shí)顯示進(jìn)度百分比指示器

我們也可以顯示進(jìn)度百分比,而不是無限加載,這樣對(duì)用戶來說更有用。

圖片圖片

return Image.network(
  imageUrl,
  loadingBuilder: (_, child, event) {
    if (event == null) return child;
    return Center(
      child: CircularProgressIndicator(
        value: event.cumulativeBytesLoaded / (event.expectedTotalBytes ?? 0),
      ),
    );
  },
);

7.加載時(shí)顯示閃爍效果,以提高用戶體驗(yàn)

顯示進(jìn)度條是好的,但并不是最好的選擇,顯示閃爍效果(Shimmer)要比顯示進(jìn)度條好得多。

圖片圖片

return Image.network(
  imageUrl,
  height: 200,
  width: 350,
  loadingBuilder: (_, child, event) {
    if (event == null) return child;
    return const Shimmer(
      height: 200,
      width: 350,
     );
  }
);

// Most Basic Shimmer
class Shimmer extends StatefulWidget {
  const Shimmer({
    super.key,
    this.width,
    this.height,
    this.minOpacity = 0.015,
    this.maxOpacity = 0.15,
    this.borderRadius = const BorderRadius.all(Radius.circular(4)),
    this.child,
  });

  final double? width;
  final double? height;
  final double minOpacity;
  final double maxOpacity;
  final BorderRadius? borderRadius;
  final Widget? child;

  @override
  State createState() => _ShimmerState();
}

class _ShimmerState extends State with SingleTickerProviderStateMixin {
  late final AnimationController controller;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 1),
      lowerBound: widget.minOpacity,
      upperBound: widget.maxOpacity,
    )..repeat(reverse: true);
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return RepaintBoundary(
      child: FadeTransition(
        opacity: controller,
        child: Container(
          width: widget.width,
          height: widget.height,
          decoration: BoxDecoration(
            color: Colors.black,
            borderRadius: widget.borderRadius,
          ),
          child: widget.child,
        ),
      ),
    );
  }
}

我創(chuàng)建了一個(gè)簡(jiǎn)單的 shimmer 小部件,但你可以從官方文檔中學(xué)習(xí)如何創(chuàng)建高級(jí)版本的 shimmer 效果。

?

官方文檔:創(chuàng)建 shimmer 加載效果(https://docs.flutter.dev/cookbook/effects/shimmer-loading)

8.顯示 blurhash 作為占位符

為了改善用戶體驗(yàn),可以使用哈希代碼顯示圖像的模糊版本,而不是在圖像加載時(shí)顯示空白的灰色區(qū)域。

圖片圖片

https://blurha.sh/

return const SizedBox(
  width: 350,
  height: 200,
  child: BlurHash(
    hash: hashCode,
    imageFit: BoxFit.cover,
    image: imageUrl,
  ),
);

9.使用漸變效果來提高用戶體驗(yàn)

默認(rèn)情況下,圖片加載后立即顯示,這對(duì)我們的視覺體驗(yàn)來說非常糟糕,為了改善這一點(diǎn),我們可以用一個(gè)小的漸入動(dòng)畫來顯示它們。

我們可以使用 FadeInImage 來實(shí)現(xiàn)這個(gè)功能。

它需要字節(jié)或資源作為占位符,在這個(gè)例子中,我將使用 transparent_image 包來獲取透明圖像字節(jié)。

我們還可以使用 cached_network_image 包來實(shí)現(xiàn)這一點(diǎn),以及更多。

return FadeInImage.memoryNetwork(
  image: imageUrl,
  placeholder: kTransparentImage,
);

// 或者
return CachedNetworkImage(
  imageUrl: imageUrl,
);

10.緩存圖像以減少網(wǎng)絡(luò)使用并提高性能

為了避免每次下載相同的圖片,我們可以緩存第一次下載的圖片并重復(fù)使用,為了實(shí)現(xiàn)這一點(diǎn),我們可以創(chuàng)建自己的緩存機(jī)制,或者我們可以直接使用 cached_network_image。

它緩存網(wǎng)絡(luò)圖像,默認(rèn)情況下自動(dòng)顯示它們的淡入效果,并提供了更多的圖像控制。

11.注意非經(jīng)常性成本

Image widget 沒有 const 構(gòu)造函數(shù),雖然這在大多數(shù)情況下都不是問題,但我們可以通過將它包裝在自定義 widget 中來修復(fù)它。

它不僅可以讓我們的應(yīng)用程序更具性能,而且我們還可以根據(jù)我們的意愿定制小部件,例如,我們可以為每個(gè)圖像小部件創(chuàng)建一個(gè)全局解決方案,而不是每次都處理 error/loading 情況。

enum _ImageType { asset, network }

class AppImage extends StatelessWidget {
  const AppImage.asset(
    this.image, {
    super.key,
  }) : type = _ImageType.asset;

  const AppImage.network(
    this.image, {
    super.key,
  }) : type = _ImageType.network;

  final String image;
  final _ImageType type;

  @override
  Widget build(BuildContext context) {
    const errorWidget = Icon(Icons.error);
    return switch (type) {
      _ImageType.asset => Image.asset(
          image,
          errorBuilder: (_, __, ___) => errorWidget,
        ),
      _ImageType.network => Image.network(
          image,
          errorBuilder: (_, __, ___) => errorWidget,
        ),
    };
  }
}


/// 使用
const AppImage.asset(''), // OK
const Image.asset(''), // 錯(cuò)誤!!Image 沒有const構(gòu)造函數(shù)

// 正如您所知,擁有 const 的小部件非常重要以獲得更好的性能。

12.在失敗時(shí)顯示重試按鈕

有時(shí)候由于網(wǎng)絡(luò)連接不好或其他原因,圖片無法第一次加載,顯示錯(cuò)誤消息是好的,但這還不夠,如果我們想把應(yīng)用的 UX 提升到另一個(gè)層次,我們應(yīng)該讓用戶重新加載圖片,并繼續(xù)使用應(yīng)用,而不會(huì)遇到任何麻煩。

圖片圖片

class MyImage extends StatefulWidget {
  const MyImage({super.key});

  @override
  State createState() => _MyImageState();
}

class _MyImageState extends State {
  int attempt = 0;

  @override
  Widget build(BuildContext context) {
    return CachedNetworkImage(
      imageUrl: imageUrl,
      cacheKey: '$attempt',
      height: 200,
      width: 250,
      fit: BoxFit.cover,
      errorWidget: (_, __, ___) {
        return RetryWidget(
          height: 200,
          width: 250,
          onTap: () => setState(() => attempt++),
        );
      },
    );
  }
}

// Just a basic retry button
class RetryWidget extends StatelessWidget {
  const RetryWidget({
    super.key,
    required this.height,
    required this.width,
    required this.onTap,
  });

  final double? height;
  final double? width;
  final void Function() onTap;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        height: height,
        width: width,
        alignment: Alignment.center,
        decoration: const BoxDecoration(color: Colors.black12),
        child: const Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Icon(Icons.image_not_supported, size: 20),
            SizedBox(height: 12),
            Padding(
              padding: EdgeInsets.symmetric(horizontal: 12),
              child: Text(
                "Image couldn't load, tap here to retry",
                textAlign: TextAlign.center,
                style: TextStyle(fontSize: 14, color: Colors.black),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

結(jié)語

僅僅展示圖片是不夠的!您還應(yīng)該為用戶提供他們需要的最佳體驗(yàn)!因此,我強(qiáng)烈建議您創(chuàng)建自己的自定義圖片 widget,將它們隨意組合并自由使用!

原文:https://medium.com/itnext/12-image-tips-and-best-practices-for-the-best-ux-performance-in-flutter-e7a1b2b1da2a&strip=0&vwsrc=1&referer=medium-parser

責(zé)任編輯:武曉燕 來源: 獨(dú)立開發(fā)者張張
相關(guān)推薦

2023-07-07 12:26:39

攜程開發(fā)

2023-09-13 08:00:00

JavaScript循環(huán)語句

2013-12-31 09:26:31

JavaScript技巧

2023-10-16 11:27:43

2023-07-25 11:22:31

2011-08-11 09:45:25

2010-02-04 11:55:27

ibmdwDB2

2012-03-19 09:55:38

CSS

2012-03-29 09:35:32

WEBCSS

2011-12-31 10:18:33

響應(yīng)設(shè)計(jì)

2010-07-06 09:07:09

2009-11-26 10:31:55

配置IPS最佳實(shí)踐

2022-05-30 07:48:11

DevOps測(cè)試策略

2013-05-17 11:43:55

主數(shù)據(jù)數(shù)據(jù)管理

2010-11-15 09:13:22

云計(jì)算開發(fā)測(cè)試

2009-07-01 17:44:46

Servlet和JSP

2020-09-17 06:00:21

Git

2018-01-12 14:37:34

Java代碼實(shí)踐

2017-01-15 14:50:34

Spring Batc實(shí)踐

2018-05-02 13:59:01

大數(shù)據(jù)數(shù)據(jù)收集數(shù)據(jù)科學(xué)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 亚洲三级免费看 | 亚洲精品乱码 | 免费观看日韩精品 | 99久久久久久 | 国产视频福利一区 | 精品无码久久久久久国产 | 成人免费大片黄在线播放 | 四虎免费视频 | 亚洲精品一区二区三区中文字幕 | 一区视频 | 日韩三级| 亚洲精色| 成人黄色在线观看 | 欧美成人精品一区二区男人看 | 国产精品欧美日韩 | 欧美精 | 日韩激情免费 | 亚欧洲精品在线视频免费观看 | 亚洲精品免费视频 | 日韩av电影在线观看 | 欧美一区二区三区在线观看视频 | 国产久| 91在线视频播放 | 国产高清视频一区 | 久久久久久久av | 天天操天天拍 | 久久免费精品视频 | 麻豆久久精品 | 精品一区二区三区在线观看 | 成人一区二区在线 | 精品国产精品三级精品av网址 | 久久久久久久久久久久久91 | 91免费观看视频 | 91伊人网 | 久久噜噜噜精品国产亚洲综合 | 日韩在线精品视频 | 国产亚洲二区 | 粉嫩高清一区二区三区 | 91精品国产91久久综合桃花 | 亚洲色欲色欲www | 国产久|