Flutter之基础Widget

2019-04-19  本文已影响0人  TitanCoder
Flutter Flutter框架

Widget与Element

Widget类的声明

@immutable
abstract class Widget extends DiagnosticableTree {
  /// Initializes [key] for subclasses.
  const Widget({ this.key });

  /// See also the discussions at [Key] and [GlobalKey].
  final Key key;

  /// multiple times.
  @protected
  Element createElement();

  /// A short, textual description of this widget.
  @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;
  }
}

StatelessWidget和StatefulWidget的区别

Text

UI上面文字的展示基本上都要靠Text组件来完成

// 两种构造函数
// 显示普通的文本
 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);

  /// 段落式文本,可以给文本中的每个textSpan设置其样式
  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

文本的内容

Text('titanjun')

style

文本的样式

  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: 字体的粗体

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(自定义字体的时候用的到,后面再详解)

使用样式示例

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

文本显示方向

left: 居左显示
center: 居中显示
right: 居右显示
justify: 文本的拉伸行,其末尾用软换行符填充宽度
start: 对齐容器前缘的文本。
    对于从左到右的文本([TextDirection.ltr]),这是左边缘。
    对于从右到左的文本([TextDirection.rtl]),这是右边缘。
end: 对齐容器尾部边缘的文本。
    对于从左到右的文本([TextDirection.ltr]),这是右边缘。
    对于从右到左的文本([TextDirection.rtl]),这是左边缘。

textDirection

和上述TextAlign.start和TextAlign.end一样

softWrap

文本是否能换行,bool类型

overflow

用来指定超出文本的表示方式,是截断文本啊还是用三个点显示等

ellipsis: ...形式显示
clip: 直接截断
fade: 效果和clip一样

maxLines

用来指定文本最多显示多少行

textScaleFactor

文本字体的缩放倍数,如:1.5则在默认字体上变成1.5倍大小字体,0.5则是0.5倍

Text构造函数

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中用到的富文本类似

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)
        ),
      ]
    ),
),
Text.rich

其中TextSpan的构造函数如下

const TextSpan({
    this.style,
    this.text,
    // 接受List<TextSpan>类型的数组
    this.children,
    // 文本的手势操作, 后面说这个
    this.recognizer,
});

Image

image

Image()

  const Image({
    Key key,
    // 一个图片对象ImageProvider, 可设置NetworkImage(), FileImage(), MemoryImage()三种对象
    @required this.image,
    // 图片的描述, String
    this.semanticLabel,
    this.excludeFromSemantics = false,
    // 图片的宽度, double
    this.width,
    // 图片的高度, double
    this.height,
    // 图像的颜色, 用于和图片混合的颜色, 结合colorBlendMode使用
    this.color,
    // 颜色和图片混合的状态, BlendMode
    this.colorBlendMode,
    // 图像在布局中分配的空间, BoxFit
    this.fit,
    // 图像边界内对齐图像, Alignment
    this.alignment = Alignment.center,
    // 未充分填充容器时,是否重复显示图片
    this.repeat = ImageRepeat.noRepeat,
    // 九片图像的中心切点, Rect
    this.centerSlice,
    // 是否在图像的方向上绘制图像 TextDirection
    this.matchTextDirection = false,
    // 当图像提供者发生变化时,是继续显示旧图像(true)还是暂时不显示(false)
    this.gaplessPlayback = false,
    // 设置图片的过滤质量
    this.filterQuality = FilterQuality.low,
  }) 

部分属性详解

fit

图像在布局中分配的空间, BoxFit枚举值

alignment

图像边界内对齐图像, Alignment类, 不是枚举值

  /// 定义方式为垂直方向-水平方向
  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)
Image(
  image: NetworkImage('https://titanjun.oss-cn-hangzhou.aliyuncs.com/flutter/flutter.jpeg?x-oss-process=style/titanjun'),
  fit: BoxFit.scaleDown,
  alignment: Alignment.topLeft,
),

Image.network

用于显示网络图片

Image.network(
  'https://titanjun.oss-cn-hangzhou.aliyuncs.com/flutter/catimage.jpg',
  width: 100,
  height: 100,
  fit: BoxFit.scaleDown,
  alignment: Alignment.center,
)

网络请求Image是最常见的操作, 这里重点说明两个点

缓存

CDN优化

如果想要使用cdn优化,可以通过url增加后缀的方式实现。默认实现中没有这个点,但是考虑到cdn优化的可观收益,建议大家利用好这个优化

Image.asset

image

需要注意的是

image
Image.asset(
  'images/home.png',
  width: 100,
  height: 100,
  fit: BoxFit.scaleDown,
  alignment: Alignment.center,
)

Image.file

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,
  })

Image.memory

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,
  })

Icon

使用Material Design字体图标

Flutter默认包含了一套Material Design的字体图标,在pubspec.yaml文件中的配置如下

flutter:
  uses-material-design: true

<div class="note warning"><p>如果设置成false, 则图片效果如下, 图片颜色为自己设置的颜色</p></div>

image

在Text中使用

下面看一个在Text中使用iconfont的示例

String iconStr = "";
// accessible: &#xE914; or 0xE914 or E914
iconStr += "\uE914";
// error: &#xE000; or 0xE000 or E000
iconStr += " \uE000";
// fingerprint: &#xE90D; or 0xE90D or E90D
iconStr += " \uE90D";


Text(iconStr,
  style: TextStyle(
    fontFamily: "MaterialIcons",
    fontSize: 80.0,
    color: Colors.green
  ),
)

上述代码的运行效果如下

image

