前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【-Flutter组件篇- 】1.20新增组件InteractiveViewer

【-Flutter组件篇- 】1.20新增组件InteractiveViewer

作者头像
张风捷特烈
发布2020-10-16 11:07:44
1.8K0
发布2020-10-16 11:07:44
举报
文章被收录于专栏:Android知识点总结
0、前言

Flutter更新到1.20,出了一个新组件InteractiveViewer,主要对移动、缩放的手势交互进行封装,简化使用。

移动
移动
缩放
缩放
代码语言:javascript
复制
家族: StatefulWidget
源码行数: 1207
依赖的核心组件: GestureDetector、Transform、ClipRect、OverflowBox

1

2

3

1
1
2
2
3
3

1、子组件的移动

属性名

类型

默认值

简介

alignPanAxis

bool

false

沿轴拖动

boundaryMargin

EdgeInsets

EdgeInsets.zero

边界边矩

panEnabled

bool

true

是否可平移

child

Widget

@required

子组件


移动

缩放

移动
移动
缩放
缩放
  • 如左图,灰色区域是InteractiveViewer的上级区域。
  • boundaryMargin是可移动的限定边距。默认是EdgeInsets.zero,即被定死,不能移动
  • panEnabled可指定是否支持移动,默认为true
  • alignPanAxis 指定是否沿轴拖动,默认为false(左图)。当为true时,按下后只能沿某个轴向进行拖动(如右图)

  • 示例代码
代码语言:javascript
复制
class InteractiveViewerDemo extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 150,
      color: Colors.grey.withAlpha(33),
      child: InteractiveViewer(
//        alignPanAxis: true,
        panEnabled: true,
        boundaryMargin: EdgeInsets.all(40.0),
        child: Container(
          child: Image.asset('assets/images/caver.jpeg'),
        ),
      ),
    );
  }
}

复制代码

2、子组件缩放

属性名

类型

默认值

简介

maxScale

double

2.5

最大放大倍数

minScale

double

0.8

最小缩小倍数

scaleEnabled

bool

true

是否可缩放

  • scaleEnabled为是否开启缩放,maxScale和minScale分别确定放大缩小的倍数限值。

估计百分之九十的人都很难触发缩放效果,昨天在群里讨论后。Alex给出了手势触发情况: 先把一只手指放上去,边移动边放第二只。 同时提出了一个issues: [InteractiveViewer] Hard to scale when two fingers tap down at the same


  • 示例代码
代码语言:javascript
复制
class InteractiveViewerDemo extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 150,
      color: Colors.grey.withAlpha(33),
      child: InteractiveViewer(
//        alignPanAxis: true,
        boundaryMargin: EdgeInsets.all(40.0),
        maxScale: 2.5,
        minScale: 0.3,
        panEnabled: true,
        scaleEnabled: true,
        child: Container(
          child: Image.asset('assets/images/caver.jpeg'),
        ),
      ),
    );
  }
}
复制代码

3、constrained属性

属性名

类型

默认值

简介

constrained

bool

true

受约束的


关于constrained属性,源码中给了一个小demo。这里的表格可以上下滚动,左右滑动。constrained默认为true,当子组件比InteractiveViewer区域大时,将constrained设为false, 子组件将被赋予无限的约束。

代码语言:javascript
复制
class InteractiveViewerDemo2 extends StatelessWidget {

  Widget build(BuildContext context) {
    const int _rowCount = 20;
    const int _columnCount = 4;

    return Container(
      width: 300,
      height: 200,
      child: InteractiveViewer(
        constrained: false,
        scaleEnabled: false,
        child: Table(
          columnWidths: <int, TableColumnWidth>{
            for (int column = 0; column < _columnCount; column += 1)
              column: const FixedColumnWidth(150.0),
          },
          children: buildRows(_rowCount, _columnCount),
        ),
      ),
    );
  }

  List buildRows(int rowCount, int columnCount) {
    return [
          for (int row = 0; row < rowCount; row += 1)
            TableRow(
              children: [
                for (int column = 0; column < columnCount; column += 1)
                  Container(
                    margin: EdgeInsets.all(2),
                    height: 50,
                    alignment: Alignment.center,
                    color: _colorful(row,column),
                    child: Text('($row,$column)',style: TextStyle(fontSize: 20,color: Colors.white),),
                  ),
              ],
            ),
        ];
  }

