【问题标题】:Flutter GestureDetector: How to pinch in/out or zoom in/out Text using two fingers?Flutter GestureDetector:如何用两根手指捏合/缩小或放大/缩小文本?
【发布时间】:2019-08-21 17:42:26
【问题描述】:

我正在创建一个文本字段,例如 Text 或 RichText。之后,我想使用捏合来放大/缩小文本的大小。目前,我尝试实现GestureDetector,但它也可以用一根手指放大/缩小。而且很难瞄准夹点检测。有时会结冰。我添加了一段视频,显示捏住后它会冻结并突然变大。第二个视频是仅当我用一根手指点击文本并移动到左上角时图像才放大的情况。理想的实现是检测捏合和放大/缩小所有文本区域。当我只用一根手指时禁用缩放。您能否给我一些提示、链接或代码如何解决或在哪里可以找到解决方案?

body: GestureDetector(
  onScaleUpdate: (details) {
    setState(() {
      _textSize =
          _initTextSize + (_initTextSize * (details.scale * .35));
    });
  },
  onScaleEnd: (ScaleEndDetails details) {
    setState(() {
      _initTextSize = _textSize;
    });
  },
  child: Center(
      child: SizedBox(
    height: _textSize,
    child: FittedBox(
      child: Text("Test"),
    ),
  ))),

【问题讨论】:

标签: dart flutter gesture pinchzoom


【解决方案1】:

