Flutter滑动面板依赖报错及面板关闭后底部导航切换问题
Flutter 两个问题的解决方案
问题1:点击Payments按钮抛出Expanded兼容异常
错误原因
错误日志显示Spacer(本质是Expanded的封装)被放置在ListView的子节点中,而Expanded/Spacer仅能作为Flex容器(如Row、Column)的直接子组件,ListView不属于Flex容器,因此出现父数据类型不兼容的错误。
修复方案
修改TabWidget代码,移除Spacer(ListView是滚动布局,Spacer无法起到填充剩余空间的作用),可替换为固定高度的SizedBox(根据需求调整):
import 'package:flutter/material.dart'; import 'package:sadapay_clone/widgets/personal_item.dart'; class TabWidget extends StatelessWidget { final ScrollController scrollController; const TabWidget({super.key, required this.scrollController}); @override Widget build(BuildContext context) => ListView( padding: const EdgeInsets.all(16), controller: scrollController, children: const [ Padding( padding: EdgeInsets.only(bottom: 20.0), child: Icon( Icons.horizontal_rule, ), ), // 替换Spacer为固定高度的SizedBox(可选,按需调整) SizedBox(height: 20), Padding( padding: EdgeInsets.only( top: 15, left: 10, bottom: 15, ), child: Text( 'Payments', textAlign: TextAlign.left, style: TextStyle( fontSize: 35, fontWeight: FontWeight.w500, color: Colors.black, ), ), ), PersonalItem( icon: Icon( Icons.phone_android_rounded, size: 30, color: Color.fromARGB(255, 255, 129, 129), ), title: 'Mobile top up', subtext: 'Instantly top up your mobile.', ), Divider(thickness: 1.5), PersonalItem( icon: Icon( Icons.receipt_rounded, size: 30, color: Color.fromARGB(255, 255, 129, 129), ), title: 'Bills and utilities', subtext: 'Pay for your utilities.', ), Divider( thickness: 1.5, ), PersonalItem( icon: Icon( Icons.card_membership_rounded, size: 30, color: Color.fromARGB(255, 255, 129, 129), ), title: 'Money requests', subtext: 'Review pending money req.', ), ], ); }
问题2:关闭滑动面板后自动切换回Personal标签页
实现思路
给Payments组件添加回调函数,监听滑动面板的关闭状态,触发时通知父页面MyHomePage切换导航索引到0(Personal标签页)。
步骤1:修改Payments组件,添加状态监听与回调
import 'package:flutter/material.dart'; import 'package:sliding_up_panel/sliding_up_panel.dart'; import '../widgets/homepage_item.dart'; import '../widgets/tabwidget.dart'; class Payments extends StatefulWidget { // 定义面板关闭时的回调 final VoidCallback? onPanelClosed; const Payments({super.key, this.onPanelClosed}); @override State<Payments> createState() => _PaymentsState(); } class _PaymentsState extends State<Payments> { late PanelController _panelController; @override void initState() { super.initState(); _panelController = PanelController(); // 监听面板状态变化 _panelController.addListener(_handlePanelChange); } void _handlePanelChange() { if (_panelController.isClosed) { // 面板关闭时触发回调 widget.onPanelClosed?.call(); } } @override void dispose() { _panelController.removeListener(_handlePanelChange); _panelController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return SlidingUpPanel( controller: _panelController, // 绑定面板控制器 backdropEnabled: true, panelBuilder: (scrollController) => buildSlidingPanel(scrollController: scrollController), body: const HomePageItem(), borderRadius: const BorderRadius.only( topLeft: Radius.circular(20), topRight: Radius.circular(20)), backdropTapClosesPanel: true, defaultPanelState: PanelState.OPEN, panelSnapping: false, maxHeight: 400, minHeight: 40, ); } Widget buildSlidingPanel({ required ScrollController scrollController, }) => TabWidget( scrollController: scrollController, ); }
步骤2:修改MyHomePage,接收回调并切换索引
import 'package:flutter/material.dart'; import 'package:sadapay_clone/screens/more_screen.dart'; import 'package:sadapay_clone/screens/payments.dart'; import '../widgets/homepage_item.dart'; class MyHomePage extends StatefulWidget { const MyHomePage({ super.key, }); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { int _currIndex = 0; // 动态生成当前页面,传递回调 Widget getCurrentTab() { switch (_currIndex) { case 0: return const HomePageItem(); case 1: return Payments( onPanelClosed: () { setState(() { _currIndex = 0; // 切换回Personal标签页 }); }, ); case 2: return const MoreScreen(); default: return const HomePageItem(); } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color.fromARGB(255, 238, 237, 237), body: getCurrentTab(), // 使用动态生成的页面 bottomNavigationBar: BottomNavigationBar( currentIndex: _currIndex, items: const <BottomNavigationBarItem>[ BottomNavigationBarItem( icon: Icon(Icons.home_rounded), label: 'Personal', ), BottomNavigationBarItem( icon: Icon(Icons.view_kanban), label: 'Payments', ), BottomNavigationBarItem( icon: Icon(Icons.menu), label: 'More', ), ], fixedColor: const Color.fromARGB(255, 255, 129, 129), onTap: (index) { setState(() { _currIndex = index; }); }, ), ); } }
内容的提问来源于stack exchange,提问作者Ahmad Maaz
相关产品推荐
相关产品推荐

