目录
1.介绍
中心思想就是用widget构建UI
2.Hello World
一个最简单的Flutter应用程序,只需一个widget即可
import 'package:flutter/material.dart';
void main() {
runApp(
new Center(
child: new Text(
'Hello World!',
textDirection: TextDirection.ltr,
),
)
);
}
- runApp接受给定的widget,并将它作为widget树的根,在上面的代码中,widget由Center和Text组成。框架强制根widget占满整个屏幕,文本居中显示,文本显示的方向需要在Text实例中用textDirection指定,当使用MaterialApp时,文本的方向将自动设定。
- 在编写应用程序的时候通常要创建新widget,这些widget是无状态的StatelessWidget或者是有状态的StatefulWidget,具体的选择取决于你的widget是否需要管理一些状态
- widget的主要工作是实现一个build函数,用以构建自身
- 一个widget通常由一些较低级别widget组成。Flutter框架将依次构建这些widget,直到构建到最底层的子widget时。这些最低层的widget通常为RenderObject,它会计算并描述widget的几何形状。
3.基础 Widget
Flutter有一套丰富、强大的基础widget,其中以下是很常用的widget :
- Text:可创建一个带格式的文本。
- Row、 Column: 可在水平(Row)和垂直(Column)方向上创建灵活的布局。其设计是基于web开发中的Flexbox布局模型。
- Stack: 取代线性布局 (和Android中的LinearLayout相似),Stack允许子 widget 堆叠, 你可以使用 Positioned 来定位他们相对于Stack的上下左右四条边的位置。Stacks是基于Web开发中的绝度定位(absolute positioning )布局模型设计的。
- Container: 可创建矩形视觉元素。container 可以装饰为一个BoxDecoration, 如 background、一个边框、或者一个阴影。 Container 也可以具有边距(margins)、填充(padding)和应用于其大小的约束(constraints)。另外, Container可以使用矩阵在三维空间中对其进行变换。
下面创建一个 Container
。
//创建一个标题栏
class MyAppBar extends StatelessWidget{
MyAppBar({this.title});
// Widget子类中的字段往往都会定义为"final"
final Widget title;
@override
Widget build(BuildContext context) {
return new Container( // 这里是圆括号
height: 64.0, //单位是逻辑上的像素(包括手机顶部的导航栏)
padding: const EdgeInsets.symmetric(horizontal: 8.0), //左右都有8个像素的填充
decoration: new BoxDecoration(color: Colors.blue[500]), //填充背景,后面的数字表示深浅
// Row 是水平方向的线性布局(linear layout),使用Row 布局来排列其子项
child: new Row(
//列表项的类型是 <Widget>
children: <Widget>[
new IconButton(
icon: new Icon(Icons.menu),
tooltip: '导航栏',
onPressed: null), // null 会禁用 button
// 中间的title widget被标记为Expanded, ,这意味着它会填充尚未被其他子项占用的的剩余可用空间,
// Expanded可以拥有多个children,然后使用flex参数来确定他们占用剩余空间的比例
new Expanded(
child: title
),
new IconButton(
icon: new Icon(Icons.search),
tooltip: '搜索',
onPressed: null),
],
),
);
}
}
class MyScaffold extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Material 是UI呈现的“一张纸”
return new Material(
child: new Column(
children: <Widget>[
// 在Column的顶部,放置了一个MyAppBar实例,将一个Text widget作为其标题传递给应用程序栏
new MyAppBar(
title: new Text(
'Example title',
style: Theme.of(context).primaryTextTheme.title,
),
),
new Expanded(
child: new Center(
child: new Text('Hello World'),
)
),
],
),
);
}
void main() {
runApp(new MaterialApp(
title: 'Container Test',
home: new MyScaffold()
)
);
}
4.使用 Material 组件
- Material应用程序以MaterialApp widget开始, 该widget在应用程序的根部创建了一些有用的widget,其中包括一个Navigator, 它管理由字符串标识的Widget栈(即页面路由栈)。Navigator可以让您的应用程序在页面之间的平滑的过渡。
//创建Material样式
class TutorialHome extends StatelessWidget{
@override
Widget build(BuildContext context) {
//Scaffold是Material中主要的布局组件.
return new Scaffold(
appBar: new AppBar(
leading: new IconButton(icon: new Icon(Icons.menu),tooltip: '导航栏', onPressed: null),
title: new Text('搜索'),
actions: <Widget>[
new IconButton(icon: new Icon(Icons.search),tooltip: '搜索', onPressed: null)
],
),
body: new Center(
child: new Text('Hello World'),
),
floatingActionButton: new FloatingActionButton(
tooltip:'Add',
child: new Icon(Icons.add),
onPressed: null
),
);
}
void main() {
runApp(new MaterialApp(
title: 'Flutter Test',
home: new TutorialHome(),
)
);
}
- 请注意,我们再次将widget作为参数传递给其他widget。该 Scaffold widget 需要许多不同的widget的作为命名参数,其中的每一个被放置在Scaffold布局中相应的位置。 同样,AppBar 中,我们给参数leading、actions、title分别传一个widget。 这种模式在整个框架中会经常出现,这也可能是您在设计自己的widget时会考虑到一点。
5.处理手势
class MyButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new GestureDetector(
onTap: () {
print('MyButton was tapped');
},
child: new Container(
height: 36.0,
padding: const EdgeInsets.all(8.0),
margin: const EdgeInsets.symmetric(horizontal: 8.0),
decoration: new BoxDecoration(
borderRadius: new BorderRadius.circular(5.0),
color: Colors.lightGreen[500],
),
child: new Center(
child: new Text('Engage'),
),
),
);
}
}
void main() {
runApp(new MaterialApp(
title: 'ButtonTap Test',
home: new MyButton(),
)
);
}
-
GestureDetector widget并不具有显示效果,而是检测由用户做出的手势。 当用户点击Container时, GestureDetector会调用它的onTap回调, 在回调中,将消息打印到控制台。您可以使用GestureDetector来检测各种输入手势,包括点击、拖动和缩放。
-
许多widget都会使用一个GestureDetector为其他widget提供可选的回调。 例如,IconButton、 RaisedButton、 和FloatingActionButton ,它们都有一个onPressed回调,它会在用户点击该widget时被触发。
6.根据用户输入改变widget
//计数
class Counter extends StatefulWidget{
@override
_CounterState createState() => new _CounterState();
}
class _CounterState extends State<Counter>{
int _counter = 0;
void _increment() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return new Row(
children: <Widget>[
new RaisedButton(
onPressed: _increment,
child: new Text('Increment'),
),
new Text('Count : $_counter'),
],
);
}
}
void main() {
runApp(new MaterialApp(
title: 'ButtonTap Test',
home: new Counter(),
)
);
}
- 无状态widget从它们的父widget接收参数, 它们被存储在final型的成员变量中。 当一个widget被要求构建时,它使用这些存储的值作为参数来构建widget。
- StatefulWidgets是特殊的widget,它知道如何生成State对象,然后用它来保持状态
为什么StatefulWidget和State是单独的对象?
在Flutter中,这两种类型的对象具有不同的生命周期: Widget是临时对象,用于构建当前状态下的应用程序,而State对象在多次调用build()之间保持不变,允许它们记住信息(状态)。
class CounterDisplay extends StatelessWidget {
// 注意:圆括号里面有一个大括号
CounterDisplay({this.count});
final int count;
@override
Widget build(BuildContext context) {
return new Text('Count :$count');
}
}
class CounterIncrementor extends StatelessWidget{
CounterIncrementor({this.onPressed});
final VoidCallback onPressed;
@override
Widget build(BuildContext context) {
return new RaisedButton(
onPressed: onPressed,
child: new Text('Increment'),
);
}
}
class CounterUpdate extends StatefulWidget{
@override
_CounterStateUpdate createState() => new _CounterStateUpdate();
}
class _CounterStateUpdate extends State<CounterUpdate>{
int _counter = 0;
void _increment() {
setState(() {
++_counter;
});
}
@override
Widget build(BuildContext context) {
return new Row(
children: <Widget>[
new CounterDisplay(count:_counter),
new CounterIncrementor(onPressed: _increment),
],
);
}
}
void main() {
runApp(new MaterialApp(
title: 'ButtonTap Test',
home: new CounterUpdate(),
)
);
}
说明:
- Widget子类中的字段往往都会定义为"final"
- 在Flutter中,事件流是“向上”传递的,而状态流是“向下”传递的(译者语:这类似于React/Vue中父子组件通信的方式:子widget到父widget是通过事件通信,而父到子是通过状态),重定向这一流程的共同父元素是State