嵌套列表 - ShrinkWrap 与 Slivers
使用 ShrinkWrap 的列表列表
下面是一些使用ListView
对象呈现列表列表的代码,内部列表的shrinkWrap
值设置为 true。shrinkWrap
强行评估整个内部列表,允许它请求有限的高度,而不是通常的ListView
对象高度,即无穷大!
下面是基本的代码结构:
ListView( // Setting `shrinkWrap` to `true` here is both unnecessary and expensive. children: <Widget>[ ListView.builder( itemCount: list1Children.length, itemBuilder: (BuildContext context, int index) { return list1Children[index]; }, // This forces the `ListView` to build all of its children up front, // negating much of the benefit of using `ListView.builder`. shrinkWrap: true, ), ListView.builder( itemCount: list2Children.length, itemBuilder: (BuildContext context, int index) { return list2Children[index]; }, // This forces the `ListView` to build all of its children up front, // negating much of the benefit of using `ListView.builder`. shrinkWrap: true, ), ... ], )
复制代码
注意:观察外部
ListView
没有将其shrinkWrap
值设置为true
。只有内部列表需要设置shrinkWrap
。
另请注意:虽然
ListView.builder
(默认情况下)有效地构建其子项,为您节省构建屏幕外小部件的不必要成本,但设置shrinkWrap
为true
覆盖此默认行为!
import 'package:flutter/material.dart'; import 'dart:math' as math; void main() { runApp(ShrinkWrApp()); } class ShrinkWrApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, title: 'ShrinkWrap vs Slivers', home: Scaffold( appBar: AppBar( title: const Text("ShrinkWrap, Street Rat, I don't, Buy that!"), ), body: const ShrinkWrapSlivers(), ), ); } } class ShrinkWrapSlivers extends StatefulWidget { const ShrinkWrapSlivers({ Key? key, }) : super(key: key); @override _ShrinkWrapSliversState createState() => _ShrinkWrapSliversState(); } class _ShrinkWrapSliversState extends State<ShrinkWrapSlivers> { List<ListView> innerLists = []; final numLists = 15; final numberOfItemsPerList = 100; @override void initState() { super.initState(); for (int i = 0; i < numLists; i++) { final _innerList = <ColorRow>[]; for (int j = 0; j < numberOfItemsPerList; j++) { _innerList.add(const ColorRow()); } innerLists.add( ListView.builder( itemCount: numberOfItemsPerList, itemBuilder: (BuildContext context, int index) => _innerList[index], shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), ), ); } } @override Widget build(BuildContext context) { return ListView.builder( itemCount: numLists, itemBuilder: (context, index) => innerLists[index]); } } @immutable class ColorRow extends StatefulWidget { const ColorRow({Key? key}) : super(key: key); @override State createState() => ColorRowState(); } class ColorRowState extends State<ColorRow> { Color? color; @override void initState() { super.initState(); color = randomColor(); } @override Widget build(BuildContext context) { print('Building ColorRowState'); return Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ randomColor(), randomColor(), ], ), ), child: Row( children: <Widget>[ Padding( padding: const EdgeInsets.all(8.0), child: Container(height: 50, width: 50, color: Colors.white), ), Flexible( child: Column( children: const <Widget>[ Padding( padding: EdgeInsets.all(8), child: Text('这里是 坚果前端小课堂!', style: TextStyle(color: Colors.white)), ), ], ), ), ], ), ); } } Color randomColor() => Color((math.Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0);
复制代码
一切都建立起来!
当您滚动浏览此 UI 并注意该ColorBarState.build
方法的调用方式时,会出现可怕的部分 。每个内部列表包含 100 个元素,因此当 UI 加载时,您会立即看到 100 个“Building ColorBarState”的实例打印到控制台,
更糟糕的是,一旦向下滚动大约一百行,就会再生成一百行。😱😱😱
而且你滑动的快的时候列表会抖动!
重新构建嵌套列表
要了解如何使您的用户免受卡顿威胁,请等待我的第二节,下一节将使用 Slivers 而不是 ListViews 重建相同的 UI。