美文网首页
实战中学习Flutter1:简易聊天AppUI

实战中学习Flutter1:简易聊天AppUI

作者: Meandni | 来源:发表于2018-12-05 14:24 被阅读27次

作为新手写实战教程是为了也是为了学习,课程内容的实战项目主要来自我看的英文视频教程

希望尽一点点薄力让大家有兴趣学习这门新技术。

这里暂不介绍环境配置等操作,不了解的朋友请先移步官网:https://flutter.io/

开发环境:VS Code

本套课程适合直接上手,无需各种基础,dart基础也不需要~~

项目展示

废话不多说了,看下这次我们需要开发的UI界面吧:


Flutter.gif

代码实现

首先我们替换入口文件lib/main.dart下的代码

代码如下:

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {

}
  • import 'package:flutter/material.dart';

    Flutter默认帮我们导入了flutter/material.dart包,这个包也是我们开发FlutterApp必备的包,其实也是一个UI库,其内部实现了大量优秀炫酷的组件(Widgets),有App结构和导航、按钮、输入框和选择框、对话框、Alert、Panel、动画等等等等Material Design风格的控件。

    Material Design:熟悉Android开发的童鞋一定非常了解了,是谷歌推出的一套视觉设计语言。其风格简单大方是我个人非常喜欢的设计风格,有兴趣的同学可以学习了解一下Material Design官方原版Material Design中文翻译版

    在本项目中还需倒入其他哦两个库:

       import 'package:flutter/cupertino.dart';  //IOS风格适配
       import 'package:flutter/foundation.dart'; //flutter核心库之一
    
  • void main() => runApp(new MyApp());

    是Dart程序的入口,也就是说,Flutter程序在运行的时候,第一个执行的函数就是main()函数,Flutter默认会找到lib目录下的main.dart并运行void main() => runApp(new MyApp());

  • 第一个组件

    class MyApp extends StatelessWidget {
    
       }
    

    这是我们在主函数中调用的第一个控件(Widget)。

定义添加两种Theme(主题)分别适配Android和IOS

import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';

final ThemeData iOSTheme = new ThemeData(
  primarySwatch: Colors.red,
  primaryColor: Colors.grey[400],
  primaryColorBrightness: Brightness.dark,
);

final ThemeData androidTheme = new ThemeData(
  primarySwatch: Colors.blue,
  accentColor: Colors.green,
);

const String defaultUserName = "MeandNi"; //默认用户名

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {

}

关注一下其中的primary颜色对双平台的设置,并且我们将ThemeData变量设置为final不可变的变量。

定义一个无状态StatelessWidget组件

继承 StatelessWidget,通过 build 方法返回一个布局好的静态控件


class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext ctx) {
    return new MaterialApp(
      title: "Chat Application",
      theme: defaultTargetPlatform == TargetPlatform.iOS
          ? iOSTheme
          : androidTheme,
      home: new Chat(),
    );
  }
}

我们在这里返回了一个MaterialApp,其中可以设置title(标题)theme(主题)home(主页)等属性。

其他属性参考:https://docs.flutter.io/flutter/widgets/WidgetsApp-class.html

title:App标题

theme:App主题

home:App根路径

我们通过判断用户平台给出不同的主题。

定义一个有状态StatefulWidget组件 —— Chat

继承 StatefulWidget,通过 build 方法返回一个布局好的动态控件。所谓动态控件,这里我们主要关注State,通过 State 的 build 方法去构建控件。在 State 中,你可以动态改变数据,这类似 MVVM 实现,在 setState 之后,改变的数据会触发 Widget 重新构建刷新。而下方代码中,我们咋State中定义了_messages、_textController、_isWriting三个变量,我们需要在改变着三个变量时触发 Widget 重新刷新。

class Chat extends StatefulWidget {
  @override
  State createState() => new ChatWindow();
}

class ChatWindow extends State<Chat> with TickerProviderStateMixin {
  final List<Msg> _messages = <Msg>[];
  final TextEditingController _textController = new TextEditingController();
  bool _isWriting = false;

