basic.dart
[TOC]
-
basic.dart
这个文件按对子节点的影响大致分成了6种Widget。 - 按子节点的数量,可以分为
SingleChildRenderObjectWidget
和MultiChildRenderObjectWidget
。也就是布局Widget与普通Widget的区别。相当于android中的ViewGroup
和View
吧。
PAINTING NODES
对子节点施加额外的painting的效果,透明度、阴影、边角剪裁等。
Opacity
-
改变子节点的不透明度,效果图:
源码:
import 'package:flutter/material.dart';
class OpacityDemo extends StatefulWidget {
@override
_OpacityDemoState createState() {
return _OpacityDemoState();
}
}
class _OpacityDemoState extends State<OpacityDemo> {
double _opacity = 0.0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('OpacityDemo')),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Slider(
value: _opacity,
onChanged: (value) => setState(() => _opacity = value),
),
Opacity(
// 只有一个不透明度的参数,动态设置即可实现动画
opacity: _opacity,
child: Container(
width: 200.0,
height: 200.0,
color: Colors.red,
),
),
],
),
);
}
}
-
Opacity
的实现方式是先把子节点绘制到一个缓存中,然后把设置好的透明度与子节点混合(blend)。 - 当不透明度为0和1的时候,效率会高一些,因为没了把子节点绘制到缓存中的步骤。
- 跟动态添加或删除widget相比,设置不透明度(1或0)的效率要更高。
- 如果要做不透明度的动画,使用
AnimatedOpacity
的效率要高很多,因为动态设置Opacity
的不透明度会触发每一帧的刷新,而AnimatedOpacity
不会。
ShaderMask
-
用shader给子节点增加一个mask。效果图:
源码:
import 'package:flutter/material.dart';
class ShaderMaskDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: ShaderMask(
shaderCallback: (Rect bounds) {
return RadialGradient(
center: Alignment.topLeft,
colors: <Color>[Colors.yellow, Colors.deepOrange.shade900],
tileMode: TileMode.mirror,
).createShader(bounds);
},
child: Center(child: Text('I’m burning the memories')),
),
);
}
}
-
RadialGradient
的createShader
方法来自Gradient
类,也就说只有Gradient
的子类才能创建Shader
? - 这个Widget目前来看用的人还是比较少,github上相关的Issue也只有个位数。
- 看了下
Gradient
是继承了Shader
类的。
BackdropFilter
-
主要的一个应用是ios风格的毛玻璃效果。效果图:
源码:
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
class BackdropFilterDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
ConstrainedBox(
constraints: BoxConstraints.expand(), child: FlutterLogo()),
BackdropFilter(
filter: ui.ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
child: Center(
child: Text(
'Frosted',
style: Theme.of(context).textTheme.display3,
),
),
),
],
),
);
}
}
-
BackdropFilter
服务的对象应该是在它“后面”的那些Widget,而它的child只是借用了一下它的效果(让BackdropFilter
以及child在“后面”的Widget基础之上产生毛玻璃效果)。 - 最终产生毛玻璃效果的区域是child的区域。
- 有一点搞不懂的是这里用的Text的毛玻璃的区域,为什么是不对称的?
CustomPaint
// TODO
ClipRect
-
把子节点的形状剪成矩形,不过一般的子节点都看不出来,因为大部分本身就是矩形的吧。效果图:
源码:
import 'package:flutter/material.dart';
class ClipRectDemo extends StatefulWidget {
@override
_ClipRectDemoState createState() {
return _ClipRectDemoState();
}
}
class _ClipRectDemoState extends State<ClipRectDemo> {
double _heightFactor = 0.0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('ClipRectDemo')),
body: Column(
children: <Widget>[
Slider(
value: _heightFactor,
onChanged: (value) => setState(() => _heightFactor = value),
),
ClipRect(
// Align设置了heightFactor, 那么Align的高度为Container的_heightFactor倍
// ClipRect之后, Container的下半部分就被剪掉了
child: Align(
alignment: AlignmentDirectional.topCenter,
heightFactor: _heightFactor,
// Container的大小为整个body
child: Container(
height: 400.0,
color: Colors.red,
),
),
),
],
),
);
}
}
- 默认情况
ClipRect
会禁止子节点超出它本身,但是可以通过自定义clipper
属性来实现超出ClipRect
的情况。clipper
可以移动要剪的位置和大小,也就是那个Rect
。自定义clipper单独发篇文章好了。
ClipRRect
- 作用和
ClipRect
一样,只不过有一个borderRadius
参数,可以控制矩形的圆角,效果图:
源码:
import 'package:flutter/material.dart';
class ClipRRectDemo extends StatefulWidget {
@override
_ClipRRectDemoState createState() {
return _ClipRRectDemoState();
}
}
class _ClipRRectDemoState extends State<ClipRRectDemo> {
double _cornerRadius = 0.0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('ClipRRectDemo')),
body: Column(
children: <Widget>[
Slider(
value: _cornerRadius,
onChanged: (value) => setState(() => _cornerRadius = value),
),
ClipRRect(
// 控制圆角的角度
borderRadius: BorderRadius.circular(_cornerRadius * 180),
child: Container(
height: 400.0,
color: Colors.red,
),
),
],
),
);
}
}
ClipOval
-
ClipOval
就是ClipRRect
的圆角成90°的情况
源码:
import 'package:flutter/material.dart';
class ClipOvalDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('ClipOvalDemo')),
body: ClipOval(
child: Container(
height: 400.0,
color: Colors.red,
),
),
);
}
}
ClipPath
-
ClipPath
默认的行为(clipper
参数为空)与ClipRect
一样,就是简单的矩形。那么也就可知Clip
系列Widget就是ClipPath
的特殊情况。 -
clipper
放到proxy_box.dart
再分析了。
效果图:
源码:
import 'package:flutter/material.dart';
class ClipPathDemo extends StatefulWidget {
@override
_ClipPathDemoState createState() {
return _ClipPathDemoState();
}
}
class _ClipPathDemoState extends State<ClipPathDemo> {
double _heightFactor = 0.0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('ClipRectDemo')),
body: Column(
children: <Widget>[
Slider(
value: _heightFactor,
onChanged: (value) => setState(() => _heightFactor = value),
),
ClipPath(
// Align设置了heightFactor, 那么Align的高度为Container的0.5倍
// ClipRect之后, Container的下半部分就被剪掉了
child: Align(
alignment: AlignmentDirectional.topCenter,
heightFactor: _heightFactor,
// Container的大小为整个body
child: Container(
height: 400.0,
color: Colors.red,
),
),
),
],
),
);
}
}
PhysicalModel
-
PhysicalModel
的一个主要作用就是给子节点添加阴影效果(由elevation
指定),还可以指定阴影的颜色和子节点的形状,主要的功能就这几个。效果图:
源码:
import 'package:flutter/material.dart';
class PhysicalModelDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('PhysicalModelDemo')),
body: PhysicalModel(
color: Colors.red,
shape: BoxShape.circle,
shadowColor: Colors.blueAccent,
elevation: 20.0,
child: Container(),
),
);
}
}
PhysicalShape
-
PhysicalShape
和PhysicalModel
类似,只不过可以自定义path(即clipper参数)。
POSITIONING AND SIZING NODES
- 控制子节点的位置和大小
Transform
-
给子节点做一个变换,注意是静态的变换(也就是变换应用了之后再画出来),而不是动画!效果图:
源码:
import 'package:flutter/material.dart';
class TransformDemo extends StatefulWidget {
@override
TransformDemoState createState() {
return TransformDemoState();
}
}
class TransformDemoState extends State<TransformDemo> {
double _angle = 0.0;
@override
Widget build(BuildContext context) {
return Container(
child: Scaffold(
appBar: AppBar(title: Text('TransformDemo')),
body: Column(
children: <Widget>[
Slider(
value: _angle,
onChanged: (value) => setState(() => _angle = value),
),
Transform.rotate(
alignment: AlignmentDirectional.center,
angle: _angle,
child: Center(
child: Container(
height: 200.0,
width: 200.0,
color: Colors.red,
),
),
),
],
),
),
);
}
}
CompositedTransformTarget
// TODO
CompositedTransformFollower
// TODO
FittedBox
- 提供了子节点在
FittedBox
中展示的几种策略,分别是fill
(子节点长和宽都撑满父节点),contain
(子节点包含在父节点中),cover
(子节点覆盖住父节点所占的区域,但是不像fill那样拉伸,而是通过剪裁然后放大的方式实现覆盖),fitHeight
(按子节点的高度撑满父节点,宽度不管它),fitWidth
(按子节点的宽度撑满父节点,高度不管它),none
(按原图显示),scaleDown
(如果图片需要缩小的话,那么就跟contain效果一样,其他情况和none效果一样)。效果图:
源码:
import 'package:flutter/material.dart';
class FittedBoxDemo extends StatefulWidget {
@override
FittedBoxDemoState createState() {
return FittedBoxDemoState();
}
}
class FittedBoxDemoState extends State<FittedBoxDemo> {
BoxFit _fit = BoxFit.cover;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('FittedBoxDemo')),
body: Column(
children: <Widget>[
Row(
children: <Widget>[
RaisedButton(
onPressed: () {
setState(() => _fit = BoxFit.fill);
},
child: Text('fill'),
),
RaisedButton(
onPressed: () {
setState(() => _fit = BoxFit.contain);
},
child: Text('contain'),
),
RaisedButton(
onPressed: () {
setState(() => _fit = BoxFit.cover);
},
child: Text('cover'),
),
RaisedButton(
onPressed: () {
setState(() => _fit = BoxFit.fitWidth);
},
child: Text('fitWidth'),
),
],
),
Row(
children: <Widget>[
RaisedButton(
onPressed: () {
setState(() => _fit = BoxFit.fitHeight);
},
child: Text('fitHeight'),
),
RaisedButton(
onPressed: () {
setState(() => _fit = BoxFit.none);
},
child: Text('none'),
),
RaisedButton(
onPressed: () {
setState(() => _fit = BoxFit.scaleDown);
},
child: Text('scaleDown'),
),
],
),
Center(
child: Container(
height: 200.0,
width: 200.0,
color: Colors.red,
child: FittedBox(
fit: _fit,
child: Image.asset('assets/rabbit.png'),
),
),
),
],
),
);
}
}
FractionalTranslation
-
依据子节点自身的百分比来对子节点做出变换。效果图:
源码:
import 'package:flutter/material.dart';
class FractionalTranslationDemo extends StatefulWidget {
@override
FractionalTranslationDemoState createState() {
return FractionalTranslationDemoState();
}
}
class FractionalTranslationDemoState extends State<FractionalTranslationDemo> {
double _offset = 0.0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('FractionalTranslationDemo')),
body: Column(
children: <Widget>[
Slider(
value: _offset,
onChanged: (value) => setState(() => _offset = value),
),
Container(
color: Colors.blueAccent,
child: GestureDetector(
onTap: () => print('tapped!!!!'),
child: FractionalTranslation(
transformHitTests: true,
translation: Offset(_offset, _offset),
child: Container(
height: 200.0,
width: 200.0,
color: Colors.red,
),
),
),
),
],
),
);
}
}
- 有一点需要注意的是
transformHitTests
参数,这个参数表示接收hit test的区域是否也跟着Transition一起走。
RotatedBox
-
以90°为单位对子节点进行旋转。效果图:
源码:
import 'package:flutter/material.dart';
class RotatedBoxDemo extends StatefulWidget {
@override
RotatedBoxDemoState createState() {
return new RotatedBoxDemoState();
}
}
class RotatedBoxDemoState extends State<RotatedBoxDemo> {
int turns = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('RotatedBoxDemo')),
body: GestureDetector(
onTap: () {
setState(() => ++turns);
},
child: RotatedBox(
quarterTurns: turns,
child: Center(
child: Image.asset('assets/rabbit.png'),
),
),
),
);
}
}
-
quarterTurns
可以一直累加,它自己会做判断。
Padding
-
给子节点增加(其实是外边距吧)padding。效果图:
源码:
import 'package:flutter/material.dart';
class PaddingDemo extends StatefulWidget {
@override
PaddingDemoState createState() {
return new PaddingDemoState();
}
}
class PaddingDemoState extends State<PaddingDemo> {
double _insets = 0.0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('PaddingDemo')),
body: Column(
children: <Widget>[
Slider(
value: _insets,
onChanged: (value) => setState(() => _insets = value),
),
Container(
color: Colors.blueAccent,
child: Center(
child: Padding(
padding: EdgeInsets.all(_insets * 50.0),
child: Container(
width: 200.0,
height: 200.0,
color: Colors.red,
),
),
),
),
],
),
);
}
}
- 关于直接使用
Padding
和使用Container
的padding
属性来构造padding,实际上如果Container
只设置了padding
属性的话,那么构造出来的就是单纯的Padding
。Container
只是一个便利的控件而已。
Align
-
对子节点进行对齐的控件。效果图:
源码:
import 'package:flutter/material.dart';
class AlignDemo extends StatefulWidget {
@override
AlignDemoState createState() {
return new AlignDemoState();
}
}
class AlignDemoState extends State<AlignDemo> {
AlignmentDirectional _directional = AlignmentDirectional.center;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('AlignDemo')),
body: Column(
children: <Widget>[
GridView.count(
crossAxisCount: 3,
shrinkWrap: true,
childAspectRatio: 3.0,
crossAxisSpacing: 8.0,
mainAxisSpacing: 8.0,
children: <Widget>[
RaisedButton(
onPressed: () {
setState(() {
_directional = AlignmentDirectional.topStart;
});
},
child: Text('topStart'),
),
RaisedButton(
onPressed: () {
setState(() {
_directional = AlignmentDirectional.topCenter;
});
},
child: Text('topCenter'),
),
RaisedButton(
onPressed: () {
setState(() {
_directional = AlignmentDirectional.topEnd;
});
},
child: Text('topEnd'),
),
RaisedButton(
onPressed: () {
setState(() {
_directional = AlignmentDirectional.centerStart;
});
},
child: Text('centerStart'),
),
RaisedButton(
onPressed: () {
setState(() {
_directional = AlignmentDirectional.center;
});
},
child: Text('center'),
),
RaisedButton(
onPressed: () {
setState(() {
_directional = AlignmentDirectional.centerEnd;
});
},
child: Text('centerEnd'),
),
RaisedButton(
onPressed: () {
setState(() {
_directional = AlignmentDirectional.bottomStart;
});
},
child: Text('bottomStart'),
),
RaisedButton(
onPressed: () {
setState(() {
_directional = AlignmentDirectional.bottomCenter;
});
},
child: Text('bottomCenter'),
),
RaisedButton(
onPressed: () {
setState(() {
_directional = AlignmentDirectional.bottomEnd;
});
},
child: Text('bottomEnd'),
),
],
),
Expanded(
child: Center(
child: Align(
alignment: _directional,
child: Text(
'i am text',
style: Theme.of(context).textTheme.headline,
),
),
),
),
],
),
);
}
}
-
Align
有个widthFactor
和heightFactor
,它们的作用是确定Align
本身的大小,Align
的大小(如果设置了*Factor
)会是子节点的宽/高*factor,比如说如果子节点宽100,widthFactor为2.0的话,那么Align
自身的宽度就为200.0,高度同理。 - 如果factor没有设置,且有约束(?什么意思)的话,那么
Align
会尽可能的大 - 如果没有约束,且factor为null的话,那么
Align
就是子节点的大小。
Center
-
Center
没什么好讲的,算是最常用的几种Widget之一了,就是Align
的一个特殊情况/别名,它本身也是继承了Align
类,它的widthFactor
和heightFactor
和Align
的一样。效果图:
源码:
import 'package:flutter/material.dart';
class CenterDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('CenterDemo')),
body: Center(
child: Text('i am in center'),
),
);
}
}
CustomSingleChildLayout
- [SingleChildLayoutDelegate]没有对外开放的已实现的实现类, flutter内建的实现类(但是是private的)的有(包括但不限于): _ToolbarContainerLayout(这个appbar内部使用的)。也就是说要用[CustomSingleChildLayout]的话, 就需要自己去实现[SingleChildLayoutDelegate]这里暂时就不去搞了。
LayoutId
// TODO
CustomMultiChildLayout
// TODO
SizedBox
- 强制子节点拥有
SizedBox
指定的大小。效果图:
源码:
import 'package:flutter/material.dart';
class SizedBoxDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('SizedBoxDemo')),
body: Center(
child: SizedBox(
width: 200.0,
height: 200.0,
child: Container(
color: Colors.red,
),
),
),
);
}
}
-
SizedBox
有个expand
命名构造函数,调用这个构造函数的时候,会生成一个撑满父节点的SizedBox
。
ConstrainedBox
- 对子节点施加额外的约束,但是所谓的最大(小)宽(高)度,到底是以什么策略进行变换还是搞不清楚。之后涉及到
BoxConstraint
的时候再来细看吧。效果图:
源码:
import 'package:flutter/material.dart';
class ConstrainedBoxDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('ConstrainedBoxDemo')),
body: Column(
children: <Widget>[
Flexible(
child: Container(
color: Colors.blueAccent,
),
),
ConstrainedBox(
constraints: BoxConstraints(minHeight: 100.0, maxHeight: 200.0),
child: Container(
color: Colors.red,
),
),
],
),
);
}
}
UnconstrainedBox
// TODO
FractionallySizedBox
- 对子节点施加一个百分比,这个百分比是
FractionallySizedBox
的可用空间的百分比。比较适合那种一个按钮占据一行的场景,可以设置按钮以屏幕宽为基准进行百分比缩小,这样的话在不同尺寸的设备上适应性会比较好。效果图:
源码:
import 'package:flutter/material.dart';
class FractionallySizedBoxDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('FractionallySizedBoxDemo')),
body: Container(
color: Colors.blueAccent,
constraints: BoxConstraints.expand(),
child: Column(
children: <Widget>[
FractionallySizedBox(
widthFactor: 0.5,
child: RaisedButton(
onPressed: () {},
child: Text('child1'),
),
),
SizedBox(height: 8.0),
FractionallySizedBox(
widthFactor: 0.8,
child: RaisedButton(
onPressed: () {},
child: Text('child2'),
),
),
SizedBox(height: 8.0),
FractionallySizedBox(
widthFactor: 0.6,
child: RaisedButton(
onPressed: () {},
child: Text('child2'),
),
),
],
),
),
);
}
}
LimitedBox
// TODO
OverflowBox
// TODO
SizedOverflowBox
// TODO
Offstage
// TODO
_OffstageElement
// TODO
AspectRatio
// TODO
IntrinsicWidth
// TODO
IntrinsicHeight
// TODO
Baseline
// TODO
SLIVERS
SliverToBoxAdapter
// TODO
SliverPadding
// TODO
LAYOUT NODES
ListBody
// TODO
Stack
// TODO
IndexedStack
// TODO
Positioned
// TODO
PositionedDirectional
// TODO
Flex
// TODO
Row
// TODO
Column
// TODO
Flexible
// TODO
Expanded
// TODO
Wrap
// TODO
Flow
// TODO
RichText
// TODO
RawImage
// TODO
DefaultAssetBundle
// TODO
WidgetToRenderBoxAdapter
// TODO
EVENT HANDLING
Listener
// TODO
RepaintBoundary
// TODO
IgnorePointer
- 此节点以及其子节点都将忽略点击事件,用
ignoring
参数区分是否忽略。效果图:
源码:
import 'package:flutter/material.dart';
class IgnorePointerDemo extends StatefulWidget {
@override
_IgnorePointerDemoState createState() {
return _IgnorePointerDemoState();
}
}
class _IgnorePointerDemoState extends State<IgnorePointerDemo> {
bool _ignore = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('IgnorePointerDemo')),
body: Container(
color: Colors.red,
alignment: AlignmentDirectional.center,
child: Column(
children: <Widget>[
Switch(
value: _ignore,
onChanged: (value) {
setState(() => _ignore = value);
},
),
IgnorePointer(
ignoring: _ignore,
child: RaisedButton(
onPressed: () => print('clicked!!'),
child: Text('ignore'),
),
),
],
),
),
);
}
}
- switch开关控制了是否忽略点击事件。
AbsorbPointer
- 这个控件会
absorb(吸收)
掉点击事件,跟其他框架里的swallow(吞)
是一个意思吧。这个控件和上个控件的区别在于,这个控件本身是能够响应点击事件的,它做的是阻止事件传播到它的子节点上去,而IgnorePointer
本身也不再接收事件了。效果图:
源码:
import 'package:flutter/material.dart';
class AbsorbPointerDemo extends StatefulWidget {
@override
_AbsorbPointerDemo createState() {
return _AbsorbPointerDemo();
}
}
class _AbsorbPointerDemo extends State<AbsorbPointerDemo> {
bool _absorb = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('AbsorbPointerDemo')),
body: Container(
color: Colors.red,
alignment: AlignmentDirectional.center,
child: Column(
children: <Widget>[
Switch(
value: _absorb,
onChanged: (value) {
setState(() => _absorb = value);
},
),
GestureDetector(
onTap: () => print('tap in GestureDetector'),
child: AbsorbPointer(
absorbing: _absorb,
child: RaisedButton(
onPressed: () => print('tap in RaisedButton'),
child: Text('absorb'),
),
),
),
],
),
),
);
}
}
-
GestureDetector
的响应反映AbsorbPointer
本身是可以接收事件的。
MetaData
// TODO
UTILITY NODES
Semantics
// TODO
MergeSemantics
// TODO
BlockSemantics
// TODO
ExcludeSemantics
// TODO
KeyedSubtree
// TODO
Builder
// TODO
StatefulBuilder
// TOD