Flutter for OpenHarmony 实战:ListView与GridView滚动列表完全指南

Flutter for OpenHarmony 实战:ListView与GridView滚动列表完全指南

文章目录

  • [Flutter for OpenHarmony 实战:ListView与GridView滚动列表完全指南](#Flutter for OpenHarmony 实战:ListView与GridView滚动列表完全指南)

摘要

ListView和GridView是Flutter中最常用的滚动Widget。ListView用于线性列表,GridView用于网格布局。理解它们的构建方式和性能优化对开发流畅的应用至关重要。这篇文章我想深入讲解ListView和GridView的使用方法。

前言

滚动列表是应用中最常见的UI形式。一开始我用ListView很随意,数据多了就卡。

后来学会了ListView.builder,性能提升了很多。GridView也有很多技巧。这篇文章我想分享滚动列表的使用经验。

一、ListView滚动列表

1.1 基础ListView

dart 复制代码
class BasicListView extends StatelessWidget {
  const BasicListView({super.key});

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: [
        _tile('项目1', '副标题1'),
        _tile('项目2', '副标题2'),
        _tile('项目3', '副标题3'),
        _tile('项目4', '副标题4'),
        _tile('项目5', '副标题5'),
      ],
    );
  }

  Widget _tile(String title, String subtitle) {
    return ListTile(
      title: Text(title),
      subtitle: Text(subtitle),
      leading: Icon(Icons.label),
      trailing: Icon(Icons.arrow_forward),
    );
  }
}

1.2 ListView.builder

dart 复制代码
class ListViewBuilder extends StatelessWidget {
  const ListViewBuilder({super.key});

  final List<String> items = const [
    '项目1', '项目2', '项目3', '项目4', '项目5',
    '项目6', '项目7', '项目8', '项目9', '项目10',
  ];

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: items.length,
      itemBuilder: (context, index) {
        return ListTile(
          title: Text(items[index]),
          subtitle: Text('索引:$index'),
          leading: CircleAvatar(child: Text('${index + 1}')),
        );
      },
    );
  }
}

1.3 ListView.separated

dart 复制代码
class ListViewSeparated extends StatelessWidget {
  const ListViewSeparated({super.key});

  final List<String> items = const [
    '项目1', '项目2', '项目3', '项目4', '项目5',
  ];

  @override
  Widget build(BuildContext context) {
    return ListView.separated(
      itemCount: items.length,
      itemBuilder: (context, index) {
        return ListTile(
          title: Text(items[index]),
        );
      },
      separatorBuilder: (context, index) {
        return Divider(height: 1, color: Colors.grey);
      },
    );
  }
}

1.4 水平ListView

dart 复制代码
class HorizontalListView extends StatelessWidget {
  const HorizontalListView({super.key});

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 150,
      child: ListView.builder(
        scrollDirection: Axis.horizontal,
        itemCount: 20,
        itemBuilder: (context, index) {
          return Container(
            width: 150,
            margin: EdgeInsets.all(8),
            decoration: BoxDecoration(
              color: Colors.primaries[index % Colors.primaries.length],
              borderRadius: BorderRadius.circular(12),
            ),
            child: Center(
              child: Text(
                'Item $index',
                style: TextStyle(color: Colors.white, fontSize: 20),
              ),
            ),
          );
        },
      ),
    );
  }
}

1.5 ListView自定义

dart 复制代码
class CustomListView extends StatelessWidget {
  const CustomListView({super.key});

  @override
  Widget build(BuildContext context) {
    return ListView.custom(
      childrenDelegate: SliverChildBuilderDelegate(
        (context, index) {
          return ListTile(
            title: Text('项目 $index'),
          );
        },
        childCount: 20,
        addAutomaticKeepAlives: true,
        addRepaintBoundaries: true,
      ),
    );
  }
}

二、GridView网格布局

2.1 GridView.count

dart 复制代码
class GridViewCount extends StatelessWidget {
  const GridViewCount({super.key});

  @override
  Widget build(BuildContext context) {
    return GridView.count(
      crossAxisCount: 2,
      children: List.generate(20, (index) {
        return Container(
          decoration: BoxDecoration(
            color: Colors.primaries[index % Colors.primaries.length],
            borderRadius: BorderRadius.circular(12),
          ),
          child: Center(
            child: Text(
              '$index',
              style: TextStyle(color: Colors.white, fontSize: 30),
            ),
          ),
        );
      }),
    );
  }
}

2.2 GridView.extent

dart 复制代码
class GridViewExtent extends StatelessWidget {
  const GridViewExtent({super.key});