  @override
  Widget build(BuildContext ctx) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Chat Application"),
        elevation:
        Theme.of(ctx).platform == TargetPlatform.iOS ? 0.0 : 6.0,
      ),
      body: new Column(children: <Widget>[
        new Flexible(
            child: new ListView.builder(
              itemBuilder: (_, int index) => _messages[index],
              itemCount: _messages.length,
              reverse: true,
              padding: new EdgeInsets.all(6.0),
            )),
        new Divider(height: 1.0),
        new Container(
          child: _buildComposer(),
          decoration: new BoxDecoration(color: Theme.of(ctx).cardColor),
        ),
      ]),
    );
  }

定义底部输入框和submit按钮:

此Widget被放在ChatWindow的底部用于用户的输入提交。我们可以放关注点聚焦在TextField组件上,我们通过对输入值的监听修改_isWriting的值并对输入框组件和下方的按钮组件刷新达到一定用户体验。

对于放松按钮,我们在IOS端使用CupertinoButton,android端使用IconButton

Widget _buildComposer() {
    return new IconTheme(
      data: new IconThemeData(color: Theme.of(context).accentColor),
      child: new Container(
          margin: const EdgeInsets.symmetric(horizontal: 9.0),
          child: new Row(
            children: <Widget>[
              new Flexible(
                child: new TextField(
                  controller: _textController,
                  onChanged: (String txt) {
                    setState(() {
                      _isWriting = txt.length > 0;
                    });
                  },
                  onSubmitted: _submitMsg,
                  decoration:
                  new InputDecoration.collapsed(hintText: "Enter some text to send a message"),
                ),
              ),
              new Container(
                  margin: new EdgeInsets.symmetric(horizontal: 3.0),
                  child: Theme.of(context).platform == TargetPlatform.iOS
                      ? new CupertinoButton(
                      child: new Text("Submit"),
                      onPressed: _isWriting ? () => _submitMsg(_textController.text)
                          : null
                  )
                      : new IconButton(
                    icon: new Icon(Icons.message),
                    onPressed: _isWriting
                        ? () => _submitMsg(_textController.text)
                        : null,
                  )
              ),
            ],
          ),
          decoration: Theme.of(context).platform == TargetPlatform.iOS
              ? new BoxDecoration(
              border:
              new Border(top: new BorderSide(color: Colors.brown))) :
          null
      ),
    );
  }

定义发送数据的函数

通过_textController清除输入框的旧数据。定义Msg插入到_messages数组中。

这里我们可以关注动画效果的操作,animationController将作为传输传递到Msg组件当中。

void _submitMsg(String txt) {
    _textController.clear();
    setState(() {
      _isWriting = false;
    });
    Msg msg = new Msg(
      txt: txt,
      animationController: new AnimationController(
          vsync: this,
          duration: new Duration(milliseconds: 800)
      ),
    );
    setState(() {
      _messages.insert(0, msg);
    });
    msg.animationController.forward();
  }

定义Msg消息的模版组件

实际上我们点击Submit消息发送时,发送的就是这样一个StatelessWidget,其中携带两个变量:txt消息内容、animationController动画控制器。


class Msg extends StatelessWidget {
  Msg({this.txt, this.animationController});
  final String txt;
  final AnimationController animationController;

  @override
  Widget build(BuildContext ctx) {
    return new SizeTransition(
      sizeFactor: new CurvedAnimation(
          parent: animationController, curve: Curves.easeOut),
      axisAlignment: 0.0,
      child: new Container(
        margin: const EdgeInsets.symmetric(vertical: 8.0),
        child: new Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            new Container(
              margin: const EdgeInsets.only(right: 18.0),
              child: new CircleAvatar(child: new Text(defaultUserName[0])),
            ),
            new Expanded(
              child: new Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[
                  new Text(defaultUserName, style: Theme.of(ctx).textTheme.subhead),
                  new Container(
                    margin: const EdgeInsets.only(top: 6.0),
                    child: new Text(txt),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

至此我们的第一个实战App旧大功告成了,是不是很简单的样子,没错,就是这么简单就能做出这么漂亮的UI,其中有些Flutter基础可能没有涉及,如果又需要会尽量更新!

完整代码

github demo

喜欢Star~~~~hhh

相关文章

网友评论

      本文标题:实战中学习Flutter1:简易聊天AppUI

      本文链接:https://www.haomeiwen.com/subject/zyaicqtx.html