解决方案:两指放大和缩小。

  import 'package:flutter/material.dart';
  import 'package:matrix_gesture_detector/matrix_gesture_detector.dart';

  class TransformText extends StatefulWidget {
    TransformText({Key key}) : super(key: key); // changed

    @override
    _TransformTextState createState() => _TransformTextState();
  }

  class _TransformTextState extends State<TransformText> {
    double scale = 0.0;

    @override
    Widget build(BuildContext context) {
      final ValueNotifier<Matrix4> notifier = ValueNotifier(Matrix4.identity());

      return Scaffold(
        appBar: AppBar(
          title: Text('Single finger Rotate text'), // changed
        ),
        body: Center(
          child: MatrixGestureDetector(
            onMatrixUpdate: (m, tm, sm, rm) {
              notifier.value = m;
            },
            child: AnimatedBuilder(
              animation: notifier,
              builder: (ctx, child) {
                return Transform(
                  transform: notifier.value,
                  child: Center(
                    child: Stack(
                      children: <Widget>[
                        Container(
                          color: Colors.red,
                          padding: EdgeInsets.all(10),
                          margin: EdgeInsets.only(top: 50),
                          child: Transform.scale(
                            scale:
                                1, // make this dynamic to change the scaling as in the basic demo
                            origin: Offset(0.0, 0.0),
                            child: Container(
                              height: 100,
                              child: Text(
                                "Two finger to zoom!!",
                                style:
                                    TextStyle(fontSize: 26, color: Colors.white),
                              ),
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                );
              },
            ),
          ),
        ),
      );
    }
  }

【讨论】:

  • 怎样才能只缩放不旋转?
【解决方案2】:

在具有这些配置的 Stateful 小部件中

double _scaleFactor = 1.0;
double _baseScaleFactor = 1.0;

并且仅在更新时使用setState,在RichTexttextScaleFactor 属性上使用scaleFactor

只有一个 setState 来重建小部件并在缩放开始时存储初始因子

GestureDetector(
  onScaleStart: (details) {
    _baseScaleFactor = _scaleFactor;
  },
  onScaleUpdate: (details) {
    setState(() {
      _scaleFactor = _baseScaleFactor * details.scale;
    });
  },
  child: Container(
    height: MediaQuery.of(context).size.height,
    width: MediaQuery.of(context).size.width,
    color: Colors.red,
    child: Center(
      child: Text(
        'Test',
        textScaleFactor: _scaleFactor,
      ),
    ),
  ),
);

我放的高度和宽度只是为了扩展和模拟手势检测器的区域。

【讨论】:

    【解决方案3】:

    完整代码。希望对您有所帮助。

    import 'package:flutter/material.dart';
    import 'package:flutter/foundation.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final appTitle = 'Demo';
    
        return MaterialApp(
          title: appTitle,
          home: MyHomePage(title: appTitle),
        );
      }
    }
    
    class MyHomePage extends StatelessWidget {
      final String title;
    
      MyHomePage({Key key, this.title}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(
              title: Text(title),
            ),
            body: TransformText());
      }
    }
    
    class TransformText extends StatefulWidget {
      TransformText({Key key}) : super(key: key); // changed
    
      @override
      _TransformTextState createState() => _TransformTextState();
    }
    
    class _TransformTextState extends State<TransformText> {
      double scale = 0.0;
      double _scaleFactor = 1.0;
      double _baseScaleFactor = 1.0;
      double _savedVal = 1.0;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('GestureDetector Test'), // changed
          ),
          body: Column(
            children: <Widget>[
              RaisedButton(
                  child: Text('get'),
                  onPressed: () {
                    _savedVal = _scaleFactor;
                  }),
              RaisedButton(
                  child: Text('set'),
                  onPressed: () {
                    setState(() {
                      _scaleFactor = _savedVal;
                    });
                  }),
              Expanded(
                child: Center(
                    child: GestureDetector(
                  behavior: HitTestBehavior.translucent,
                  onScaleStart: (details) {
                    _baseScaleFactor = _scaleFactor;
                  },
                  onScaleUpdate: (details) {
                    setState(() {
                      _scaleFactor = _baseScaleFactor * details.scale;
                    });
                  },
                  child: Container(
                    height: MediaQuery.of(context).size.height,
                    width: MediaQuery.of(context).size.width,
                    child: Center(
                      child: Text(
                        'Test',
                        textScaleFactor: _scaleFactor,
                      ),
                    ),
                  ),
                )),
              ),
            ],
          ),
        );
      }
    }
    

    【讨论】:

      【解决方案4】:

      Google 软件工程师 Gary Qian 和 Chris Yang 在他们的 Google Developer Days 演讲中展示了这一点。视频可在此处观看:

      那里的代码与此处的其他一些答案相似,但他们特别添加了一个夹子,使其不会变得太大或太小。

      以下是他们的可扩展文本气泡的摘要:

      因为即使单指触摸仍会调用缩放,所以我添加了对scaleUpdateDetails.scale == 1.0 的检查。这意味着如果规模没有变化,用户界面将不会更新。

      class Bubble extends StatefulWidget {
        @override
        _BubbleState createState() => _BubbleState();
      }
      
      class _BubbleState extends State<Bubble> {
        double _fontSize = 20;
        final double _baseFontSize = 20;
        double _fontScale = 1;
        double _baseFontScale = 1;
      
        @override
        Widget build(BuildContext context) {
          return GestureDetector(
            onScaleStart: (ScaleStartDetails scaleStartDetails) {
              _baseFontScale = _fontScale;
            },
            onScaleUpdate: (ScaleUpdateDetails scaleUpdateDetails) {
              // don't update the UI if the scale didn't change
              if (scaleUpdateDetails.scale == 1.0) {
                return;
              }
              setState(() {
                _fontScale = (_baseFontScale * scaleUpdateDetails.scale).clamp(0.5, 5.0);
                _fontSize = _fontScale * _baseFontSize;
              });
            },
            child: ...
              // descendant with a Text widget that uses the _fontSize
          );
        }
      }
      

      注意事项:

      • 使用StatefulWidget,以便随时存储当前字体大小和比例
      • 使用两个附加变量来记住原始字体大小以及捏合开始时的比例
      • 将 Text 小部件包装在 GestureDetector
      • 将原始比例保存在onScaleStart
      • 计算新字体大小onScaleUpdate
      • 使用setState 重建具有新尺寸的小部件

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-06-20
        • 2021-07-26
        • 1970-01-01
        • 2012-04-18
        • 2018-07-31
        • 1970-01-01
        • 2011-06-21
        • 1970-01-01
        相关资源
        最近更新 更多