首先,我要对上一篇文章进行一点补充,在添加动画效果后,需要重新启动应用程序。使用重新启动而不是热重新加载,因为需要清除任何没有动画控制器的现有消息。

目前在我们的应用程序中,即使输入字段中没有文本,也会启用“发送”按钮,我们可以根据该字段是否包含要发送的文本来决定是否启用发送按钮,并更改按钮的外观。定义_isComposing,一个私有成员变量,只要用户在输入字段中键入,该变量就是true

class ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {final List<ChatMessage> _messages = <ChatMessage>[];final TextEditingController _textController = new TextEditingController();bool _isComposing = false;//...

要在用户与该字段交互时通知文本的更改,需要将onChanged回调传递给TextField构造函数。当它的值随着字段的当前值而变化时,TextField将调用此方法。在我们的onChanged回调中,当字段包含一些文本时,调用setState()_isComposing的值更改为true。然后当_isComposingfalse时,将onPressed参数修改为null

class ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {//...Widget _buildTextComposer() {return new IconTheme(data: new IconThemeData(color: Theme.of(context).accentColor),child: new Container(margin: const EdgeInsets.symmetric(horizontal: 8.0),child: new Row(children: <Widget> [new Flexible(child: new TextField(controller: _textController,onChanged: (String text) {setState((){_isComposing = text.length > 0;});},onSubmitted: _handleSubmitted,decoration: new InputDecoration.collapsed(hintText: '发送消息'),)),new Container(margin: new EdgeInsets.symmetric(horizontal: 4.0),child: new IconButton(icon: new Icon(Icons.send),onPressed: _isComposing ?() => _handleSubmitted(_textController.text) : null),)])));//...
}

当文本字段被清除时,修改_handleSubmitted_isComposing更新为false

class ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {//...void _handleSubmitted(String text) {_textController.clear();setState((){_isComposing = false;});ChatMessage message = new ChatMessage(text: text,animationController: new AnimationController(duration: new Duration(milliseconds: 300),vsync: this));setState((){_messages.insert(0, message);});message.animationController.forward();}//...
}

_isComposing变量现在控制发送按钮的行为和视觉外观。如果用户在文本字段中键入字符串,则_isComposingtrue,按钮的颜色设置为Theme.of(context).accentColor。当用户按下按钮时,系统调用_handleSubmitted()。如果用户在文本字段中不输入任何内容,则_isComposingfalse,该控件的onPressed属性设置为null,禁用发送按钮。框架将自动将按钮的颜色更改为Theme.of(context).disabledColor

接下来,为了让应用程序的UI具有自然的外观,我们可以为TalkcasuallyApp类的build()方法添加一个主题和一些简单的逻辑。在此步骤中,我们可以定义应用不同主要和重点颜色的平台主题,还可以自定义发送按钮以在iOS上使用CupertinoButton,而在Android上使用质感设计的IconButton

首先,定义一个名为kIOSTheme的新的ThemeData对象,其颜色为iOS(浅灰色、橙色)和另一个ThemeData对象kDefaultTheme,其颜色为Android(紫色、橙色)。在main.dart文件下添加下面的代码。

final ThemeData kIOSTheme = new ThemeData(primarySwatch: Colors.orange,primaryColor: Colors.grey[100],primaryColorBrightness: Brightness.light,
);final ThemeData kDefaultTheme = new ThemeData(primarySwatch: Colors.purple,accentColor: Colors.orangeAccent[400],
);

修改TalkcasuallyApp类以使用应用程序的MaterialApp控件的theme属性来更改主题。使用顶级的defaultTargetPlatform属性和条件运算符构建用于选择主题的表达式。

//...
import 'package:flutter/foundation.dart';
//...
class TalkcasuallyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return new MaterialApp(title: '谈天说地',theme: defaultTargetPlatform == TargetPlatform.iOS? kIOSTheme: kDefaultTheme,home: new ChatScreen(),);}
}
//...

我们还需要将所选主题应用到AppBar控件,也就是应用程序UI顶部的横幅。elevation属性定义了AppBarz坐标。z坐标值为0.0没有阴影(iOS),4.0的值具有定义的阴影(Android)。

class ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {//...Widget build(BuildContext context) {return new Scaffold(appBar: new AppBar(title: new Text('谈天说地'),elevation:Theme.of(context).platform == TargetPlatform.iOS ? 0.0 : 4.0,),//...//...
}

通过在_buildTextComposer方法中修改其Container父窗口控件来自定义发送图标。使用child属性和条件运算符构建一个用于选择按钮的表达式。

//...
import 'package:flutter/cupertino.dart';
//...
class ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {//...Widget _buildTextComposer() {return new IconTheme(data: new IconThemeData(color: Theme.of(context).accentColor),child: new Container(margin: const EdgeInsets.symmetric(horizontal: 8.0),child: new Row(children: <Widget> [//...new Container(margin: new EdgeInsets.symmetric(horizontal: 4.0),child: Theme.of(context).platform == TargetPlatform.iOS ?new CupertinoButton(child: new Text('发送'),onPressed: _isComposing ?() => _handleSubmitted(_textController.text) : null) :new IconButton(icon: new Icon(Icons.send),onPressed: _isComposing ?() => _handleSubmitted(_textController.text) : null),)])));}//...
}

