如何在Flutter中实现屏幕中部Tab Bar及相关性能优化方案
需求与问题描述

- 除顶部AppBar和底部栏外,页面其余区域均可滚动
- TabView内包含50+条带图片的大数据列表,需做性能优化,且所有列表项需实现骨架屏加载
- 开发难点:多ListView、Sliver组件的使用,Tab指示器及选中状态的处理
实现代码
import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Scrollable Tab Page', theme: ThemeData(primarySwatch: Colors.blue), home: const MainPage(), ); } } class MainPage extends StatefulWidget { const MainPage({super.key}); @override State<MainPage> createState() => _MainPageState(); } class _MainPageState extends State<MainPage> with SingleTickerProviderStateMixin { late TabController _tabController; @override void initState() { super.initState(); _tabController = TabController(length: 2, vsync: this); } @override void dispose() { _tabController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Tab Scroll Page'), bottom: TabBar( controller: _tabController, tabs: const [ Tab(text: '列表1'), Tab(text: '列表2'), ], indicatorColor: Colors.white, labelColor: Colors.white, unselectedLabelColor: Colors.grey[300], ), ), body: NestedScrollView( headerSliverBuilder: (context, innerBoxIsScrolled) => [], body: TabBarView( controller: _tabController, children: const [ ItemList(), ItemList(), ], ), ), bottomNavigationBar: BottomNavigationBar( items: const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'), BottomNavigationBarItem(icon: Icon(Icons.settings), label: '设置'), ], currentIndex: 0, ), ); } } class ItemList extends StatefulWidget { const ItemList({super.key}); @override State<ItemList> createState() => _ItemListState(); } class _ItemListState extends State<ItemList> with AutomaticKeepAliveClientMixin { final List<String> _imageUrls = List.generate(60, (index) => 'https://picsum.photos/200/200?random=$index'); bool _isLoading = true; @override void initState() { super.initState(); // 模拟接口请求延迟 SchedulerBinding.instance.addPostFrameCallback((_) { Future.delayed(const Duration(seconds: 2), () { if (mounted) { setState(() { _isLoading = false; }); } }); }); } @override bool get wantKeepAlive => true; @override Widget build(BuildContext context) { super.build(context); return ListView.builder( itemCount: _isLoading ? 10 : _imageUrls.length, itemBuilder: (context, index) { if (_isLoading) { return const SkeletonItem(); } return Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Card( child: Padding( padding: const EdgeInsets.all(12), child: Row( children: [ Image.network( _imageUrls[index], width: 80, height: 80, fit: BoxFit.cover, ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '列表项 ${index + 1}', style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500), ), const SizedBox(height: 8), const Text( '这是列表项的描述内容,用于展示更多信息', maxLines: 2, overflow: TextOverflow.ellipsis, style: TextStyle(color: Colors.grey), ), ], ), ), ], ), ), ), ); }, ); } } class SkeletonItem extends StatelessWidget { const SkeletonItem({super.key}); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Card( child: Padding( padding: const EdgeInsets.all(12), child: Row( children: [ Container( width: 80, height: 80, decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.circular(8), ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: double.infinity, height: 20, decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.circular(4), ), ), const SizedBox(height: 8), Container( width: 200, height: 16, decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.circular(4), ), ), ], ), ), ], ), ), ), ); } }
关键实现说明
- 性能优化:使用
ListView.builder实现懒加载,避免一次性创建所有列表项;通过AutomaticKeepAliveClientMixin保持Tab页面状态,切换Tab时不会重新加载数据 - 骨架屏:加载状态下显示
SkeletonItem灰色占位组件,模拟列表项布局 - 滚动布局:通过
NestedScrollView配合TabBarView,实现除AppBar和底部栏外的区域滚动 - Tab指示器:自定义
TabBar的indicatorColor、labelColor等属性,实现选中状态样式
可将上述代码复制到Dart Pad中直接运行测试。
内容的提问来源于stack exchange,提问作者user3066829
相关产品推荐
相关产品推荐