  @override
  Widget build(BuildContext context) {
    return GridView.extent(
      maxCrossAxisExtent: 150,
      children: List.generate(20, (index) {
        return Container(
          decoration: BoxDecoration(
            color: Colors.teal,
            borderRadius: BorderRadius.circular(8),
          ),
          child: Center(
            child: Text(
              'Item $index',
              style: TextStyle(color: Colors.white),
            ),
          ),
        );
      }),
    );
  }
}

2.3 GridView.builder

dart 复制代码
class GridViewBuilder extends StatelessWidget {
  const GridViewBuilder({super.key});

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 3,
        mainAxisSpacing: 8,
        crossAxisSpacing: 8,
        childAspectRatio: 1,
      ),
      itemCount: 50,
      itemBuilder: (context, index) {
        return Container(
          decoration: BoxDecoration(
            color: Colors.primaries[index % Colors.primaries.length],
            borderRadius: BorderRadius.circular(12),
          ),
          child: Center(
            child: Text(
              '$index',
              style: TextStyle(color: Colors.white, fontSize: 24),
            ),
          ),
        );
      },
    );
  }
}

2.4 自定义网格

dart 复制代码
class CustomGridView extends StatelessWidget {
  const CustomGridView({super.key});

  @override
  Widget build(BuildContext context) {
    return GridView.custom(
      gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
        maxCrossAxisExtent: 150,
        mainAxisSpacing: 8,
        crossAxisSpacing: 8,
      ),
      childrenDelegate: SliverChildBuilderDelegate(
        (context, index) {
          return Container(
            decoration: BoxDecoration(
              color: Colors.orange,
              borderRadius: BorderRadius.circular(12),
            ),
            child: Center(
              child: Text(
                'Item $index',
                style: TextStyle(color: Colors.white),
              ),
            ),
          );
        },
        childCount: 30,
      ),
    );
  }
}

三、列表性能优化

3.1 使用const

dart 复制代码
class OptimizedListView extends StatelessWidget {
  const OptimizedListView({super.key});

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: 1000,
      itemBuilder: (context, index) {
        return const ListTile(
          title: Text('标题'),
          subtitle: Text('副标题'),
          leading: Icon(Icons.star),
        );
      },
    );
  }
}

3.2 提取Widget

dart 复制代码
class ExtractedListItem extends StatelessWidget {
  final int index;

  const ExtractedListItem({super.key, required this.index});

  @override
  Widget build(BuildContext context) {
    return ListTile(
      title: Text('项目 $index'),
      subtitle: Text('这是项目 $index 的描述'),
      leading: CircleAvatar(child: Text('$index')),
    );
  }
}

class OptimizedListExtracted extends StatelessWidget {
  const OptimizedListExtracted({super.key});

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: 1000,
      itemBuilder: (context, index) {
        return ExtractedListItem(index: index);
      },
    );
  }
}

3.3 AutomaticKeepAliveClientMixin

dart 复制代码
class KeepAliveListItem extends StatefulWidget {
  final int index;

  const KeepAliveListItem({super.key, required this.index});

  @override
  State<KeepAliveListItem> createState() => _KeepAliveListItemState();
}

class _KeepAliveListItemState extends State<KeepAliveListItem>
    with AutomaticKeepAliveClientMixin {
  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return ListTile(
      title: Text('项目 ${widget.index}'),
      subtitle: Text('滚动后保持状态'),
    );
  }
}

3.4 ListView性能对比

dart 复制代码
class PerformanceComparison extends StatelessWidget {
  const PerformanceComparison({super.key});

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: 10000,
      itemExtent: 60, // 固定高度提高性能
      itemBuilder: (context, index) {
        return _buildItem(index);
      },
    );
  }

  Widget _buildItem(int index) {
    return ListTile(
      title: Text('项目 $index'),
      leading: Icon(Icons.list),
    );
  }
}

四、自定义滚动效果

4.1 滚动监听

dart 复制代码
class ScrollListener extends StatefulWidget {
  const ScrollListener({super.key});

  @override
  State<ScrollListener> createState() => _ScrollListenerState();
}

class _ScrollListenerState extends State<ScrollListener> {
  final ScrollController _controller = ScrollController();
  bool _showButton = false;

  @override
  void initState() {
    super.initState();
    _controller.addListener(() {
      setState(() {
        _showButton = _controller.offset > 500;
      });
    });
  }

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

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        ListView.builder(
          controller: _controller,
          itemCount: 100,
          itemBuilder: (context, index) {
            return ListTile(title: Text('项目 $index'));
          },
        ),
        if (_showButton)
          Positioned(
            right: 16,
            bottom: 16,
            child: FloatingActionButton(
              onPressed: () {
                _controller.animateTo(
                  0,
                  duration: Duration(seconds: 1),
                  curve: Curves.easeInOut,
                );
              },
              child: Icon(Icons.arrow_upward),
            ),
          ),
      ],
    );
  }
}

