Flutter实战一Flutter聊天应用(四)

119 篇文章 9 订阅
82 篇文章 1417 订阅

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

目前在我们的应用程序中,即使输入字段中没有文本,也会启用“发送”按钮,我们可以根据该字段是否包含要发送的文本来决定是否启用发送按钮,并更改按钮的外观。定义_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 {
  @override
  Widget 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
      )
    );
  }
  //...
}

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

何小有

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值