作为新手写实战教程是为了也是为了学习,课程内容的实战项目主要来自我看的英文视频教程
希望尽一点点薄力让大家有兴趣学习这门新技术。
这里暂不介绍环境配置等操作,不了解的朋友请先移步官网: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基础可能没有涉及,如果又需要会尽量更新!
完整代码
喜欢Star~~~~hhh
网友评论