4.2 下拉刷新

dart 复制代码
class PullToRefresh extends StatefulWidget {
  const PullToRefresh({super.key});

  @override
  State<PullToRefresh> createState() => _PullToRefreshState();
}

class _PullToRefreshState extends State<PullToRefresh> {
  List<String> items = List.generate(20, (index) => '项目 $index');

  Future<void> _refresh() async {
    await Future.delayed(Duration(seconds: 2));
    setState(() {
      items = List.generate(20, (index) => '刷新项目 $index');
    });
  }

  @override
  Widget build(BuildContext context) {
    return RefreshIndicator(
      onRefresh: _refresh,
      child: ListView.builder(
        itemCount: items.length,
        itemBuilder: (context, index) {
          return ListTile(title: Text(items[index]));
        },
      ),
    );
  }
}

4.3 上拉加载

dart 复制代码
class LoadMore extends StatefulWidget {
  const LoadMore({super.key});

  @override
  State<LoadMore> createState() => _LoadMoreState();
}

class _LoadMoreState extends State<LoadMore> {
  final ScrollController _controller = ScrollController();
  List<String> items = List.generate(20, (index) => '项目 $index');
  bool _isLoading = false;

  @override
  void initState() {
    super.initState();
    _controller.addListener(() {
      if (_controller.position.pixels >= _controller.position.maxScrollExtent - 200) {
        _loadMore();
      }
    });
  }

  Future<void> _loadMore() async {
    if (_isLoading) return;
    setState(() {
      _isLoading = true;
    });

    await Future.delayed(Duration(seconds: 2));

    setState(() {
      items.addAll(List.generate(20, (index) => '新项目 ${items.length + index}'));
      _isLoading = false;
    });
  }

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

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      controller: _controller,
      itemCount: items.length + (_isLoading ? 1 : 0),
      itemBuilder: (context, index) {
        if (index == items.length) {
          return Center(
            child: Padding(
              padding: EdgeInsets.all(16),
              child: CircularProgressIndicator(),
            ),
          );
        }
        return ListTile(title: Text(items[index]));
      },
    );
  }
}

4.4 粘性标题

dart 复制代码
class StickyHeaders extends StatelessWidget {
  const StickyHeaders({super.key});

  final Map<String, List<String>> _data = const {
    'A': ['Apple', 'Apricot', 'Avocado'],
    'B': ['Banana', 'Blueberry', 'Blackberry'],
    'C': ['Cherry', 'Coconut', 'Cranberry'],
  };

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: _data.length * 2,
      itemBuilder: (context, index) {
        if (index.isEven) {
          final key = _data.keys.elementAt(index ~/ 2);
          return Container(
            color: Colors.grey.shade200,
            padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
            child: Text(
              key,
              style: TextStyle(fontWeight: FontWeight.bold),
            ),
          );
        } else {
          final key = _data.keys.elementAt(index ~/ 2);
          final items = _data[key]!;
          final itemIndex = (index - 1) % 3;
          if (itemIndex < items.length) {
            return ListTile(title: Text(items[itemIndex]));
          }
          return SizedBox.shrink();
        }
      },
    );
  }
}

总结

ListView和GridView是滚动布局的基础。

核心要点

  1. ListView用于线性列表
  2. GridView用于网格布局
  3. builder方式性能更好
  4. 使用const优化性能
  5. 合理使用itemExtent

最佳实践

  • 数据多用builder方式
  • 固定高度设置itemExtent
  • 提取Widget减少重建
  • 及时释放Controller
  • 使用keepAlive保持状态

滚动列表是Flutter开发中最常用的组件,需要熟练掌握。

欢迎加入开源鸿蒙跨平台社区:开源鸿蒙跨平台开发者社区

相关推荐
程序员清洒2 小时前
Flutter for OpenHarmony:ListView — 高效滚动列表
开发语言·flutter·华为·鸿蒙
naruto_lnq2 小时前
C++与自动驾驶系统
开发语言·c++·算法
熊猫钓鱼>_>2 小时前
从零到一:打造“抗造” Electron 录屏神器的故事
前端·javascript·ffmpeg·electron·node·录屏·record
wjs20242 小时前
jEasyUI 启用行内编辑
开发语言
夕除2 小时前
js--6
java·开发语言
ytttr8732 小时前
C#实现海康威视智能车牌识别
开发语言·c#
Miguo94well2 小时前
Flutter框架跨平台鸿蒙开发——旅行攻略规划APP的开发流程
flutter·华为·harmonyos·鸿蒙
梵刹古音2 小时前
【C语言】 关键字与用户标识符
c语言·开发语言
悟能不能悟2 小时前
grpc协议
开发语言