You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 18:35:23