Widgets 介绍
Flutter 从 React 中吸取灵感,通过现代化框架创建出精美的组件。它的核心思想是用 widget 来构建你的 UI 界面。 Widget 描述了在当前的配置和状态下视图所应该呈现的样子。当 widget 的状态改变时,它会重新构建其描述(展示的 UI),框架则会对比前后变化的不同,以确定底层渲染树从一个状态转换到下一个状态所需的最小更改。
待续
如果你想通过深入了解一些代码来更好地掌握 Flutter,请查阅 Codelab: Flutter 布局基础教程、 Flutter 中的布局 和 为你的 Flutter 应用加入交互体验 这三篇文章。
Hello world
创建一个最小的 Flutter 应用简单到仅需调用 runApp()
方法并传入一个 widget 即可:
import 'package:flutter/material.dart';
void main() {
runApp(
Center(
child: Text(
'Hello, world!',
textDirection: TextDirection.ltr,
),
),
);
}
runApp()
函数会持有传入的 Widget
,并且使它成为 widget 树中的根节点。在这个例子中,Widget 树有两个 widgets, Center
widget 及其子 widget ——Text
。框架会强制让根 widget 铺满整个屏幕,也就是说“Hello World”会在屏幕上居中显示。在这个例子我们需要指定文字的方向,当使用 MaterialApp
widget 时,你就无需考虑这一点,之后我们会进一步的描述。
在写应用的过程中,取决于是否需要管理状态,你通常会创建一个新的组件继承 StatelessWidget
或 StatefulWidget
。 Widget 的主要工作是实现 build
方法,该方法根据其它较低级别的 widget 来描述这个 widget。框架会逐一构建这些 widget,直到最底层的描述 widget 几何形状的 RenderObject
。
comment:这个布局其实很简单,对照Android其实就是一个布局嵌套了一个text
基础 widgets
Flutter 自带了一套强大的基础 widgets,下面列出了一些常用的:
Text
Text
widget 可以用来在应用内创建带样式的文本。
Row
, Column
这两个 flex widgets 可以让你在水平 (Row
) 和垂直(Column
) 方向创建灵活的布局。它是基于 web 的 flexbox 布局模型设计的。
Stack
Stack
widget 不是线性(水平或垂直)定位的,而是按照绘制顺序将 widget 堆叠在一起。你可以用 Positioned
widget 作为Stack
的子 widget,以相对于 Stack
的上,右,下,左来定位它们。 Stack 是基于 Web 中的绝对位置布局模型设计的。
Container
Container
widget 可以用来创建一个可见的矩形元素。 Container 可以使用 BoxDecoration
来进行装饰,如背景,边框,或阴影等。 Container
还可以设置外边距、内边距和尺寸的约束条件等。另外,Container
可以使用矩阵在三维空间进行转换。
下面是一些简单的 widget,它们结合了上面提到的 widget 和一些其他的 widget:
import 'package:flutter/material.dart';
class MyAppBar extends StatelessWidget {
MyAppBar({this.title});
// Fields in a Widget subclass are always marked "final".
final Widget title;
@override
Widget build(BuildContext context) {
return Container(
height: 56.0, // in logical pixels
padding: const EdgeInsets.symmetric(horizontal: 8.0),
decoration: BoxDecoration(color: Colors.blue[500]),
// Row is a horizontal, linear layout.
child: Row(
// <Widget> is the type of items in the list.
children: <Widget>[
IconButton(
icon: Icon(Icons.menu),
tooltip: 'Navigation menu',
onPressed: null, // null disables the button
),
// Expanded expands its child to fill the available space.
Expanded(
child: title,
),
IconButton(
icon: Icon(Icons.search),
tooltip: 'Search',
onPressed: null,
),
],
),
);
}
}
class MyScaffold extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Material is a conceptual piece of paper on which the UI appears.
return Material(
// Column is a vertical, linear layout.
child: Column(
children: <Widget>[
MyAppBar(
title: Text(
'Example title',
style: Theme.of(context).primaryTextTheme.title,
),
),
Expanded(
child: Center(
child: Text('Hello, world!'),
),
),
],
),
);
}
}
void main() {
runApp(MaterialApp(
title: 'My app', // used by the OS task switcher
home: MyScaffold(),
));
}
对照界面就看的很明白,其实就是一个水平布局的actionbar + 一个之前的hello world
不好的地方在于systemUi的状态栏和这个actionbar重叠了,点击不了。。。
改了下几个参数的配置,查看作用,热加载还是很好用的
height: 556.0, // in logical pixels
padding: const EdgeInsets.symmetric(horizontal: 48.0),
decoration: BoxDecoration(color: Colors.blue[100]),
效果如下
高度和pading很好理解,那个蓝色里面的数字应该是指蓝色的深度,越大越深
请确认在 pubspec.yaml
文件中 flutter
部分有 uses-material-design: true
这条,它能让你使用预置的 Material icons。
name: my_app
flutter:
uses-material-design: true
为了获得(MaterialApp
)主题的数据,许多 Material Design 的 widget 需要在 MaterialApp
中才能显现正常。因此,请使用 MaterialApp
运行应用。
MyAppBar
widget 创建了一个高 56 独立像素,左右内边距 8 像素的 Container
。在容器内,MyAppBar
以 Row
布局来组织它的子元素。中间的子 widget(title
widget),被标记为 Expanded
,这意味着它会扩展以填充其它子 widget 未使用的可用空间。你可以定义多个Expanded
子 widget,并使用 flex
参数确定它们占用可用空间的比例。
MyScaffold
widget 将其子 widget 组织在垂直列中。在列的顶部,它放置一个 MyAppBar
实例,并把 Text
widget 传给它来作为应用的标题。把 widget 作为参数传递给其他 widget 是一个很强大的技术,它可以让你以各种方式创建一些可重用的通用组件。最后,MyScaffold 使用 Expanded
来填充剩余空间,其中包含一个居中的消息。
有关更多信息,请参阅 布局。
使用 Material 组件
Flutter 提供了许多 widget,可帮助你构建遵循 Material Design 的应用。 Material 应用以 MaterialApp
widget 开始,它在你的应用的底层下构建了许多有用的 widget。这其中包括 Navigator
,它管理由字符串标识的 widget 栈,也称为“routes”。 Navigator
可以让你在应用的页面中平滑的切换。使用 MaterialApp
widget 不是必须的,但这是一个很好的做法。
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'Flutter Tutorial',
home: TutorialHome(),
));
}
class TutorialHome extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Scaffold is a layout for the major Material Components.
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.menu),
tooltip: 'Navigation menu',
onPressed: null,
),
title: Text('Example title'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
tooltip: 'Search',
onPressed: null,
),
],
),
// body is the majority of the screen.
body: Center(
child: Text('Hello, world!'),
),
floatingActionButton: FloatingActionButton(
tooltip: 'Add', // used by assistive technologies
child: Icon(Icons.add),
onPressed: null,
),
);
}
}
效果
现在我们已经从
MyAppBar
和MyScaffold
切换到了 material.dart 中的AppBar
和Scaffold
widget,我们的应用更“Material”了一些。例如,标题栏有了阴影,标题文本会自动继承正确的样式,此外还添加了一个浮动操作按钮。注意,widget 作为参数传递给了另外的 widget。
Scaffold
widget 将许多不同的 widget 作为命名参数,每个 widget 都放在了 Scofford 布局中的合适位置。同样的,AppBar
widget 允许我们给leading
、title
widget 的actions
传递 widget。这种模式在整个框架会中重复出现,在设计自己的 widget 时可以考虑这种模式。有关更多信息,请参阅 Material 组件。
备忘
Material 是 Flutter 中两个自带的设计之一,如果想要以 iOS 为主的设计,可以参考 Cupertino components,它有自己版本的
CupertinoApp
和CupertinoNavigationBar
.。
处理手势
大多数应用都需要通过系统来处理一些用户交互。构建交互式应用程序的第一步是检测输入手势,这里通过创建一个简单的按钮来了解其工作原理:
class MyButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
print('MyButton was tapped!');
},
child: Container(
height: 36.0,
padding: const EdgeInsets.all(8.0),
margin: const EdgeInsets.symmetric(horizontal: 8.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5.0),
color: Colors.lightGreen[500],
),
child: Center(
child: Text('Engage'),
),
),
);
}
}
GestureDetector
widget 没有可视化的展现,但它能识别用户的手势。当用户点击 Container
时, GestureDetector
会调用其 onTap()
回调,在这里会向控制台打印一条消息。你可以使用 GestureDetector
检测各种输入的手势,包括点击,拖动和缩放。
许多 widget 使用 GestureDetector
为其他 widget 提供可选的回调。例如,IconButton
、RaisedButton
和 FloatingActionButton
widget 都有 onPressed()
回调,当用户点击 widget 时就会触发这些回调。
有关更多信息,请参阅 Flutter 中的手势。
试了下,这个按钮有圆角,设置了内边距,点击终端会打印
I/flutter ( 3206): MyButton was tapped!
I/flutter ( 3206): MyButton was tapped!
I/flutter ( 3206): MyButton was tapped!
I/flutter ( 3206): MyButton was tapped!