  final colors = [Colors.red,Colors.yellow,Colors.blue,Colors.green];
  final colors2 = [Colors.yellow,Colors.blue,Colors.green,Colors.red];

  _colorful(int row, int column ) => row % 2==0?colors[column]:colors2[column];
}
复制代码

4、回调事件

属性名

类型

默认值

简介

onInteractionEnd

GestureScaleEndCallback

null

交互结束回调

onInteractionStart

GestureScaleStartCallback

null

交互开始回调

onInteractionUpdate

GestureScaleUpdateCallback

null

交互更新回调


  • onInteractionStart 当触碰时,onInteractionStart 会回调ScaleStartDetails对象 focalPoint 是相对于屏幕左上角的偏移量。 localFocalPoint是相对于父容器区域左上角的偏移量。
代码语言:javascript
复制
ScaleStartDetails(
    focalPoint: Offset(306.0, 168.7), 
    localFocalPoint: Offset(50.4, 63.7)
)

  • onInteractionUpdate 当手指滑动时,onInteractionUpdate 会回调ScaleUpdateDetails对象 focalPoint 是相对于屏幕左上角的偏移量。 localFocalPoint是相对于父容器区域左上角的偏移量。 scale缩放量。 horizontalScale水平缩放量。 verticalScale竖直缩放量。 rotation旋转量。------ 这里说明能监听到旋转量
代码语言:javascript
复制
onInteractionUpdate----
ScaleUpdateDetails(
    focalPoint: Offset(6.4, 13.7), 
    localFocalPoint: Offset(6.4, 13.7),
    scale: 1.0,
    horizontalScale: 1.0, 
    verticalScale: 1.0, 
    rotation: 0.0
)

  • onInteractionEnd 当手指滑动时,onInteractionEnd 会回调ScaleEndDetails对象 velocity 水平和竖直方向的速度量。
代码语言:javascript
复制
onInteractionEnd----
ScaleEndDetails(velocity: Velocity(0.0, 0.0))

  • 示例代码
代码语言:javascript
复制
class InteractiveViewerDemo extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 150,
      color: Colors.grey.withAlpha(33),
      child: InteractiveViewer(
        boundaryMargin: EdgeInsets.all(40.0),
        maxScale: 2.5,
        minScale: 0.3,
        panEnabled: true,
        scaleEnabled: true,
        child: Container(
          child: Image.asset('assets/images/caver.jpeg'),
        ),
        onInteractionStart: _onInteractionStart,
        onInteractionUpdate: _onInteractionUpdate,
        onInteractionEnd: _onInteractionEnd,
      ),
    );
  }

  void _onInteractionStart(ScaleStartDetails details) {
    print('onInteractionStart----' + details.toString());
  }

  void _onInteractionUpdate(ScaleUpdateDetails details) {
    print('onInteractionUpdate----' + details.toString());
  }

  void _onInteractionEnd(ScaleEndDetails details) {
    print('onInteractionEnd----' + details.toString());
  }
}

5.变换控制器 transformationController

属性名

类型

默认值

简介

transformationController

TransformationController

null

变化控制器


可以通过transformationController进行变换控制,如上面通过按钮进行复位、移动 TransformationController是一个Matrix4泛型的ValueNotifier 所以可以通过改变TransformationController.value来对子组件进行高级的变换操作,Matrix4的强大,你懂得...

代码语言:javascript
复制
class TransformationController extends ValueNotifier {

