Flutter:点击悬浮按钮重复触发Dialer页面BottomSheet的问题
解决Flutter中切换到Dialer页面时每次都自动弹出BottomSheet的问题
现有Flutter项目中,HomeScreen页面包含底部标签栏与悬浮按钮,点击悬浮按钮会切换至Dialer页面。Dialer页面原本在initState生命周期中通过shouldShowDialPad参数自动弹出BottomSheet,但由于initState仅执行一次,且使用了_hasShownDialPad标记控制,导致仅首次点击悬浮按钮时BottomSheet弹出,后续点击无法触发。需求是每次切换到Dialer页面时都自动弹出该BottomSheet。
核心思路
HomeScreen中的_pages列表是提前初始化的,Dialer实例只会创建一次,因此initState仅执行一次。我们需要在每次Dialer页面被选中时主动触发BottomSheet的弹出逻辑,而非仅依赖初始化阶段的执行。这里采用GlobalKey调用页面内部方法的方案,直接可控且逻辑清晰。
修改步骤
1. 修改HomeScreen代码
- 为Dialer页面创建
GlobalKey,用于调用其内部弹出BottomSheet的方法 - 在切换页面的
_updateIndex方法中,当目标页面为Dialer时,触发弹出逻辑
import 'package:consultancy_app/src/constants/colors.dart'; import 'package:consultancy_app/src/constants/customtext.dart'; import 'package:consultancy_app/src/ui/Dialer.dart'; import 'package:consultancy_app/src/ui/ats/ats_screen.dart'; import 'package:consultancy_app/src/ui/jobs_screen.dart'; import 'package:consultancy_app/src/ui/work_space/work_space_home.dart'; import 'package:consultancy_app/src/widget/custom_drawer.dart'; import 'package:flutter/material.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override _HomeScreenState createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); // 为Dialer页面创建GlobalKey final GlobalKey<_State> _dialerKey = GlobalKey<_State>(); int _selectedIndex = 0; late final List<Widget> _pages = [ JobsScreen(scaffoldKey: _scaffoldKey), const AtsScreen(), Dialer( key: _dialerKey, // 绑定GlobalKey到Dialer实例 shouldShowDialPad: true, ), const WorkSpaceHome(), Scaffold( body: Center( child: customText(title: "Universal Search"), ), ), ]; @override Widget build(BuildContext context) { return Scaffold( drawer: CustomDrawer( scaffoldKey: _scaffoldKey, onClose: () => _scaffoldKey.currentState?.closeDrawer(), ), key: _scaffoldKey, backgroundColor: Constants.white, floatingActionButton: Container( height: 60, width: 60, decoration: BoxDecoration( shape: BoxShape.circle, color: Constants.borderColor, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 8, spreadRadius: 2, offset: const Offset(0, 4), ), ], ), child: FloatingActionButton( onPressed: () => _updateIndex(2), backgroundColor: Colors.transparent, elevation: 0, child: const Icon(Icons.dialpad_outlined, color: Constants.darkBlue, size: 30), ), ), floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, bottomNavigationBar: BottomAppBar( color: Constants.white, elevation: 1, height: kToolbarHeight, padding: EdgeInsets.zero, shape: const CircularNotchedRectangle(), notchMargin: 8.0, child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _buildNavItem(0, "assets/images/job.png", "Jobs"), _buildNavItem(1, "assets/images/ats.png", "ATS"), const SizedBox(width: 48), _buildNavItem(3, "assets/images/workspace.png", "Wspace"), _buildNavItem(4, "assets/images/search.png", "Search"), ], ), ), body: _pages[_selectedIndex], ); } Widget _buildNavItem(int index, String iconPath, String label) { return IconButton( splashColor: Constants.white, padding: EdgeInsets.zero, icon: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset( iconPath, height: 24, color: _selectedIndex == index ? Constants.darkBlue : Constants.subtitleclr, ), customText( title: label, fontSize: 11, color: _selectedIndex == index ? Constants.darkBlue : Constants.subtitleclr, ) ], ), onPressed: () => _updateIndex(index), ); } void _updateIndex(int index) { if (_selectedIndex != index) { setState(() { _selectedIndex = index; print("Navigating to index: $index"); }); // 切换到Dialer页面时,调用其弹出BottomSheet的方法 if (index == 2) { WidgetsBinding.instance.addPostFrameCallback((_) { _dialerKey.currentState?.showDialPadSheet(); }); } } } }
2. 修改Dialer代码
- 将弹出BottomSheet的方法改为公开方法(移除下划线前缀)
- 移除
_hasShownDialPad标记,取消仅弹出一次的限制 - 保留初始化阶段的弹出逻辑(可选,因HomeScreen切换时已触发)
import 'package:consultancy_app/src/constants/colors.dart'; import 'package:consultancy_app/src/provider/work_space/works_space_provider.dart'; import 'package:consultancy_app/src/utils/dial_pad_sheet.dart'; import 'package:consultancy_app/src/widget/buttons/custom_button_with_image.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; class Dialer extends StatefulWidget { final bool shouldShowDialPad; const Dialer({super.key, required this.shouldShowDialPad}); @override State<Dialer> createState() => _State(); } class _State extends State<Dialer> { @override void initState() { super.initState(); // 保持首次初始化时的弹出逻辑(可选) if (widget.shouldShowDialPad) { WidgetsBinding.instance.addPostFrameCallback((_) { showDialPadSheet(); }); } } // 改为公开方法,允许外部调用 void showDialPadSheet() { showModalBottomSheet( barrierColor: Colors.transparent, backgroundColor: Constants.lightdull, elevation: 1, context: context, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), builder: (_) => const DialPadSheet(), ); } @override Widget build(BuildContext context) { final provider = Provider.of<WorkSpaceProvider>(context, listen: false); return Scaffold( body: Padding( padding: const EdgeInsets.only(top: kToolbarHeight), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ CustomButtonForAddWorkSpace( title: "Add to workspace", subtitle: "Add New lead to workspace", imgurl: "https://cdn-icons-png.flaticon.com/128/19001/19001449.png", onTap: () {}, ), CustomButtonForAddWorkSpace( title: "Send Message", subtitle: "Send WhatsApp message", imgurl: "https://cdn-icons-png.flaticon.com/128/6422/6422213.png", onTap: () async { // 增加空值判断,避免解析失败崩溃 int? phone = int.tryParse(provider.dialerSearchController.text); if (phone != null) { var whatsappUrl = "whatsapp://send?phone=$phone"; await launchUrl(Uri.parse(whatsappUrl)); } }, ), CustomButtonForAddWorkSpace( title: "Marketing Message", subtitle: "Re-engage with offer & Announcements", imgurl: "https://cdn-icons-png.flaticon.com/128/15852/15852042.png", onTap: () {}, ), ], ), ), ); } }
补充说明
- 通过
GlobalKey直接访问Dialer的State实例,确保每次切换到该页面时都能触发BottomSheet弹出 - 移除
_hasShownDialPad标记,取消弹出次数限制,满足每次进入都弹出的需求 - 优化了WhatsApp跳转逻辑,增加空值判断,避免因输入无效号码导致的崩溃
内容的提问来源于stack exchange,提问作者Mohd Sameer Gauri
相关产品推荐
相关产品推荐

