Flutter实战一Flutter聊天应用(二)
随着项目的代码越来越多,我们会碰到各种问题,所以我们需要学习一下如何使用IntelliJ定位并解决问题。我们可以在IntelliJ IDE上调试在模拟器/仿真器或真机设备上运行的Flutter应用程序,使用IntelliJ编辑器可以:
选择一个设备或模拟器来调试应用程序。
查看控制台消息。
在代码中设置断点。
在运行时检查变量并评估表达式。
IntelliJ编辑器在您的应用程序运行时会显示系统日志,并提供了一个调试器UI来处理断点并控制执行流程。还可以使用断点来调试Flutter应用程序:
打开要设置断点的源文件。
找到要设置断点的行,单击它,然后在菜单中选择
Run > Toggle Line Breakpoint
。或者,我们也可以点击行号与代码中间的空白区切换断点。在菜单中选择
Run > Debug
。
IntelliJ编辑器启动调试器UI,并在应用程序到达断点时暂停执行。然后,我们可以使用调试器UI中的控制器来确定错误的原因。
在这篇文章中,我们要创建一个显示用户聊天消息的窗口控件,将创建和组合多个较小的控件。从一个表示单个聊天消息的控件开始,将该控件嵌套在父级可滚动列表中,并将可滚动列表嵌套在基本应用程序脚手架(Scaffold)中。
首先,我们需要一个表示单个聊天消息的控件,定义一个名为ChatMessage
的StatelessWidget
。如下面代码所示,它的build()
方法返回一个Row
控件,它显示一个简单的图形头像来表示发送消息的用户,一个包含发件人姓名的Column
控件和消息的文本。
我们还需要定义_name
变量,我们将使用此变量标记每个聊天消息与发件人的名字。将以下类和变量的定义添加到main.dart
。
const String _name = "hekaiyou";class ChatMessage extends StatelessWidget {ChatMessage({this.text});final String text;@overrideWidget build(BuildContext context) {return new Container(margin: const EdgeInsets.symmetric(vertical: 10.0),child: new Row(crossAxisAlignment: CrossAxisAlignment.start,children: <Widget>[new Container(margin: const EdgeInsets.only(right: 16.0),child: new CircleAvatar(child: new Text(_name[0])),),new Column(crossAxisAlignment: CrossAxisAlignment.start,children: <Widget>[new Text(_name, style: Theme.of(context).textTheme.subhead),new Container(margin: const EdgeInsets.only(top: 5.0),child: new Text(text),)])]));}
}
为使CircleAvatar控件看起来更有个性,我们将_name变量的值的第一个字符传递给子Text控件。我们将使用CrossAxisAlignment.start作为Row构造函数的crossAxisAlignment参数来定位头像和消息,相对于其父窗口控件。
对于头像,父对象是一个Row
控件,其主轴是水平的,因此CrossAxisAlignment.start
会沿垂直轴给出最高位置。对于消息,父对象是一个Column
控件,其主轴是垂直的,因此CrossAxisAlignment.start
将沿着水平轴的最左侧位置的文本对齐。
在头像旁边,垂直对齐两个Text
控件,以便在顶部显示发送人的名字,并在下面显示消息的文本。要设置发送人的名称并使其大于消息文本,您需要使用Theme.of(context)
来获取适当的ThemeData
对象。其textTheme属性可以让我们访问质感设计逻辑样式,比如文字像subhead
,因此您可以避免硬编码字体大小和其他文本属性。
我们尚未为此应用程序指定主题,因此Theme.of(context)
将检索默认的Flutter主题。在稍后的步骤中,我们将覆盖此默认主题,以适应Android与iOS之间不同的应用程式风格。
接下来的我们要做的是获取聊天消息列表,并在UI中显示。此列表是可以滚动的,以便用户可以查看聊天记录。列表还应按时间顺序显示消息,最近的消息显示在可见列表的最下面。
在我们的ChatScreenState
控件定义中,添加名为_messages
的List
成员来表示每个聊天消息。每个列表项都是一个ChatMessage
实例,而且需要将消息列表初始化为空列表。
class ChatScreenState extends State<ChatScreen> {final List<ChatMessage> _messages = <ChatMessage>[];final TextEditingController _textController = new TextEditingController();//...
在当前用户从文本字段发送消息时,我们的应用程序应该将新消息添加到消息列表中。因此需要修改_handleSubmitted()
方法,以执行此行为。
class ChatScreenState extends State<ChatScreen> {//...void _handleSubmitted(String text) {_textController.clear();ChatMessage message = new ChatMessage(text: text,);setState((){_messages.insert(0, message);});}//...
}
您调用setState()
来修改_messages
并让框架知道这部分控件树已经更改,并且需要重建UI。在setState()
中只能执行同步操作,否则框架可能在操作完成之前重新构建窗口控件。
一般来说,可以在一些私有数据改变之后,再使用一个空的闭包调用setState()
。然而,更新setState()
的闭包中的数据是首选的,所以我们要记得调用它。
现在可以显示聊天消息列表了,我们将从_messages
列表中获取ChatMessage
控件,并将它们放在ListView
控件中,作为可滚动列表。
在我们的ChatScreenState
类的build()
方法中,为消息列表添加一个ListView
窗口控件。我们选择ListView.builder
构造函数,因为默认构造函数不会自动检测其children
的突然变化。
class ChatScreenState extends State<ChatScreen> {//...Widget build(BuildContext context) {return new Scaffold(appBar: new AppBar(title: new Text('谈天说地'),),body: new Column(children: <Widget>[new Flexible(child: new ListView.builder(padding: new EdgeInsets.all(8.0),reverse: true,itemBuilder: (_, int index) => _messages[index],itemCount: _messages.length,)),new Divider(height: 1.0),new Container(decoration: new BoxDecoration(color: Theme.of(context).cardColor,),child: _buildTextComposer(),)]));}//...
}
Scaffold
控件的body
属性现在包含传入消息的列表以及输入字段和发送按钮。我们正在使用以下布局控件:
Column
,垂直布置其直接的子控件列表(children
)。Column可以占用多个子窗口控件,这是包括一个滚动列表和一个输入字段的行。Flexible
,作为ListView
的父级。这告诉框架让接收到消息的列表展开以填充Column
高度,而TextField
保持固定的大小。Divider
,在用于显示消息的UI和用于撰写消息的文本输入字段之间绘制水平的横线。Container
,作为文本编辑区的父级,可用于定义背景图像、填充、边距和其他常见布局细节。使用decoration
来创建一个定义背景颜色的新BoxDecoration
对象。在这种情况下,我们使用由默认主题的ThemeData
对象定义的cardColor
,这使得用于组合消息的UI与消息列表有不同的背景。
将参数传递给ListView.builder
构造函数以自定义列表内容和外观:
padding
对于消息文本周围的空白reverse
使ListView
从屏幕底部开始itemCount
指定列表中的消息数itemBuilder
对于在[index]
中构建每个窗口控件的函数。因为我们不需要当前的构建上下文,所以我们可以忽略IndexedWidgetBuilder
的第一个参数。命名参数_
(下划线)是一个表示不会被使用的约定。
Flutter实战一Flutter聊天应用(二)相关推荐
- Flutter实战一Flutter聊天应用(二十)
在上一篇文章<Flutter实战一Flutter聊天应用(十九)>中,我们完成了删除用户的逻辑,就是将会话的有效性设置为false就可以了.那么当会话的有效性为false时,用户再次添加该 ...
- Flutter实战一Flutter聊天应用(汇总)
纸聊 这个应用程序使用Google的Flutter移动框架开发,是一个实时聊天应用程序,为了能专注于APP设计,应用程序的服务端使用Googler的Firebase平台.程序程序的名称为纸聊,意为像传 ...
- 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属性,使其 ...
最新文章
- 老大批评我不要为了“分库分表”而“分库分表”
- SQL Between a and b
- JavaFX 8的弹出式编辑器
- LeetCode 1187. 使数组严格递增(DP)*
- 图解TCPIP-IP 网际协议-IP包
- python源_python更换国内源
- 是该为硬核创新的寒武纪说句公道话了
- 华为鸿蒙2.0安装包,华为鸿蒙2.0系统安装包
- Android修改开机动画
- FreeMarker模板引擎实现页面静态化
- SVN Cleanup之后显示Skipped remains conflicted / 提交missing状态处理
- codecademy
- 华为服务器cpu位置,服务器cpu参数详解
- 常用复原reast.css
- 使用GCD 转自 Posted by 唐巧
- TOP20W词库,双十一,直通车,用超级推荐获取手淘流量的方法——上篇
- 搜索量过低百度和谷歌竞价账户分别是怎么处理的
- Android中通过USB接口与和PC进行通讯的demo程序
- 有人考过阿里云大数据助理工程师认证(ACA)从而能分享心得攻略吗?
- matlab小作业答案,MATLAB编程作业答案.doc
热门文章
- 数据结构 3-1-1 栈
- Evolution Game DP
- 数据可视化——利用pandas和seaborn绘图基础
- 软件版本 —— Alpha、Beta、RC、Stable版本的区别
- Android studio 去除软件运行时顶部原有的蓝色/绿色框
- 实验8.3 C++标准模板库(STL)中的双向队列类(deque)
- 《Win测试的学习笔记》——Introduction
- PIL image.convert('RGB')在数据生成中真的比较好吗?
- 移远EC600S-CN (4) - MQTT接入阿里云
- stm32F1的 PA13/PA14/PA15/PB3/PB4 作为普通引脚使用