为什么Flutter没有ListView.iterable()构造函数?

问题描述 投票:0回答:1

主要是为了讨论,但确实如此。

我在互联网上没有找到任何类似的东西,所以我想也许性能受到了一些影响,或者也许没有人没有尝试过。但如果没有,这个构造函数将非常有助于减少

ListView
的样板代码。

通常,在 Flutter 中,当我们使用

ListView.builder()
构造函数时,它看起来像这样:

@override
Widget build(BuildContext context) {
  final items = <ListItem>[
    // item 1,
    // item 2,
    // ... et cetera
  ];
  
  // ... code
  child: ListView.builder(
    itemBuilder: (context, index) => SomeFancyListTile(
      leading: Image.network(items[index].imageUrl),
      title: Text(items[index].title),
    ),
    itemCount: items.length,
  ),
  // ... more code
}

这完全没问题,但最近,我需要使用

LinkedList
来根据相邻元素修改小部件,它没有方便的
[]
运算符,而只有
.elementAt(index)
方法。然而,了解
LinkedList
的结构后,对于大型列表,代码将非常未优化(渲染整个列表需要 O(n^2))。

但后来我认为不需要这样,因为

ListView
会一个接一个地渲染项目,这正是迭代器的工作原理。

因此,我实现了一个自定义的

SliverChildDelegate
,它正是这样做的 - 将迭代器的功能与
ListView
的渲染相结合。它还带来了一些语法改进,可以减少样板代码以及每次我们需要访问列表项时使用
items[index]

import 'package:flutter/material.dart';

typedef ValueWidgetBuilder<T> = Widget Function(BuildContext context, T value);

class SliverIterableBuilderDelegate<T> extends SliverChildDelegate {
  SliverIterableBuilderDelegate(
    this.iterable, {
    required this.builder,
  }) : iterator = iterable.iterator;

  final Iterable<T> iterable;
  final ValueWidgetBuilder<T> builder;
  final Iterator<T> iterator;

  @override
  Widget? build(BuildContext context, int index) {
    if (index < 0 || index >= iterable.length) {
      return null;
    }

    if (iterator.moveNext()) {
      return builder(context, iterator.current);
    }

    return null;
  }

  @override
  bool shouldRebuild(covariant SliverChildDelegate oldDelegate) => true;
}

@override
Widget build(BuildContext context) {
  final items = <ListItem>[
    // item 1,
    // item 2,
    // ... et cetera
  ];
  
  // ... code
  child: ListView.custom(
    childrenDelegate: SliverIterableBuilderDelegate(
      builder: (context, value) => SomeFancyListTile(
        leading: Image.network(value.imageUrl),
        title: Text(value.title),
      ),
  ),
  // ... more code
}

所以,我想知道这种方法是否有一些缺点?另外,建议添加

ListView.iterable()
构造函数以进一步减少代码。

flutter listview widget
1个回答
0
投票

这是一个好主意,但是这个实现不起作用。无论滚动方向如何,您都会调用

Iterator.moveNext()
。如果您有一个很长的列表,请向下滚动很多次,然后向上滚动,您将开始看到列表以相反的方式继续。我做了一个例子来展示这种行为,它可以直接复制粘贴到 DartPad 中:

import 'package:flutter/material.dart';

const Color darkBlue = Color.fromARGB(255, 18, 32, 47);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: darkBlue,
      ),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}

class MyWidget extends StatelessWidget {
  @override
Widget build(BuildContext context) {
  final items = List.generate(1000, (val) => val, growable: false);
  
  // ... code
  return ListView.custom(
    childrenDelegate: SliverIterableBuilderDelegate(
      items,
      builder: (context, value) => Text(
        value.toString()
      ),
  )
);}
}

typedef ValueWidgetBuilder<T> = Widget Function(BuildContext context, T value);

class SliverIterableBuilderDelegate<T> extends SliverChildDelegate {
  SliverIterableBuilderDelegate(
    this.iterable, {
    required this.builder,
  }) : iterator = iterable.iterator;

  final Iterable<T> iterable;
  final ValueWidgetBuilder<T> builder;
  final Iterator<T> iterator;

  @override
  Widget? build(BuildContext context, int index) {
    if (index < 0 || index >= iterable.length) {
      return null;
    }

    if (iterator.moveNext()) {
      return builder(context, iterator.current);
    }

    return null;
  }

  @override
  bool shouldRebuild(covariant SliverChildDelegate oldDelegate) => true;
}

最简单的解决方案是仅使用普通列表而不是链接列表,这样您就可以进行快速索引操作。

© www.soinside.com 2019 - 2024. All rights reserved.