Flutter实战一Flutter聊天应用(十一)
我们的应用程序现在可以点击查看图像,但还没有实现查看时放大、缩小与移动图像。要实现这个功能,需要监听用户在图像上的操作,并调用相应的回调处理用户操作。我们先将Transform
控件从_ImageZoomableState
的build
方法中拆分出来。在_ImageZoomableState
类中添加_drawImage
方法。
class _ImageZoomableState extends State<ImageZoomable> {//...Widget _drawImage() {if (_image == null) {return null;}return new Transform(transform: new Matrix4.diagonal3Values(1.0, 1.0, 1.0),child: new CustomPaint(painter: new _ImageZoomablePainter(image: _image,offset: Offset.zero,zoom: 1.0,)));}//...
}
修改_ImageZoomableState
的build
方法,使用GestureDetector
控件包装_drawImage
方法中的Transform
控件,我们可以使用GestureDetector
控件监听用户的各种操作手势。
class _ImageZoomableState extends State<ImageZoomable> {//...@overrideWidget build(BuildContext context) {return new GestureDetector(child: _drawImage(),);}//...
}
图片缩放需要一个比例,在ImageZoomable
类定义中声明一个成员变量scale
来设置图像的显示比例,同时为变量scale
设置默认值,使scale
变量为可选参数。
class ImageZoomable extends StatefulWidget {ImageZoomable(this.image, {Key key, this.scale = 2.0}) : super(key: key);final ImageProvider image;final double scale;@override_ImageZoomableState createState() => new _ImageZoomableState(scale);
}
在_ImageZoomableState
类定义中接收ImageZoomable
类成员变量scale
,并声明三个Offset
(不可变的二维浮点偏移量)类型的成员变量。_startingFocalPoint
变量存储初始焦点值,_previousOffset
变量存储历史偏移量,_offset
变量使用静态方法Offset.zero
为其赋值,Offset
类的静态方法zero
返回一个零幅度偏移量,即常量Offset(0.0, 0.0)
。_zoom
和_previousZoom
则是用于存储缩放值与存储历史缩放值。
class _ImageZoomableState extends State<ImageZoomable> {_ImageZoomableState(this._scale);final double _scale;ImageStream _imageStream;ui.Image _image;Offset _startingFocalPoint;Offset _previousOffset;Offset _offset = Offset.zero;double _zoom = 1.0;double _previousZoom;//...
}
现在我们可以修改_drawImage
方法中Transform
控件的硬编码。
class _ImageZoomableState extends State<ImageZoomable> {
//...Widget _drawImage() {//...return new Transform(transform: new Matrix4.diagonal3Values(_scale, _scale, _scale),child: new CustomPaint(painter: new _ImageZoomablePainter(image: _image,offset: _offset,zoom: _zoom / _scale,)));}//...
}
现在回到main.dart
文件中来,修改ImageZoomable
类的调用。
class ChatMessage extends StatelessWidget {//...@overrideWidget build(BuildContext context) {return new SizeTransition(//...onTap: (){Navigator.of(context).push( new MaterialPageRoute<Null>(builder: (BuildContext context) {return new ImageZoomable(new NetworkImage(snapshot.value['imageUrl']));}));},//...);}
}
我们要在用户对图像操作之前获取图像偏移位置相关的参数,也就是之前声明一些_ImageZoomableState
类成员变量。我们需要在_ImageZoomableState
类中增加_handleScaleStart
方法作为手势监听器onScaleStart
的回调函数。
手势监听器onScaleStart
触发回调的条件:与屏幕接触的指针已经建立起了一个焦点时,此时初始图像显示比例为1.0。参数details
的类型为ScaleStartDetails
,该类型存储手势监听器onScaleStart
的详细信息,其focalPoint
属性以全局坐标方式返回与屏幕接触的指针初始焦点。
class _ImageZoomableState extends State<ImageZoomable> {//...void _handleScaleStart(ScaleStartDetails details) {if (_image == null) {return;}_startingFocalPoint = details.focalPoint / _scale;_previousOffset = _offset;_previousZoom = _zoom;}//...
}
现在我们在_ImageZoomableState
类的GestureDetector
控件中添加一个onScaleStart
监听器,把上面的_handleScaleStart
方法作为回调。
class _ImageZoomableState extends State<ImageZoomable> {//...@overrideWidget build(BuildContext context) {return new GestureDetector(child: _drawImage(),onScaleStart: _handleScaleStart,);}//...
}
在用户对图像操作时,我们需要跟踪焦点的移动,并重新绘制图像。因此我们需要在_ImageZoomableState
类中增加_handleScaleUpdate
方法作为手势监听器onScaleUpdate
的回调函数,手势监听器onScaleUpdate
会监听焦点的变化,并在焦点变化时调用回调处理。
_handleScaleUpdate
方法的参数details
的类型为ScaleUpdateDetails
,该类型存储手势监听器onScaleUpdate
的详细信息,其focalPoint
属性以全局坐标方式返回与屏幕接触的指针焦点。
void _handleScaleUpdate(Size size, ScaleUpdateDetails details) {if (_image == null) {return;}double newZoom = _previousZoom * details.scale;bool tooZoomedIn = _image.width * _scale / newZoom <= size.width ||_image.height * _scale / newZoom <= size.height || newZoom <= 0.8;if (tooZoomedIn) {return;}setState(() {_zoom = newZoom;final Offset normalizedOffset = (_startingFocalPoint - _previousOffset) / _previousZoom;_offset = details.focalPoint / _scale - normalizedOffset * _zoom;});}
上面定义了两个局部变量,newZoom
存储最新缩放值,tooZoomedIn
变量判断图像是否过于放大或缩小。如果tooZoomedIn
变量为真时,则不会重新绘制图像。局部变量normalizedOffset
用于确保焦点下方的图像保持在相同位置的前提下放大图像。
现在我们在_ImageZoomableState
类的GestureDetector
控件中添加一个onScaleUpdate
监听器,把上面的_handleScaleUpdate
方法作为回调。
class _ImageZoomableState extends State<ImageZoomable> {//...@overrideWidget build(BuildContext context) {return new GestureDetector(child: _drawImage(),onScaleStart: _handleScaleStart,onScaleUpdate: (d) => _handleScaleUpdate(context.size, d),);}//...
}
目前用户在查看图像时,需要点击系统的后退按钮才能返回到聊天屏幕。在大部分应用程序中,在查看图像时点击即可返回聊天屏幕,我们也可以这么做。在ImageZoomable
类定义中声明一个GestureTapCallback
类型的成员变量onTap
,GestureTapCallback
类型表示点击发生时的回调函数。
class ImageZoomable extends StatefulWidget {ImageZoomable(this.image, {Key key, this.scale = 2.0, this.onTap}) : super(key: key);final ImageProvider image;final double scale;final GestureTapCallback onTap;@override_ImageZoomableState createState() => new _ImageZoomableState(scale);
}
然后我们需要在_ImageZoomableState
类的GestureDetector
控件中添加一个onTap
监听器,并把ImageZoomable
类的成员变量onTap
作为回调。
class _ImageZoomableState extends State<ImageZoomable> {//...@overrideWidget build(BuildContext context) {return new GestureDetector(child: _drawImage(),onTap: widget.onTap,onScaleStart: _handleScaleStart,onScaleUpdate: (d) => _handleScaleUpdate(context.size, d),);}//...
}
最后回到main.dart
文件中来,修改ImageZoomable
类的调用。
class ChatMessage extends StatelessWidget {//...@overrideWidget build(BuildContext context) {return new SizeTransition(//...onTap: (){Navigator.of(context).push( new MaterialPageRoute<Null>(builder: (BuildContext context) {return new ImageZoomable(new NetworkImage(snapshot.value['imageUrl']),onTap: (){Navigator.of(context).pop();},);}));},//...);}
}
现在图像查看,且查看时可以缩放、移动图像的功能已经完成了!
Flutter实战一Flutter聊天应用(十一)相关推荐
- Flutter实战一Flutter聊天应用(汇总)
纸聊 这个应用程序使用Google的Flutter移动框架开发,是一个实时聊天应用程序,为了能专注于APP设计,应用程序的服务端使用Googler的Firebase平台.程序程序的名称为纸聊,意为像传 ...
- Flutter实战一Flutter聊天应用(二十)
在上一篇文章<Flutter实战一Flutter聊天应用(十九)>中,我们完成了删除用户的逻辑,就是将会话的有效性设置为false就可以了.那么当会话的有效性为false时,用户再次添加该 ...
- Flutter实战一Flutter聊天应用(十六)
在上一篇文章<Flutter实战一Flutter聊天应用(十五)>中,我们完成了登陆屏幕.在用户登陆成功后,会在本地创建一个LandingInformation文件,以使应用程序在启动时可 ...
- Flutter实战一Flutter聊天应用(十五)
在上一篇文章<Flutter实战一Flutter聊天应用(十四)>中,我们完成了注册屏幕.为了保持应用程序入口只有一个,即登陆屏幕,用户注册完成之后会返回手机号码.密码到登陆屏幕,让用户点 ...
- Flutter实战一Flutter聊天应用(五)
我们的应用程序现在已经有了一个好看的UI,但是我们还没有一个后端.所以我们要买一个云服务器,然后再安装数据库?当然不是!我们可以使用Firebase平台作为后端,那么Firebase是什么呢? Fir ...
- Flutter实战一Flutter聊天应用(二十一)
在这一系列的前二十篇文章里,我们已经完成了最主要的添加.删除好友,并与好友聊天,还可以发送图片的功能.这一篇文章会完成个人资料与设置相关的功能,并将应用发布上线. 之前设置了个人资料的入口按钮,现在我 ...
- Flutter实战一Flutter聊天应用(十九)
在上一篇文章中,我们完成了聊天列表的用户界面与功能代码.在用户添加完会话后,聊天列表会增加对应的会话项,通过点击会话项,可以进入聊天屏幕.在这一篇文章中,我们主要是修改lib/chat_screen. ...
- Flutter实战一Flutter聊天应用(十八)
在上一篇文章中,我们完成了基本的添加聊天功能,但是还没有在聊天列表显示添加的新聊天,在这篇文章中我们将实现这个功能--在聊天列表中展示所有的聊天. 首先,我们在/lib目录下新建一个group_cha ...
- Flutter实战一Flutter聊天应用(十)
首先,我们要修复一下之前几篇文章中存在的缺陷.在发送超过两行的消息时,屏幕上显示的消息不会自动换行,会超出最大宽度.我们可以通过将Text包装在Container控件中,再添加一个width属性,使其 ...
最新文章
- 华为选拔人才的五个素质
- android模糊查询listview数据_ListView的简单应用(一)
- Docker进阶-快速扩容
- “睡服”面试官系列第十七篇之Reflect(建议收藏学习)
- 软件测试nodejs面试题,nodejs单元测试和性能测试
- selenium + python自动化测试unittest框架学习(一)selenium原理及应用
- wordpress 运行_如何为您的教室设置和运行WordPress
- 有关Activity的Launch mode 以及Intent的setFlags(转载)
- 项目的ip地址更改,用git从远程提取代码出现错误,提示为 network error connection timed out...
- 三坐标测量圆直径_多台三坐标测量机联动测量方法的研究
- 计算机辅助设计工业产品cad竞赛试题,2017工业产品设计CAD比赛试题
- 在docker中配置apt工具与python的源均为国内源
- vue路由(router)设置:父路由默认选中第一个子路由,切换子路由让父路由高亮不会消失
- LED的基本操作(138译码器 573锁存器)
- 安卓一键新机_「科技犬」三星华为频发新品对标iPhone12,稳固安卓生态
- 报告丨前瞻产业研究院:2019年中国大数据行业研究报告
- windows 配置中科大的 Rust 下载云,提高下载速度
- R: ggplot2图片的布局排版
- 【C++课程设计项目】歌手评分系统(代码量1500行含设计文档)
- 应广单片机adc_台湾应广单片机 单片机PMC131 带12位ADC、采用FPPATM技术