  • 示例代码
代码语言:javascript
复制
class InteractiveViewerDemo3 extends StatefulWidget {
  @override
  _InteractiveViewerDemo3State createState() => _InteractiveViewerDemo3State();
}

class _InteractiveViewerDemo3State extends State
    with SingleTickerProviderStateMixin {
  final TransformationController _transformationController =
      TransformationController();
  Animation _animationReset;
  AnimationController _controllerReset;

  void _onAnimateReset() {
    _transformationController.value = _animationReset.value;
    if (!_controllerReset.isAnimating) {
      _animationReset?.removeListener(_onAnimateReset);
      _animationReset = null;
      _controllerReset.reset();
    }
  }

  void _animateResetInitialize() {
    _controllerReset.reset();
    _animationReset = Matrix4Tween(
      begin: _transformationController.value,
      end: Matrix4.identity(),
    ).animate(_controllerReset);
    _animationReset.addListener(_onAnimateReset);
    _controllerReset.forward();
  }

  void _animateResetStop() {
    _controllerReset.stop();
    _animationReset?.removeListener(_onAnimateReset);
    _animationReset = null;
    _controllerReset.reset();
  }

  void _onInteractionStart(ScaleStartDetails details) {
    if (_controllerReset.status == AnimationStatus.forward) {
      _animateResetStop();
    }
  }

  @override
  void initState() {
    super.initState();
    _controllerReset = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 400),
    );
  }

  @override
  void dispose() {
    _controllerReset.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Wrap(
      direction: Axis.vertical,
      spacing: 10,
      crossAxisAlignment: WrapCrossAlignment.center,
      alignment: WrapAlignment.center,
      children: [
        Container(
          height: 150,
          color: Colors.grey.withAlpha(33),
          child: InteractiveViewer(
            boundaryMargin: EdgeInsets.all(40),
            transformationController: _transformationController,
            minScale: 0.1,
            maxScale: 1.8,
            onInteractionStart: _onInteractionStart,
            child: Container(
              child: Image.asset('assets/images/caver.jpeg'),
            ),
          ),
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            _buildButton(),
            _buildButton2(),
            _buildButton3(),
          ],
        )
      ],
    );
  }

  Widget _buildButton() {
    return MaterialButton(
        child: Icon(
          Icons.refresh,
          color: Colors.white,
        ),
        color: Colors.green,
        shape: CircleBorder(
          side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),
        ),
        onPressed: _animateResetInitialize);
  }

  var _x = 0.0;

  Widget _buildButton2() {
    return MaterialButton(
        child: Icon(
          Icons.navigate_before,
          color: Colors.white,
        ),
        color: Colors.green,
        shape: CircleBorder(
          side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),
        ),
        onPressed: () {
          var temp = _transformationController.value.clone();
          temp.translate(_x - 4);
          _transformationController.value = temp;
        });
  }

  Widget _buildButton3() {
    return MaterialButton(
        child: Icon(
          Icons.navigate_next,
          color: Colors.white,
        ),
        color: Colors.green,
        shape: CircleBorder(
          side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),
        ),
        onPressed: () {
          var temp = _transformationController.value.clone();
          temp.translate(_x + 4);
          _transformationController.value = temp;
        });
  }
}

6.InteractiveViewer的核心源码

Listener组件 + GestureDetector组件 实现手势交互相关功能及回调 Transform组件通过transformationController的Matrix4进行变换 如果 constrained=false 外会附加一层ClipRect+OverflowBox

代码语言:javascript
复制
  @override
  Widget build(BuildContext context) {
    Widget child = Transform(
      transform: _transformationController.value,
      child: KeyedSubtree(
        key: _childKey,
        child: widget.child,
      ),
    );

    if (!widget.constrained) {
      child = ClipRect(
        child: OverflowBox(
          alignment: Alignment.topLeft,
          minWidth: 0.0,
          minHeight: 0.0,
          maxWidth: double.infinity,
          maxHeight: double.infinity,
          child: child,
        ),
      );
    }

    // A GestureDetector allows the detection of panning and zooming gestures on
    // the child.
    return Listener(
      key: _parentKey,
      onPointerSignal: _receivedPointerSignal,
      child: GestureDetector(
        behavior: HitTestBehavior.opaque, // Necessary when panning off screen.
        onScaleEnd: _onScaleEnd,
        onScaleStart: _onScaleStart,
        onScaleUpdate: _onScaleUpdate,
        child: child,
      ),
    );
  }
}

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0、前言
  • 1、子组件的移动
  • 2、子组件缩放
  • 3、constrained属性
  • 4、回调事件
  • 5.变换控制器 transformationController
  • 6.InteractiveViewer的核心源码
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档