- Flutter和Dart系列文章
- 项目GitHub地址
Flutter
作为一种全新的响应式,跨平台,高性能, 完全免费、开源的移动开发框架
Widget
是Flutter
开发中的主要组成部分, 是Flutter
的基础, Flutter
的核心设计思想便是: 一切皆Widget
Flutter
中的widget
的概念更广泛,它不仅可以表示UI
元素,也可以表示一些功能性的组件如:用于手势检测的 GestureDetector
widget
、用于应用主题数据传递的Theme
等等
Widget
实际上就是Element
的配置数据, Widget
的功能是描述一个UI元素的一个配置数据, 而真正的UI渲染是由Element
构成
- 由于
Element
是通过Widget
生成,所以它们之间有对应关系,所以在大多数场景,我们可以宽泛地认为Widget
就是指UI控件或UI渲染
- 一个
Widget
对象可以对应多个Element
对象。这很好理解,根据同一份配置(Widget
),可以创建多个实例(Element
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| @immutable abstract class Widget extends DiagnosticableTree { const Widget({ this.key });
final Key key;
@protected Element createElement();
@override String toStringShort() { return key == null ? '$runtimeType' : '$runtimeType-$key'; }
@override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense; }
static bool canUpdate(Widget oldWidget, Widget newWidget) { return oldWidget.runtimeType == newWidget.runtimeType && oldWidget.key == newWidget.key; } }
|
Widget
类继承自DiagnosticableTree
,主要作用是提供调试信息。
Key
: 这个key
属性类似于React/Vue
中的key
,主要的作用是决定是否在下一次build
时复用旧的widget
,决定的条件在canUpdate()
方法中
createElement()
:正如前文所述一个Widget可以对应多个Element
;Flutter Framework
在构建UI时,会先调用此方法生成对应节点的Element
对象。此方法是Flutter Framework
隐式调用的,在我们开发过程中基本不会调用到。
debugFillProperties
复写父类的方法,主要是设置DiagnosticableTree
的一些特性。
canUpdate
是一个静态方法,它主要用于在Widget
树重新build
时复用旧的widget
- 具体来说就是:是否用新的
Widget
对象去更新旧UI上所对应的Element
对象的配置;
- 通过其源码我们可以看到,只要
newWidget
与oldWidget
的runtimeType
和key
同时相等时就会用newWidget
去更新Element
对象的配置,否则就会创建新的Element
StatelessWidget
是状态不可变的widget
, 初始状态设置以后就不可再变化, 如果需要变化需要重新创建; StatefulWidget
可以保存自己的状态
- 在
Flutter
中通过引入State
来保存状态, 当State
的状态改变时,能重新构建本节点以及孩子的Widget
树来进行UI变化
- 如果需要主动改变
State
的状态,需要通过setState()
方法进行触发,单纯改变数据是不会引发UI改变的
- 下面介绍部分的
Widget
组件
Text
UI上面文字的展示基本上都要靠Text
组件来完成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
const Text(this.data, { Key key, this.style, this.textAlign, this.textDirection, this.locale, this.softWrap, this.overflow, this.textScaleFactor, this.maxLines, this.semanticsLabel, }) : assert(data != null), textSpan = null, super(key: key);
const Text.rich(this.textSpan, { Key key, this.style, this.textAlign, this.textDirection, this.locale, this.softWrap, this.overflow, this.textScaleFactor, this.maxLines, this.semanticsLabel, }): assert(textSpan != null), data = null, super(key: key);
|
参数介绍
data
文本的内容
style
文本的样式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| const TextStyle({ this.inherit = true, this.color, this.fontSize, this.fontWeight, this.fontStyle, this.letterSpacing, this.wordSpacing, this.textBaseline, this.height, this.locale, this.foreground, this.background, this.shadows, this.decoration, this.decorationColor, this.decorationStyle, this.debugLabel, String fontFamily, String package, }) : fontFamily = package == null ? fontFamily : 'packages/$package/$fontFamily', assert(inherit != null), assert(color == null || foreground == null, _kColorForegroundWarning);
1. inherit: 为false时不显示
2. color: 字体颜色
3. fontSize: 字体大小, 默认是14.0
4. fontWeight: 字体的粗体, FontWeight.w500 5. fontStyle: 字体的样式 normal正常 italic 斜体
6. letterSpacing: 字符间距
7. wordSpacing: 单词间距
8. textBaseline alphabetic:用于对齐字母字符底部的水平线 ideographic:用于对齐表意字符的水平线 9. height: 用在Text控件上的时候,会乘以fontSize做为行高,
10. locale: 国际化
11. foreground: 用paint来渲染text,也可以用他来改变字体颜色等
12. background: 背景颜色
13. decoration: 下划线 underline、 删除线 lineThrough、上划线 overline,默认是无 none 14. decorationStyle: decoration线的样式 solid: 直线, double: 两条线, dotted: 短虚线, dashed: 长虚线, wavy: 波浪线
15. decorationColor: decoration线的颜色
16. debugLabel: 文本样式的描述, 该属性只在调试中维护
17. fontFamily和package(自定义字体的时候用的到,后面再详解)
|
使用样式示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| style: TextStyle( inherit: true, color: Colors.red, fontSize: 50, fontWeight: FontWeight.bold, fontStyle: FontStyle.italic, letterSpacing: 2, wordSpacing: 5, textBaseline: TextBaseline.alphabetic, height: 2, locale: Locale('CH'), decoration: TextDecoration.lineThrough, decorationColor: Colors.blue, decorationStyle: TextDecorationStyle.wavy, ),
|
textAlign
文本显示方向
1 2 3 4 5 6 7 8 9 10
| left: 居左显示 center: 居中显示 right: 居右显示 justify: 文本的拉伸行,其末尾用软换行符填充宽度 start: 对齐容器前缘的文本。 对于从左到右的文本([TextDirection.ltr]),这是左边缘。 对于从右到左的文本([TextDirection.rtl]),这是右边缘。 end: 对齐容器尾部边缘的文本。 对于从左到右的文本([TextDirection.ltr]),这是右边缘。 对于从右到左的文本([TextDirection.rtl]),这是左边缘。
|
textDirection
和上述TextAlign.start和TextAlign.end
一样
softWrap
文本是否能换行,bool类型
overflow
用来指定超出文本的表示方式,是截断文本啊还是用三个点显示等
1 2 3
| ellipsis: ...形式显示 clip: 直接截断 fade: 效果和clip一样
|
maxLines
用来指定文本最多显示多少行
textScaleFactor
文本字体的缩放倍数,如:1.5则在默认字体上变成1.5倍大小字体,0.5则是0.5倍
Text构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| child: Text( 'titanjun.top' * 3, textAlign: TextAlign.left, textDirection: TextDirection.ltr, locale: Locale('CH'), maxLines: 1, overflow: TextOverflow.fade, style: TextStyle( inherit: true, color: Colors.red, fontSize: 50, fontWeight: FontWeight.bold, fontStyle: FontStyle.italic, letterSpacing: 2, wordSpacing: 5, textBaseline: TextBaseline.alphabetic, height: 2, locale: Locale('CH'), decoration: TextDecoration.lineThrough, decorationColor: Colors.blue, decorationStyle: TextDecorationStyle.wavy, ), ),
|
Text.rich构造函数
这个构造函数和iOS
中用到的富文本类似
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| child: Text.rich( TextSpan( text: '博客地址: ', children: [ TextSpan( text: 'https://', style: TextStyle(color: Colors.red) ), TextSpan( text: 'titanjun.top', style: TextStyle(color: Colors.blue), ), TextSpan( text: '欢迎访问', style: TextStyle(color: Colors.orange) ), ] ), ),
|
其中TextSpan
的构造函数如下
1 2 3 4 5 6 7 8
| const TextSpan({ this.style, this.text, this.children, this.recognizer, });
|
Image
- 一个用于展示图片的组件。支持 JPEG、PNG、GIF、Animated GIF、WebP、Animated WebP、BMP 和 WBMP 等格式
Image
共有五种构造函数
Image()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| const Image({ Key key, @required this.image, this.semanticLabel, this.excludeFromSemantics = false, this.width, this.height, this.color, this.colorBlendMode, this.fit, this.alignment = Alignment.center, this.repeat = ImageRepeat.noRepeat, this.centerSlice, this.matchTextDirection = false, this.gaplessPlayback = false, this.filterQuality = FilterQuality.low, })
|
部分属性详解
width、height:
- 用于设置图片的宽、高,当不指定宽高时,图片会根据当前父容器的限制,尽可能的显示其原始大小
- 如果只设置
width
、height
的其中一个,那么另一个属性默认会按比例缩放
- 但可以通过下面介绍的fit属性来指定适应规则
fit
图像在布局中分配的空间, BoxFit
枚举值
fill
: 填充满容器空间, 图片会被拉伸
contain
: 以容器的大小等比例缩放图片
cover
: 填充整个容器, 图片会被剪切
fitWidth
: 以容器的宽度, 等比例缩放图片
fitHeight
: 以容器的高度, 等比例的缩放图片
none
: 以图片的实际大小显示
scaleDown
: 居中显示, 图片不会拉伸, 以宽高中最小的尺寸为标准
alignment
图像边界内对齐图像, Alignment
类, 不是枚举值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| static const Alignment topLeft = Alignment(-1.0, -1.0); static const Alignment topCenter = Alignment(0.0, -1.0); static const Alignment topRight = Alignment(1.0, -1.0); static const Alignment centerLeft = Alignment(-1.0, 0.0); static const Alignment center = Alignment(0.0, 0.0); static const Alignment centerRight = Alignment(1.0, 0.0); static const Alignment bottomLeft = Alignment(-1.0, 1.0); static const Alignment bottomCenter = Alignment(0.0, 1.0); static const Alignment bottomRight = Alignment(1.0, 1.0);
alignment: Alignment.topLeft,
alignment: Alignment(0.0, 1.0)
|
1 2 3 4 5
| Image( image: NetworkImage('https://titanjun.oss-cn-hangzhou.aliyuncs.com/flutter/flutter.jpeg?x-oss-process=style/titanjun'), fit: BoxFit.scaleDown, alignment: Alignment.topLeft, ),
|
color和 colorBlendMode
在图片绘制时可以对每一个像素进行颜色混合处理,color
指定混合色,而colorBlendMode
指定混合模式;
repeat
当图片本身大小小于显示空间时,指定图片的重复规则
FadeInImage
可以用于添加占位图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class FadeInImage extends StatelessWidget { const FadeInImage({ Key key, @required this.placeholder, this.placeholderErrorBuilder, @required this.image, this.imageErrorBuilder, this.excludeFromSemantics = false, this.imageSemanticLabel, this.fadeOutDuration = const Duration(milliseconds: 300), this.fadeOutCurve = Curves.easeOut, this.fadeInDuration = const Duration(milliseconds: 700), this.fadeInCurve = Curves.easeIn, this.width, this.height, this.fit, this.alignment = Alignment.center, this.repeat = ImageRepeat.noRepeat, this.matchTextDirection = false, }) }
|
ImageProvider
用于显示图片的抽象类, 不能直接使用, 需要使用其子类
用于显示网络图片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| abstract class NetworkImage extends ImageProvider<NetworkImage> { const factory NetworkImage(String url, { double scale, Map<String, String> headers }) = network_image.NetworkImage; }
class FileImage extends ImageProvider<FileImage> { const FileImage(this.file, { this.scale = 1.0 }) : assert(file != null), assert(scale != null); }
class MemoryImage extends ImageProvider<MemoryImage> { const MemoryImage(this.bytes, { this.scale = 1.0 }) : assert(bytes != null), assert(scale != null); }
class AssetImage extends AssetBundleImageProvider { const AssetImage( this.assetName, { this.bundle, this.package, }) : assert(assetName != null); }
|
Image.network
用于显示网络图片
1 2 3 4 5 6 7
| Image.network( 'https://titanjun.oss-cn-hangzhou.aliyuncs.com/flutter/catimage.jpg', width: 100, height: 100, fit: BoxFit.scaleDown, alignment: Alignment.center, )
|
网络请求Image
是最常见的操作, 这里重点说明两个点
缓存
ImageCache
是ImageProvider
默认使用的图片缓存。ImageCache
使用的是LRU
的算法
- 默认可以存储1000张图片。如果觉得缓存太大,可以通过设置
ImageCache
的maximumSize
属性来控制缓存图片的数量。
- 也可以通过设置
maximumSizeBytes
来控制缓存的大小(默认缓存大小10MB)
CDN优化
如果想要使用cdn
优化,可以通过url
增加后缀的方式实现。默认实现中没有这个点,但是考虑到cdn
优化的可观收益,建议大家利用好这个优化
Image.asset
Flutter
应用程序可以包含代码和 assets
(有时称为资源)
asset
是打包到程序安装包中的,可在运行时访问
- 常见类型的
asset
包括静态数据(例如JSON文件),配置文件,图标和图片(JPEG,WebP,GIF,动画WebP / GIF,PNG,BMP和WBMP)
Flutter
使用pubspec.yaml
文件(位于项目根目录),来识别应用程序所需的asset
- 图片所在的文件夹
images
和pubspec.yaml
需要在同一目录下, 否则pubspec.yaml
文件中, 设置资源路径的时候要对应修改
images
图片文件夹中2.0x和3.0x
图片要分别创建两个文件夹, 并把2倍和3倍图分别放在不同的文件夹中, 切文件的名字不要在带@2x和@3x
字样
1 2 3 4 5 6 7
| Image.asset( 'images/home.png', width: 100, height: 100, fit: BoxFit.scaleDown, alignment: Alignment.center, )
|
Image.file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Image.file(File file, { Key key, double scale = 1.0, this.semanticLabel, this.excludeFromSemantics = false, this.width, this.height, this.color, this.colorBlendMode, this.fit, this.alignment = Alignment.center, this.repeat = ImageRepeat.noRepeat, this.centerSlice, this.matchTextDirection = false, this.gaplessPlayback = false, this.filterQuality = FilterQuality.low, })
|
- 主要解析file参数,其他与
Image()
构造的参数一致!
file
: 对文件系统上的文件的引用。
- File 实例是一个对象,它包含可以在其上执行操作的路径
Image.memory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Image.memory(Uint8List bytes, { Key key, double scale = 1.0, this.semanticLabel, this.excludeFromSemantics = false, this.width, this.height, this.color, this.colorBlendMode, this.fit, this.alignment = Alignment.center, this.repeat = ImageRepeat.noRepeat, this.centerSlice, this.matchTextDirection = false, this.gaplessPlayback = false, this.filterQuality = FilterQuality.low, })
|
- 加载
Uint8List
资源图片
- 主要解析
bytes
参数,其他与Image()
构造的参数一致!
Icon
Flutter
中,Icon
是类似于web
开发中一样使用iconfont
(字体图标),它是将图标做成字体文件,然后通过指定不同的字符而显示不同的图片
- 在字体文件中,每一个字符都对应一个位码,而每一个位码对应一个显示字形,不同的字体就是指字形不同,即字符对应的字形是不同的
- 而在
iconfont
中,只是将位码对应的字形做成了图标,所以不同的字符最终就会渲染成不同的图标。
- 在
Flutter
中iconfont
相较于图片的优势如下:
- 体积小:可以减小安装包大小。
- 矢量的:
iconfont
都是矢量图标,放大不会影响其清晰度。
- 可以应用文本样式:可以像文本一样改变字体图标的颜色、大小对齐等。
- 可以通过
TextSpan
和文本混用。
使用Material Design字体图标
Flutter
默认包含了一套Material Design
的字体图标,在pubspec.yaml
文件中的配置如下
1 2
| flutter: uses-material-design: true
|
如果设置成false, 则图片效果如下, 图片颜色为自己设置的颜色
在Text中使用
下面看一个在Text
中使用iconfont
的示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| String iconStr = "";
iconStr += "\uE914";
iconStr += " \uE000";
iconStr += " \uE90D";
Text(iconStr, style: TextStyle( fontFamily: "MaterialIcons", fontSize: 80.0, color: Colors.green ), )
|
上述代码的运行效果如下
任何一个图片我们都可以使用Text
文本进行展示, 但是这需要我们提供每一个图标的字符码点, 可在material-design-icons中搜索查找, 而且并不能固定指定图片的大小, 只能设置字体的大小, 这并对开发者不友好
Icon介绍
Flutter
封装了一个Icon
来专门显示字体图标,上面的例子也可以用如下方式实现
1 2 3 4 5 6 7 8
| Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Icon(Icons.accessible, color: Colors.green, size: 80), Icon(Icons.error, color: Colors.green, size: 80), Icon(Icons.fingerprint, color: Colors.green, size: 80), ], )
|
Icons
类中包含了所有Material Design
图标的IconData
静态变量定义, …..我大概算了一下, Icons
中大概一共定义了984中图标
1 2 3 4 5 6 7 8 9 10 11 12
| const Icon(this.icon, { Key key, this.size, this.color, this.semanticLabel, this.textDirection, })
|
圆形图片
在Flutter
中实现圆角效果也是使用一些Widget
来实现的
CircleAvatar
CircleAvatar
可以实现圆角头像,也可以添加一个子Widget
1 2 3 4 5 6 7 8 9 10 11 12 13
| class CircleAvatar extends StatelessWidget { const CircleAvatar({ Key key, this.child, this.backgroundColor, this.backgroundImage, this.foregroundColor, this.radius, this.minRadius, this.maxRadius, }) }
|
简单使用实现一个圆形头像:
- 这里我们使用的是
NetworkImage
,因为backgroundImage
要求我们传入一个ImageProvider
;
- 这里我还在里面添加了一个文字,但是我在文字外层包裹了一个
Container
;
- 这里
Container
的作用是为了可以控制文字在其中的位置调整
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class HomeContent extends StatelessWidget { @override Widget build(BuildContext context) { return Center( child: CircleAvatar( radius: 100, backgroundImage: NetworkImage("https://tva1.sinaimg.cn/large/006y8mN6gy1g7aa03bmfpj3069069mx8.jpg"), child: Container( alignment: Alignment(0, .5), width: 200, height: 200, child: Text("圣诞节") ), ), ); } }
|
ClipOval
ClipOval
也可以实现圆角头像,而且通常是在只有头像时使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class ClipOval extends SingleChildRenderObjectWidget { const ClipOval({ Key key, this.clipper, this.clipBehavior = Clip.antiAlias, Widget child }): assert(clipBehavior != null), super(key: key, child: child); }
class HomeContent extends StatelessWidget { @override Widget build(BuildContext context) { return Center( child: ClipOval( child: Image.network( "https://tva1.sinaimg.cn/large/006y8mN6gy1g7aa03bmfpj3069069mx8.jpg", width: 200, height: 200, ), ), ); } }
|
BoxDecoration
通过Container
和Container
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class CirCleImage extends StatelessWidget { const CirCleImage({Key key}) : super(key: key);
@override Widget build(BuildContext context) { return Container( child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(50)) ), clipBehavior: Clip.antiAlias, child: Image.network( 'https://titanjun.oss-cn-hangzhou.aliyuncs.com/flutter/flutter_scroll.png', width: 100, height: 100, fit: BoxFit.fitHeight, ), ), ); } }
|
图片圆角
ClipRRect
ClipRRect
用于实现圆角效果,可以设置圆角的大小。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class HomeContent extends StatelessWidget { @override Widget build(BuildContext context) { return Center( child: ClipRRect( borderRadius: BorderRadius.circular(10), child: Image.network( 'https://titanjun.oss-cn-hangzhou.aliyuncs.com/flutter/flutter_scroll.png', width: 200, height: 200, ), ), ); } }
|
按钮
Flutter
提供了RaisedButton
、FlatButton
、OutlineButton
和IconButton
四种按钮, 除了IconButton
之外都是继承自MaterialButton
- 所有
Material
库中的按钮都有如下相同点:
- 按下时都会有“水波动画”。
- 有一个
onPressed
属性来设置点击回调,当按钮按下时会执行该回调,如果不提供该回调则按钮会处于禁用状态,禁用状态不响应用户点击
MaterialButton
是除IconButton
按钮之外的其他按钮的父类, 下面介绍一下各属性的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| const MaterialButton({ Key key, @required this.onPressed, this.onHighlightChanged, this.textTheme, this.textColor, this.disabledTextColor, this.color, this.disabledColor, this.highlightColor, this.splashColor, this.colorBrightness, this.elevation, this.highlightElevation, this.disabledElevation, this.padding, this.shape, this.clipBehavior = Clip.none, this.materialTapTargetSize, this.animationDuration, this.minWidth, this.height, this.child, })
|
onPressed
按钮触发时触发的函数,如果不设置此属性Button
为不可用状态
1
| onPressed: () => print('被点击了'),
|
textTheme
按钮字体的主题, 在onPressed
不为空的时候才有效果
1 2 3 4
| ButtonTextTheme.normal ButtonTextTheme.accent ButtonTextTheme.primary
|
colorBrightness
设置按钮的字体亮度, 取值分别是Brightness.light
和Brightness.darks
padding
内边距,其接收值的类型是EdgeInsetsGeometry
类型的,EdgeInsetsGeometry
是一个抽象类, 只能使用其子类EdgeInsets
来实现
1
| padding: EdgeInsets.all(10)
|
shape
- 设置按钮的形状,其接收值是
ShapeBorder
类型,ShapeBorder
也是一个抽象类
ShapeBorder
的子类中比较常用的几个如下所示
BeveledRectangleBorder
带斜角的长方形边框
CircleBorder
圆形边框
RoundedRectangleBorder
圆角矩形
StadiumBorder
两端是半圆的边框
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| const BeveledRectangleBorder({ this.side = BorderSide.none, this.borderRadius = BorderRadius.zero, })
const CircleBorder({ this.side = BorderSide.none })
const RoundedRectangleBorder({ this.side = BorderSide.none, this.borderRadius = BorderRadius.zero, })
const StadiumBorder({ this.side = BorderSide.none })
const BorderSide({ this.color = const Color(0xFF000000), this.width = 1.0, this.style = BorderStyle.solid, })
|
下面就来看一下shape
的配置和使用, 设置默认状态(即所有的边框样式和圆角都是默认值)的效果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| children: <Widget>[ RaisedButton( child: Text('BeveledRectangleBorder'), onPressed: () => print('RaisedButton'), shape: BeveledRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(10)), side: BorderSide( color: Colors.red, width: 2, style: BorderStyle.solid ) ), ), RaisedButton( child: Icon(Icons.supervisor_account, color: Colors.green, size: 40), onPressed: () => print('RaisedButton'), padding: EdgeInsets.all(10), shape: CircleBorder( side: BorderSide( color: Colors.red, width: 2, style: BorderStyle.solid ) ), ), RaisedButton( child: Text('RoundedRectangleBorder'), onPressed: () => print('RaisedButton'), shape: RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(10)), side: BorderSide( color: Colors.red, width: 2, style: BorderStyle.solid ) ), ), RaisedButton( child: Text('StadiumBorder'), onPressed: () => print('RaisedButton'), shape: StadiumBorder( side: BorderSide( color: Colors.red, width: 2, style: BorderStyle.solid ) ), ), ],
|
上述代码是分别设置圆角和边框后的代码, 效果如下
RaisedButton
即”漂浮”按钮,它默认带有阴影和灰色背景。按下后阴影会变大
RaisedButton
继承自MaterialButton
, 相关属性和父类一样
1 2 3 4
| RaisedButton( child: Text('RaisedButton'), onPressed: () => print('RaisedButton'), )
|
FlatButton
即扁平按钮,默认背景透明并不带阴影。按下后,会有背景色
RaisedButton
继承自MaterialButton
, 相关属性和父类一样
1 2 3 4
| FlatButton( child: Text('FlatButton'), onPressed: () => print('FlatButton'), )
|
OutlineButton
默认有一个边框,不带阴影且背景透明。按下后,边框颜色会变亮、同时出现背景和阴影(较弱)
RaisedButton
继承自MaterialButton
, 相关属性和父类一样
1 2 3 4
| OutlineButton( child: Text('OutlineButton'), onPressed: () => print('OutlineButton'), )
|
IconButton
是一个可点击的Icon,不包括文字,默认没有背景,点击后会出现背景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class IconButton extends StatelessWidget { const IconButton({ Key key, this.iconSize = 24.0, this.padding = const EdgeInsets.all(8.0), this.alignment = Alignment.center, @required this.icon, this.color, this.highlightColor, this.splashColor, this.disabledColor, @required this.onPressed, this.tooltip }) }
|
使用示例
1 2 3 4 5 6 7 8
| IconButton( icon: Icon(Icons.mail_outline, color:Colors.orange, size: 40), color: Colors.yellow, iconSize: 100, alignment: Alignment.topLeft, onPressed: () => print('IconButton'), tooltip: 'titanjun.top', )
|
图文按钮
每一个继承自MaterialButton
的按钮Widget
都有一个工厂构造函数, 返回一个图片在左, 文字在右的按钮
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| factory RaisedButton.icon({ .... @required Widget icon, @required Widget label, }) factory FlatButton.icon({ .... @required Widget icon, @required Widget label, }) factory OutlineButton.icon({ .... @required Widget icon, @required Widget label, })
RaisedButton.icon( label: Text('data'), icon: Icon(Icons.mail), onPressed: () => {}, ),
|
单选开关和复选框
Material widgets
库中提供了Material
风格的单选开关Switch
和复选框Checkbox
,它们都是继承自StatelessWidget
- 它们本身不会保存当前选择状态,所以一般都是在父
widget
中管理选中状态
- 当用户点击
Switch
或Checkbox
时,它们会触发onChanged
回调,我们可以在此回调中处理选中状态改变逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| const Switch({ Key key, @required this.value, @required this.onChanged, this.activeColor, this.activeTrackColor, this.inactiveThumbColor, this.inactiveTrackColor, this.activeThumbImage, this.inactiveThumbImage, this.materialTapTargetSize, })
const Checkbox({ Key key, @required this.value, this.tristate = false, @required this.onChanged, this.activeColor, this.materialTapTargetSize, })
|
使用代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| children: <Widget>[ Switch( value: false, onChanged: (value) {}, activeColor: Colors.red, activeTrackColor: Colors.yellow, inactiveThumbColor: Colors.blue, inactiveTrackColor: Colors.cyan, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap ), Checkbox( value: true, onChanged: (value) { }, activeColor: Colors.orange, tristate: true, ) ],
|
参考文档