任何一个图片我们都可以使用Text文本进行展示, 但是这需要我们提供每一个图标的字符码点, 可在material-design-icons中搜索查找, 而且并不能固定指定图片的大小, 只能设置字体的大小, 这并对开发者不友好

Icon介绍

Flutter封装了一个Icon来专门显示字体图标,上面的例子也可以用如下方式实现

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中图标

// icon的构造函数
const Icon(this.icon, {
    Key key,
    // double, 设置图片的大小, 同事设置宽高
    this.size,
    // Color, 设置图片的颜色
    this.color,
    // String, 图标的语义标签
    this.semanticLabel,
    // TextDirection, 从左/右开始排列
    this.textDirection,
})

按钮

MaterialButton

MaterialButton是除IconButton按钮之外的其他按钮的父类, 下面介绍一下各属性的使用

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,
    // 按钮底部阴影效果的偏移量, double
    this.elevation,
    // 高亮状态下, 按钮底部阴影效果的偏移量, double
    this.highlightElevation,
    // 禁用状态下, 按钮底部阴影效果的偏移量, double
    this.disabledElevation,
    // 内边距
    this.padding,
    // 按钮的形状
    this.shape,
    this.clipBehavior = Clip.none,
    this.materialTapTargetSize,
    this.animationDuration,
    // 按钮的最小宽度
    this.minWidth,
    // 按钮的高度
    this.height,
    // 子widget
    this.child,
})

onPressed

按钮触发时触发的函数,如果不设置此属性Button为不可用状态

onPressed: () => print('被点击了'),

textTheme

按钮字体的主题, 在onPressed不为空的时候才有效果

/// 三个取值
ButtonTextTheme.normal
ButtonTextTheme.accent
ButtonTextTheme.primary
image

colorBrightness

设置按钮的字体亮度, 取值分别是Brightness.lightBrightness.darks

image

padding

内边距,其接收值的类型是EdgeInsetsGeometry类型的,EdgeInsetsGeometry是一个抽象类, 只能使用其子类EdgeInsets来实现

padding: EdgeInsets.all(10)

shape

// 带斜角的长方形边框
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),
    // 边框宽度, 默认1.0
    this.width = 1.0,
    // 边框样式, solid: 实线边框(默认值), none: 不显示边框
    this.style = BorderStyle.solid,
})

下面就来看一下shape的配置和使用, 设置默认状态(即所有的边框样式和圆角都是默认值)的效果如下

image
children: <Widget>[
  // BeveledRectangleBorder
  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
      )
    ),
  ),
],

上述代码是分别设置圆角和边框后的代码, 效果如下

image

RaisedButton

RaisedButton(
    child: Text('RaisedButton'),
    onPressed: () => print('RaisedButton'),
)

FlatButton

FlatButton(
    child: Text('FlatButton'),
    onPressed: () => print('FlatButton'),
)

OutlineButton

OutlineButton(
    child: Text('OutlineButton'),
    onPressed: () => print('OutlineButton'),
)

IconButton

IconButton是一个可点击的Icon,不包括文字,默认没有背景,点击后会出现背景

// 继承自StatelessWidget
class IconButton extends StatelessWidget {
  const IconButton({
    Key key,
    // 按钮的大小
    this.iconSize = 24.0,
    // 内边距
    this.padding = const EdgeInsets.all(8.0),
    // 按钮中图片的对齐方式
    this.alignment = Alignment.center,
    // 图片按钮的图片Icon
    @required this.icon,
    // 背景色
    this.color,
    // 高亮状态下的背景色
    this.highlightColor,
    // 按钮按下时, 水波纹的颜色
    this.splashColor,
    // 禁用状态下, 按钮的背景色
    this.disabledColor,
    // 点击事件
    @required this.onPressed,
    // String, 描述按钮按下时的描述文本, 需要长按才能出现(黑框显示文本)
    this.tooltip
  })
}

使用示例

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都有一个工厂构造函数, 返回一个图片在左, 文字在右的按钮

factory RaisedButton.icon({
    // 这里包含MaterialButton的所有属性
    ....
    // 图片Widget
    @required Widget icon,
    // 文字Widget
    @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: () => {},
),

单选开关和复选框

// Switch属性
const Switch({
    Key key,
    // Switch的状态值, true开启, false关闭
    @required this.value,
    // Switch改变状态所执行的操作
    @required this.onChanged,
    // 开启状态下选项条的颜色
    this.activeColor,
    // 开启状态下圆球的颜色
    this.activeTrackColor,
    // 关闭状态下选项条的颜色
    this.inactiveThumbColor,
    // 关闭状态下圆球的颜色
    this.inactiveTrackColor,
    // 设置开启状态下圆球的图片
    this.activeThumbImage,
    // 设置关闭状态下圆球的图片
    this.inactiveThumbImage,
    // 设置Switch的尺寸样式, padded: 建议大小48, shrinkWrap: 可能的最小尺寸
    this.materialTapTargetSize,
})

// Checkbox属性
const Checkbox({
    Key key,
    // Switch的状态值, true选中, false未选中
    @required this.value,
    // 如果为 true,那么复选框的值可以是 true,false 或 null
    // 如果为false(默认值), 那么只有true和false两种状态
    this.tristate = false,
    // 改变状态时执行的函数
    @required this.onChanged,
    // 选中时的颜色
    this.activeColor,
    // 设置Checkbox的尺寸样式, padded: 建议大小48, shrinkWrap: 可能的最小尺寸
    this.materialTapTargetSize,
})

使用代码

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,
  )
],
image

欢迎您扫一扫下面的微信公众号,订阅我的博客!

微信公众号
上一篇 下一篇

猜你喜欢

热点阅读