将顶级Column包装在Container控件中,使其在上边缘呈浅灰色边框。这个边框将有助于在iOS上将AppBarbody区分开来。同时要在Android上隐藏边框,需要在AppBar应用上一个代码段的逻辑。

class ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {//...Widget build(BuildContext context) {return new Scaffold(appBar: new AppBar(title: new Text('谈天说地'),elevation:Theme.of(context).platform == TargetPlatform.iOS ? 0.0 : 4.0,),body: new Container(child: 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(),)]),decoration: Theme.of(context).platform == TargetPlatform.iOS ?new BoxDecoration(border: new Border(top: new BorderSide(color: Colors.grey[200]))) :  null));}//...
}

Flutter实战一Flutter聊天应用(四)相关推荐

  1. Flutter实战一Flutter聊天应用(汇总)

    纸聊 这个应用程序使用Google的Flutter移动框架开发,是一个实时聊天应用程序,为了能专注于APP设计,应用程序的服务端使用Googler的Firebase平台.程序程序的名称为纸聊,意为像传 ...

  2. Flutter实战一Flutter聊天应用(十五)

    在上一篇文章<Flutter实战一Flutter聊天应用(十四)>中,我们完成了注册屏幕.为了保持应用程序入口只有一个,即登陆屏幕,用户注册完成之后会返回手机号码.密码到登陆屏幕,让用户点 ...

  3. Flutter实战一Flutter聊天应用(二十)

    在上一篇文章<Flutter实战一Flutter聊天应用(十九)>中,我们完成了删除用户的逻辑,就是将会话的有效性设置为false就可以了.那么当会话的有效性为false时,用户再次添加该 ...

  4. Flutter实战一Flutter聊天应用(十六)

    在上一篇文章<Flutter实战一Flutter聊天应用(十五)>中,我们完成了登陆屏幕.在用户登陆成功后,会在本地创建一个LandingInformation文件,以使应用程序在启动时可 ...

  5. Flutter实战一Flutter聊天应用(五)

    我们的应用程序现在已经有了一个好看的UI,但是我们还没有一个后端.所以我们要买一个云服务器,然后再安装数据库?当然不是!我们可以使用Firebase平台作为后端,那么Firebase是什么呢? Fir ...

  6. Flutter实战一Flutter聊天应用(十四)

    优化输入体验 在进行下一步之前,我们先优化一下注册的体验: 正在输入注册信息时,点击屏幕空白部分,清除当前文本输入框的焦点,同时收起键盘. 正在输入注册信息时,直接收起键盘,再点击空白部分,清除当前文 ...

  7. Flutter实战一Flutter聊天应用(二十一)

    在这一系列的前二十篇文章里,我们已经完成了最主要的添加.删除好友,并与好友聊天,还可以发送图片的功能.这一篇文章会完成个人资料与设置相关的功能,并将应用发布上线. 之前设置了个人资料的入口按钮,现在我 ...

  8. Flutter实战一Flutter聊天应用(十九)

    在上一篇文章中,我们完成了聊天列表的用户界面与功能代码.在用户添加完会话后,聊天列表会增加对应的会话项,通过点击会话项,可以进入聊天屏幕.在这一篇文章中,我们主要是修改lib/chat_screen. ...

  9. Flutter实战一Flutter聊天应用(十八)

    在上一篇文章中,我们完成了基本的添加聊天功能,但是还没有在聊天列表显示添加的新聊天,在这篇文章中我们将实现这个功能--在聊天列表中展示所有的聊天. 首先,我们在/lib目录下新建一个group_cha ...

最新文章

  1. 吴恩达老师深度学习视频课笔记:构建机器学习项目(机器学习策略)(2)
  2. vue router html,vue-router.html
  3. Tomcat8中的并发ConcurrentDateFormat的实现
  4. 职业大揭秘,算法攻城狮在日常工作中都干了些啥?
  5. 关于程序工作者的规划与思考
  6. #39;boost/iterator/iterator_adaptor.hpp#39; file not found之xcode生成时报错的解决方案
  7. 面试官:面对业务量增长10倍、100倍怎么处理? 当场哭出声。。
  8. spring 容器的理论知识
  9. 浅谈Get和Post方法的区别与TCP与UDP区别
  10. ThinkPHP去除url中的index.php
  11. NYOJ题目171-聪明的kk(dp)
  12. Java8下载安装详细教程,环境配置,Java、jre下载安装教程,此电脑图标位置,电脑处理器版本查询查询
  13. Crystal Reports - New Report
  14. 05.第六章、进度管理
  15. 《Web安全之深度学习实战》笔记:第六章 垃圾邮件识别
  16. order statistics(找最ith小数值)问题
  17. 解决 CLion 出现中文乱码问题【亲测有效】
  18. 今日学习——冒泡排序
  19. 指数型基金购买技巧汇总(程序猿买基金必备——未完待续)
  20. Flex中Tree的用法备忘(增删改查节点)

热门文章

  1. 第7讲 视觉里程计1
  2. 计算机导论知识点整理笔记(一.数据结构)
  3. 2018摩拜算法工程师笔试题
  4. webshell文件下载器
  5. Linux各个目录的用途
  6. STM32 资料整理贴
  7. python爬虫从小白到高手 Day1 爬取百度音乐歌单
  8. Jmeter4.X - 使用本身自带的脚本录制功能录制脚本
  9. 随机初始化(代码实现)
  10. Linux命令学习 ls