網站首頁 編程語言 正文
前言
列表 ListView 是應用中最為常見的組件,而列表往往也會承載很多元素,當元素多,尤其是那種圖片文件比較大的場合,就可能會導致列表卡頓,嚴重的時候可能導致應用崩潰。本篇來介紹如何優化列表。
優化點1:使用 builder構建列表
當你的列表元素是動態增長的時候(比如上拉加載更多),請不要直接用children
?的方式,一直往children
?的數組增加組件,那樣會很糟糕。
//糟糕的用法 ListView( ??children:?[ ????item1, ????item2, ????item3, ????... ??], ) //正確的用法 ListView.builder( ??itemBuilder:?(context,?index)?=>?ListItem(), ??itemCount:?itemCount, )
對于 ListView.builder 是按需構建列表元素,也就是只有那些可見的元素才會調用itemBuilder 構建元素,這樣對于大列表而言性能開銷自然會小很多。
Creates a scrollable, linear array of widgets that are created on demand. This constructor is appropriate for list views with a large (or infinite) number of children because the builder is called only for those children that are actually visible.
優化點2:禁用 addAutomaticKeepAlives 和 addRepaintBoundaries 特性
這兩個屬性都是為了優化滾動過程中的用戶體驗的。addAutomaticKeepAlives
?特性默認是?true
,意思是在列表元素不可見后可以保持元素的狀態,從而在再次出現在屏幕的時候能夠快速構建。這其實是一個拿空間換時間的方法,會造成一定程度的內存開銷。可以設置為?false
?關閉這一特性。缺點是滑動過快的時候可能會出現短暫的白屏(實際會很少發生)。
addRepaintBoundaries
?是將列表元素使用一個重繪邊界(Repaint Boundary)包裹,從而使得滾動的時候可以避免重繪。而如果列表很容易繪制(列表元素布局比較簡單的情況下)的時候,可以關閉這個特性來提高滾動的流暢度。
addAutomaticKeepAlives:?false, addRepaintBoundaries:?false,
優化點3:盡可能將列表元素中不變的組件使用 const 修飾
使用?const
?相當于將元素緩存起來實現共用,若列表元素某些部分一直保持不變,那么可以使用?const
?修飾。
return?Padding( ??child:?Row( ????children:?[ ??????const?ListImage(), ??????const?SizedBox( ????????width:?5.0, ??????), ??????Text('第$index?個元素'), ????], ??), ??padding:?EdgeInsets.all(10.0), );
優化點4:使用 itemExtent 確定列表元素滾動方向的尺寸
對于很多列表,我們在滾動方向上的尺寸是提前可以根據 UI設計稿知道的,如果能夠知道的話,那么使用?itemExtent
?屬性制定列表元素在滾動方向的尺寸,可以提升性能。這是因為,如果不指定的話,在滾動過程中,會需要推算每個元素在滾動方向的尺寸從而消耗計算資源。
itemExtent:?120,
優化實例
下面是一開始未改造的列表,嗯,可以認為是垃圾代碼。
class?LargeListView?extends?StatefulWidget?{ ??const?LargeListView({Key??key})?:?super(key:?key); ??@override ??_LargeListViewState?createState()?=>?_LargeListViewState(); } class?_LargeListViewState?extends?State<LargeListView>?{ ??@override ??Widget?build(BuildContext?context)?{ ????return?Scaffold( ??????appBar:?AppBar( ????????title:?Text('大列表'), ????????brightness:?Brightness.dark, ??????), ??????body:?ListView( ????????children:?List.generate( ??????????1000, ??????????(index)?=>?Padding( ????????????padding:?EdgeInsets.all(10.0), ????????????child:?Row( ??????????????children:?[ ????????????????Image.network( ??????????????????'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7869eac08a7d4177b600dc7d64998204~tplv-k3u1fbpfcp-watermark.jpeg', ??????????????????width:?200, ????????????????), ????????????????const?SizedBox( ??????????????????width:?5.0, ????????????????), ????????????????Text('第$index?個元素'), ??????????????], ????????????), ??????????), ????????), ??????), ????); ??} }
當然,實際不會是用?List.generate
?來生成列表元素,但是也不要用一個?List<Widget>
?列表對象一直往里面加列表元素,然后把這個列表作為?ListView
?的?children
!改造后的代碼如下所示,因為將列表元素拆分得更細,代碼量是多一些,但是性能上會好很多。
import?'package:flutter/material.dart'; class?LargeListView?extends?StatefulWidget?{ ??const?LargeListView({Key??key})?:?super(key:?key); ??@override ??_LargeListViewState?createState()?=>?_LargeListViewState(); } class?_LargeListViewState?extends?State<LargeListView>?{ ??@override ??Widget?build(BuildContext?context)?{ ????return?Scaffold( ??????appBar:?AppBar( ????????title:?Text('大列表'), ????????brightness:?Brightness.dark, ??????), ??????body:?ListView.builder( ????????itemBuilder:?(context,?index)?=>?ListItem( ??????????index:?index, ????????), ????????itemCount:?1000, ????????addAutomaticKeepAlives:?false, ????????addRepaintBoundaries:?false, ????????itemExtent:?120.0, ??????), ????); ??} } class?ListItem?extends?StatelessWidget?{ ??final?int?index; ??ListItem({Key??key,?required?this.index})?:?super(key:?key); ??@override ??Widget?build(BuildContext?context)?{ ????return?Padding( ??????child:?Row( ????????children:?[ ??????????const?ListImage(), ??????????const?SizedBox( ????????????width:?5.0, ??????????), ??????????Text('第$index?個元素'), ????????], ??????), ??????padding:?EdgeInsets.all(10.0), ????); ??} } class?ListImage?extends?StatelessWidget?{ ??const?ListImage({Key??key})?:?super(key:?key); ??@override ??Widget?build(BuildContext?context)?{ ????return?Image.network( ??????'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7869eac08a7d4177b600dc7d64998204~tplv-k3u1fbpfcp-watermark.jpeg', ??????width:?200, ????); ??} }
總結
本篇介紹了 Flutter?ListView
?的4個優化要點,非常實用哦!實際上,這些要點都可以從官網的文檔里找出對應的說明。因此,如果遇到了性能問題,除了搜索引擎外,也建議多看看官方的文檔。另外一個,對于列表圖片,有時候也需要前后端配合,比如目前的手機都是號稱1億像素的,如果上傳的時候直接上傳原圖,那么加載如此大的圖片肯定是非常消耗資源的。對于這種情況,建議是生成列表縮略圖(可能需要針對不同屏幕尺寸生成不同的縮略圖)。
原文鏈接:https://mp.weixin.qq.com/s/3cTkPYPmT7uWWAWiFtW8rg
相關推薦
- 2022-08-25 一文了解Android?ViewModelScope?如何自動取消協程_Android
- 2022-04-19 Windows中Python上傳文件到Liunx下的fastdfs
- 2022-11-02 Mango?Cache緩存管理庫TinyLFU源碼解析_Golang
- 2022-04-25 Entity?Framework?Core批處理SQL語句_實用技巧
- 2022-05-18 C語言程序環境和預處理詳解分析_C 語言
- 2023-12-12 SSM整合 spring-mybaits配置文件——設置數據庫字段名駝峰命名規則
- 2024-04-02 linux網絡查看配置與docker網絡刪除
- 2023-11-17 Linux centos創建用戶以及賦予sudo權限
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支