Flutter:InteractiveViewer panEnabled在三元表达式中失效问题咨询
Flutter InteractiveViewer panEnabled 属性不生效问题
我在使用Flutter的InteractiveViewer组件时遇到了panEnabled相关问题:点击PenWidget后,期望_memoMode变为true,同时将panEnabled设为false,让InteractiveViewer界面无法拖拽。但实际_memoMode变为true后,界面仍可拖拽。想请教:是不是panEnabled中的三元表达式有误?或是有没有不用InteractiveViewer也能实现「组件可缩放且_memoMode为true时禁止拖拽」的方案?
相关代码
WorkbookViewPage 组件代码
class WorkbookViewPage extends StatefulWidget { String downloadedURL; String workbookName; WorkbookViewPage(this.downloadedURL, this.workbookName, {super.key}); @override State<WorkbookViewPage> createState() => _WorkbookViewPageState(); } class _WorkbookViewPageState extends State<WorkbookViewPage> { final GlobalKey<SfPdfViewerState> _pdfViewerKey = GlobalKey(); late PdfViewerController _pdfViewerController; int _lastClosedPage = 1; bool _memoMode = false; // Widget Key GlobalKey _viewKey = GlobalKey(); // pen size control widget Entry bool isOverlayVisible = false; OverlayEntry? entry; // Listener for stylus bool isPenTouched = false; /// Hide Overlay Widget void hideOverlay() { entry?.remove(); entry = null; isOverlayVisible = false; } /// get Last closed page of workbook void _loadLastClosedPage() async { final pagePrefs = await SharedPreferences.getInstance(); _lastClosedPage = pagePrefs.getInt('${widget.workbookName}') ?? 1; } @override void initState() { super.initState(); _pdfViewerController = PdfViewerController(); String workbookName = '${widget.workbookName}'; _loadLastClosedPage(); print("======== Loaded workbook name = ${workbookName} =========="); } @override Widget build(BuildContext context) { /// Drawing Provider var p = context.read<DrawingProvider>(); /// set _lastClosedPage void countLastClosedPage() async { final pagePrefs = await SharedPreferences.getInstance(); setState(() { pagePrefs.setInt('${widget.workbookName}', _lastClosedPage); _lastClosedPage = (pagePrefs.getInt('${widget.workbookName}') ?? 1); }); } return Scaffold( appBar: AppBar( backgroundColor: Colors.white, centerTitle: true, title: buildPenFuncWidgets(p, hideOverlay, context), elevation: 0, ), body: InteractiveViewer( panEnabled: _memoMode ? false : true, maxScale: 3, child: Stack( children: [ IgnorePointer( ignoring: true, child: SfPdfViewer.network( widget.downloadedURL, controller: _pdfViewerController, key: _pdfViewerKey, pageLayoutMode: PdfPageLayoutMode.single, enableDoubleTapZooming: false, // Save the last closed page number onPageChanged: (details) { _lastClosedPage = details.newPageNumber; countLastClosedPage(); }, onDocumentLoaded: (details) { _pdfViewerController.jumpToPage(_lastClosedPage); }, ), ), // PDF view under drawing layer WorkbookDrawingPage(), //buildPenFuncWidgets(p, hideOverlay, context), ], ), ), ); } }
buildPenFuncWidgets 方法及Provider的changePenMode方法
Container buildPenFuncWidgets( DrawingProvider p, void Function() hideOverlay, BuildContext context) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), color: Colors.white), height: 50, child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { // 点击PenWidget按钮时,调用DrawingProvider的changePenMode方法,_memoMode应变为true p.changePenMode(); _memoMode = p.penMode; print("_memoMode = ${_memoMode}"); }, child: PenWidget(p: p)), ], ), ); } // DrawingProvider中的changePenMode方法 void changePenMode() { _penMode = !_penMode; // _selectedColor = _selectedColor.withOpacity(1); print("penMode is called : ${penMode} "); _eraseMode = false; _highlighterMode = false; notifyListeners(); }
解决方案
方案1:修复InteractiveViewer的panEnabled不生效问题
问题核心是修改_memoMode后未触发组件重建,导致InteractiveViewer的panEnabled属性未更新。
修复方式1:手动调用setState()
在PenWidget的点击回调中,更新_memoMode后调用setState():
onTap: () { p.changePenMode(); setState(() { _memoMode = p.penMode; }); print("_memoMode = ${_memoMode}"); },
修复方式2:利用Provider自动监听状态(更推荐)
因为你使用了Provider管理状态,直接用context.watch<DrawingProvider>()监听状态变化,无需维护本地的_memoMode变量:
@override Widget build(BuildContext context) { // 用watch替代read,自动监听Provider状态变化 var p = context.watch<DrawingProvider>(); // ...其他代码 return Scaffold( // ... body: InteractiveViewer( // 直接使用Provider的penMode状态,无需三元表达式也能清晰表达逻辑 panEnabled: !p.penMode, maxScale: 3, child: Stack(/*...*/), ), ); }
方案2:不用InteractiveViewer的自定义实现
如果不想依赖InteractiveViewer,可以通过GestureDetector+Transform+ScaleGestureDetector自定义缩放和拖拽逻辑,精准控制权限:
class _WorkbookViewPageState extends State<WorkbookViewPage> { // 缩放比例和偏移量 double _scale = 1.0; Offset _offset = Offset.zero; final GlobalKey<SfPdfViewerState> _pdfViewerKey = GlobalKey(); late PdfViewerController _pdfViewerController; int _lastClosedPage = 1; // ...其他变量和初始化方法 @override Widget build(BuildContext context) { var p = context.watch<DrawingProvider>(); // ...其他代码 return Scaffold( appBar: /*...*/, body: GestureDetector( // 仅在非memo模式允许拖拽 onPanUpdate: p.penMode ? null : (details) { setState(() { _offset += details.delta; }); }, child: ScaleGestureDetector( onScaleUpdate: (details) { setState(() { // 限制缩放范围在1~3倍 _scale = details.scale.clamp(1.0, 3.0); }); }, child: Transform( transform: Matrix4.identity() ..translate(_offset.dx, _offset.dy) ..scale(_scale), child: Stack( children: [ IgnorePointer( ignoring: true, child: SfPdfViewer.network(/*...*/), ), WorkbookDrawingPage(), ], ), ), ), ), ); } }
这个方案完全自定义手势逻辑,能更灵活地控制拖拽和缩放的启用时机。
内容的提问来源于stack exchange,提问作者KRBILN
相关产品推荐
相关产品推荐

