大家好,我是考100分的小小码 ,祝大家学习进步,加薪顺利呀。今天说一说flutter组件库demo_flutter widget,希望您对编程的造诣更进一步.
0、前言
Flutter更新到1.20,出了一个新组件
InteractiveViewer
,主要对移动、缩放的手势交互进行封装,简化使用。
移动 | 缩放 |
---|---|
家族: StatefulWidget
源码行数: 1207
依赖的核心组件: GestureDetector、Transform、ClipRect、OverflowBox
- 此组件已加入FlutterUnit,欢迎star~
1 | 2 | 3 |
---|---|---|
1、子组件的移动
属性名 | 类型 | 默认值 | 简介 |
---|---|---|---|
alignPanAxis | bool | false | 沿轴拖动 |
boundaryMargin | EdgeInsets | EdgeInsets.zero | 边界边矩 |
panEnabled | bool | true | 是否可平移 |
child | Widget | @required | 子组件 |
移动 | 缩放 |
---|---|
- 如左图,灰色区域是InteractiveViewer的上级区域。
boundaryMargin
是可移动的限定边距。默认是EdgeInsets.zero,即被定死,不能移动panEnabled
可指定是否支持移动,默认为truealignPanAxis
指定是否沿轴拖动,默认为false(左图)。当为true时,按下后只能沿某个轴向进行拖动(如右图)
示例代码
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
示例代码
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, 子组件将被赋予无限的约束。
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<TableRow> buildRows(int rowCount, int columnCount) {
return <TableRow>[
for (int row = 0; row < rowCount; row += 1)
TableRow(
children: <Widget>[
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
是相对于父容器区域左上角的偏移量。
ScaleStartDetails(
focalPoint: Offset(306.0, 168.7),
localFocalPoint: Offset(50.4, 63.7)
)
onInteractionUpdate
当手指滑动时,onInteractionUpdate 会回调
ScaleUpdateDetails
对象
focalPoint
是相对于屏幕左上角的偏移量。
localFocalPoint
是相对于父容器区域左上角的偏移量。
scale
缩放量。
horizontalScale
水平缩放量。
verticalScale
竖直缩放量。
rotation
旋转量。——这里说明能监听到旋转量
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
水平和竖直方向的速度量。
onInteractionEnd----
ScaleEndDetails(velocity: Velocity(0.0, 0.0))
示例代码
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
的强大,你懂得…
class TransformationController extends ValueNotifier<Matrix4> {
示例代码
class InteractiveViewerDemo3 extends StatefulWidget {
@override
_InteractiveViewerDemo3State createState() => _InteractiveViewerDemo3State();
}
class _InteractiveViewerDemo3State extends State<InteractiveViewerDemo3>
with SingleTickerProviderStateMixin {
final TransformationController _transformationController =
TransformationController();
Animation<Matrix4> _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
。
@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,
),
);
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/13557.html