Flutter UI基础控件和布局方式解析(1)

2019-06-28  本文已影响0人  AiLearn

一,基础控件

1,Text

1.1,继承关系
Object > Diagnosticable > DiagnosticableTree > Widget > StatelessWidget > Text

1.2 创建Text
Text一共有三种构造方法

func 注释
new Text() 构造方法创建,只能生成一种style
Text.rich() 静态方法创建,能够生成多种style
new RichText() 与Text.rich()一样

1.3 Text

 /// Creates a text widget.
  /// If the [style] argument is null, the text will use the style from the closest enclosing [DefaultTextStyle].
  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);
new Text(
              '学习Text',
              textAlign: TextAlign.center, //文本对齐方式  居中
              textDirection: TextDirection.ltr, //文本方向
              softWrap: false, //是否自动换行 false文字不考虑容器大小  单行显示   超出;屏幕部分将默认截断处理
              overflow: TextOverflow
                  .ellipsis, //文字超出屏幕之后的处理方式  TextOverflow.clip剪裁   TextOverflow.fade 渐隐  TextOverflow.ellipsis省略号
              textScaleFactor: 2.0, //字体显示的赔率
              maxLines: 10, //最大行数
              style: new TextStyle(
                decorationColor: const Color(0xffffffff), //线的颜色
                decoration: TextDecoration
                    .none, //none无文字装饰   lineThrough删除线   overline文字上面显示线    underline文字下面显示线
                decorationStyle: TextDecorationStyle
                    .solid, //文字装饰的风格  dashed,dotted虚线(简短间隔大小区分)  double三条线  solid两条线
                wordSpacing: 0.0, //单词间隙(负值可以让单词更紧凑)
                letterSpacing: 0.0, //字母间隙(负值可以让字母更紧凑)
                fontStyle: FontStyle.italic, //文字样式,斜体和正常
                fontSize: 20.0, //字体大小
                fontWeight: FontWeight.w900, //字体粗细  粗体和正常
                color: const Color(0xffffffff), //文字颜色
              )

1.4 Text.rich()、new RichText()

/// Creates a text widget with a [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);

  /// Creates a paragraph of rich text.
  const RichText({
    Key key,
    @required this.text,
    this.textAlign = TextAlign.start,
    this.textDirection,
    this.softWrap = true,
    this.overflow = TextOverflow.clip,
    this.textScaleFactor = 1.0,
    this.maxLines,
    this.locale,
  }) : assert(text != null) ,assert(textAlign != null), assert(softWrap != null), assert(overflow != null),
       assert(textScaleFactor != null), assert(maxLines == null || maxLines > 0), super(key: key);
new TextSpan(
                text: '拼接1',
              ),
              new TextSpan(
                text: '拼接2',
              ),
              new TextSpan(
                text: '拼接3',
              ),
              new TextSpan(
                text: '拼接4',
              ),
              new TextSpan(
                text: '拼接5',
              ),
              new TextSpan(
                text: '拼接6',
              ),
              new TextSpan(
                  text: '拼接7',
                  style: new TextStyle(
                    color: Colors.yellow,
                  ),
                  recognizer:new TapGestureRecognizer()..onTap=(){//增加一个点击事件
                    print('@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@');
                  },
              ),

2,Image

2.1,继承关系
Object > Diagnosticablet > DiagnosticableTreet > Widgett > StatefulWidgett > Image

2.2,支持类型
支持 JPEG、PNG、GIF、Animated GIF、WebP、Animated WebP、BMP 和 WBMP 等格式

2.3, 创建Image
Image一共有五种创建方式

func 注释
Image() 构造方法创建
Image.asset() 加载资源图片
Image.file() 加载本地图片
Image. network() 加载网络图片
Image.memory() 加载Uint8List资源图片

2.4, 创建Image


  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,
  }) : assert(image != null),
       assert(alignment != null),
       assert(repeat != null),
       assert(filterQuality != null),
       assert(matchTextDirection != null),
       super(key: key);
// 资源图片
new Image.asset('imgs/logo.jpeg'),
//网络图片
new Image.network(
    'https://flutter.io/images/homepage/header-illustration.png'),
// 本地文件图片
new Image.file(new File("/Users/gs/Downloads/1.jpeg")),
// Uint8List图片
new Image.memory(bytes),
//使用ImageProvider加载图片
new Image(image: new NetworkImage("https://flutter.io/images/homepage/screenshot-2.png"))

tips:
关于本地图片资源使用这里我需要说一下,首先在项目最顶部创建一个images文件夹放入一张图片xxx.jpeg,然后在pubspec.yaml添加几行代码,表示引用images文件夹下的xxx.jpeg图片,另外还可以设置2x和3x图片

image

2.5, 属性解析

2.6, 圆角图片

Image 是不支持圆角和阴影的,目前可以通过使用 CircleAvatar 和 Container 实现。
var img = 'https://b-ssl.duitang.com/uploads/item/' +
        '201602/15/20160215235057_EU3tS.thumb.700_0.jpeg';

new CircleAvatar(
    backgroundImage: new NetworkImage(url),
    radius: 100.0,      // --> 半径越大,图片越大
),
new Container(
    width: 200.0,
    height: 200.0,
    margin: const EdgeInsets.all(20.0),
    decoration: new BoxDecoration(
        color: Colors.white,
        image: new DecorationImage(image: new NetworkImage(this.imgsrc), fit: BoxFit.cover),
        shape: BoxShape.circle,
    ),
),

上面实现的都是一个圆形图片,下面的实现一个真正的圆角图片。

new Container(
    width: 200.0,
    height: 200.0,
    margin: const EdgeInsets.all(20.0),
    decoration: new BoxDecoration(
        color: Colors.white,
        image: new DecorationImage(image: new NetworkImage(this.imgsrc), fit: BoxFit.cover),
        shape: BoxShape.rectangle,              // <-- 这里需要设置为 rectangle
        borderRadius: new BorderRadius.all(
            const Radius.circular(20.0),        // <-- rectangle 时,BorderRadius 才有效
        ),
    ),
),

3,Button

3.1继承关系
Object > Diagnosticable > DiagnosticableTree > Widget > StatelessWidget > MaterialButton

在 Flutter 里有很多的 Button,包括了:MaterialButton、RaisedButton、FloatingActionButton、FlatButton、IconButton、ButtonBar、DropdownButton 等。
一般常用的 Button 是 MaterialButton、IconButton、FloatingActionButton

3.2构造方法


  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,
  }) : super(key: key);

tips:
VoidCallback onPressedValueChanged<bool> onHighlightChanged
VoidCallback onPressed: 点击激活按钮时调用的方法
ValueChanged<bool> onHighlightChanged: 按下和抬起时都会调用的方法

MaterialButton(
      key: ValueKey("text"),
      child: Text("MaterialButton"),
      onPressed: pressedBtn(context),
      onHighlightChanged: onHighlightChanged(context),
      textTheme: ButtonTextTheme.normal,
      textColor: Colors.blue,
      disabledTextColor: Colors.red,
      color: Color(0xFF82B1FF),
      disabledColor: Colors.grey,
      highlightColor: Colors.grey,
      // 按下的背景色
      splashColor: Colors.green,
      // 水波纹颜色
      colorBrightness: Brightness.light,
      // 主题
      elevation: 10,
      highlightElevation: 10,
      disabledElevation: 10,
      padding: EdgeInsets.all(10),
//       MaterialButton shape 子类才起效
      shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.all(Radius.circular(20)),
          side: BorderSide(
              color: Color(0xFFFFFFFF), style: BorderStyle.solid, width: 2)),
      clipBehavior: Clip.antiAlias,
      materialTapTargetSize: MaterialTapTargetSize.padded,
      animationDuration: Duration(seconds: 1),
      minWidth: 200,
      height: 50,
    );
new RaisedButton(
    child: new Text('点我'),
    onPressed: () {},
)
new FlatButton(
    child: new Text('点我'),
    onPressed: () {},
)
new IconButton(
    icon: new Icon(Icons.volume_up),
    tooltip: 'Increase volume by 10%',
    onPressed: () {
        // ...
    },
)

tips: 还有已经定义好的 Icon Button:CloseButton、BackButton。他们都有导航返回的能力。

new Scaffold(
    // ...
    floatingActionButton: new FloatingActionButton(
        onPressed: () {},
        child: new Icon(Icons.add_a_photo),
        elevation: 3.0,
        highlightElevation: 2.0,
        backgroundColor: Colors.red,        // 红色
    ),
)
new ButtonBar(
    children: <Widget>[
        new CloseButton(),
        new BackButton(),
    ],
)

4,Container

4.1继承关系
Object > Diagnosticable > DiagnosticableTree > Widget > StatelessWidget > Container
4.2构造方法

Container({
    Key key,
    this.alignment,
    this.padding,
    Color color,
    Decoration decoration,
    this.foregroundDecoration,
    double width,
    double height,
    BoxConstraints constraints,
    this.margin,
    this.transform,
    this.child,
  })

4.3用法

new Container(
  constraints: new BoxConstraints.expand(
    height:Theme.of(context).textTheme.display1.fontSize * 1.1 + 200.0,
  ),
  decoration: new BoxDecoration(
    border: new Border.all(width: 2.0, color: Colors.red),
    color: Colors.grey,
    borderRadius: new BorderRadius.all(new Radius.circular(20.0)),
    image: new DecorationImage(
      image: new NetworkImage('http://h.hiphotos.baidu.com/zhidao/wh%3D450%2C600/sign=0d023672312ac65c67506e77cec29e27/9f2f070828381f30dea167bbad014c086e06f06c.jpg'),
      centerSlice: new Rect.fromLTRB(270.0, 180.0, 1360.0, 730.0),
    ),
  ),
  padding: const EdgeInsets.all(8.0),
  alignment: Alignment.center,
  child: new Text('Hello World',
    style: Theme.of(context).textTheme.display1.copyWith(color: Colors.black)),
  transform: new Matrix4.rotationZ(0.3),
)

4.4 decoration
decoration可以设置边框、背景色、背景图片、圆角等属性,非常实用。对于transform这个属性,一般有过其他平台开发经验的,都大致了解,这种变换,一般不是变换的实际位置,而是变换的绘制效果,也就是说它的点击以及尺寸、间距等都是按照未变换前的。

decoration = decoration ?? (color != null ? new BoxDecoration(color: color) : null),

可以看出,对于颜色的设置,最后都是转换为decoration来进行绘制的。如果同时包含decoration和color两种属性,则会报错。

@override
  Widget build(BuildContext context) {
    Widget current = child;

    if (child == null && (constraints == null || !constraints.isTight)) {
      current = new LimitedBox(
        maxWidth: 0.0,
        maxHeight: 0.0,
        child: new ConstrainedBox(constraints: const BoxConstraints.expand())
      );
    }

    if (alignment != null)
      current = new Align(alignment: alignment, child: current);

    final EdgeInsetsGeometry effectivePadding = _paddingIncludingDecoration;
    if (effectivePadding != null)
      current = new Padding(padding: effectivePadding, child: current);

    if (decoration != null)
      current = new DecoratedBox(decoration: decoration, child: current);

    if (foregroundDecoration != null) {
      current = new DecoratedBox(
        decoration: foregroundDecoration,
        position: DecorationPosition.foreground,
        child: current
      );
    }

    if (constraints != null)
      current = new ConstrainedBox(constraints: constraints, child: current);

    if (margin != null)
      current = new Padding(padding: margin, child: current);

    if (transform != null)
      current = new Transform(transform: transform, child: current);

    return current;
  }
Container的build函数不长,绘制也是一个线性的判断的过程,一层一层的包裹着widget,去实现不同的样式。
最里层的是child,如果为空或者其他约束条件,则最里层包含的为一个LimitedBox,然后依次是Align、Padding、DecoratedBox、前景DecoratedBox、ConstrainedBox、Padding(实现margin效果)、Transform。
Container的源码本身并不复杂,复杂的是它的各种布局表现。我们谨记住一点,如果内部不设置约束,则按照父节点尽可能的扩大,如果内部有约束,则按照内部来。

5, Row&Column

5.1 Row

布局方式

-5.1.4 构造方法

Row({
  Key key,
  MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
  MainAxisSize mainAxisSize = MainAxisSize.max,
  CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
  TextDirection textDirection,
  VerticalDirection verticalDirection = VerticalDirection.down,
  TextBaseline textBaseline,
  List<Widget> children = const <Widget>[],
})

center:将children放置在主轴的中心;
end:将children放置在主轴的末尾;
spaceAround:将主轴方向上的空白区域均分,使得children之间的空白区域相等,但是首尾child的空白区域为1/2;
spaceBetween:将主轴方向上的空白区域均分,使得children之间的空白区域相等,首尾child都靠近首尾,没有间隙;
spaceEvenly:将主轴方向上的空白区域均分,使得children之间的空白区域相等,包括首尾child;
start:将children放置在主轴的起点;

其中spaceAround、spaceBetween以及spaceEvenly的区别,就是对待首尾child的方式。其距离首尾的距离分别是空白区域的1/2、0、1。
MainAxisSize:在主轴方向占有空间的值,默认是max。
MainAxisSize的取值有两种:

max:根据传入的布局约束条件,最大化主轴方向的可用空间;
min:与max相反,是最小化主轴方向的可用空间;

CrossAxisAlignment:children在交叉轴方向的对齐方式,与MainAxisAlignment略有不同。
CrossAxisAlignment枚举值有如下几种:

baseline:在交叉轴方向,使得children的baseline对齐;
center:children在交叉轴上居中展示;
end:children在交叉轴上末尾展示;
start:children在交叉轴上起点处展示;
stretch:让children填满交叉轴方向;

TextDirection:阿拉伯语系的兼容设置,一般无需处理。
VerticalDirection:定义了children摆放顺序,默认是down。
VerticalDirection枚举值有两种:

down:从top到bottom进行布局;
up:从bottom到top进行布局。

top对应Row以及Column的话,就是左边和顶部,bottom的话,则是右边和底部。
TextBaseline:使用的TextBaseline的方式,有两种,前面已经介绍过。

Row(
  children: <Widget>[
    Expanded(
      child: Container(
        color: Colors.red,
        padding: EdgeInsets.all(5.0),
      ),
      flex: 1,
    ),
    Expanded(
      child: Container(
        color: Colors.yellow,
        padding: EdgeInsets.all(5.0),
      ),
      flex: 2,
    ),
    Expanded(
      child: Container(
        color: Colors.blue,
        padding: EdgeInsets.all(5.0),
      ),
      flex: 1,
    ),
  ],
)

5.2 Flex

Flex({
  Key key,
  @required this.direction,
  this.mainAxisAlignment = MainAxisAlignment.start,
  this.mainAxisSize = MainAxisSize.max,
  this.crossAxisAlignment = CrossAxisAlignment.center,
  this.textDirection,
  this.verticalDirection = VerticalDirection.down,
  this.textBaseline,
  List<Widget> children = const <Widget>[],
})

tips: 可以看出,Flex的构造函数就比Row和Column的多了一个参数。Row跟Column的区别,正是这个direction参数的不同。当为Axis.horizontal的时候,则是Row,当为Axis.vertical的时候,则是Column。

while (child != null) {
  final FlexParentData childParentData = child.parentData;
  totalChildren++;
  final int flex = _getFlex(child);
  if (flex > 0) {
    totalFlex += childParentData.flex;
    lastFlexChild = child;
  } else {
    BoxConstraints innerConstraints;
    if (crossAxisAlignment == CrossAxisAlignment.stretch) {
      switch (_direction) {
        case Axis.horizontal:
          innerConstraints = new BoxConstraints(minHeight: constraints.maxHeight,
                                                maxHeight: constraints.maxHeight);
          break;
        case Axis.vertical:
          innerConstraints = new BoxConstraints(minWidth: constraints.maxWidth,
                                                maxWidth: constraints.maxWidth);
          break;
      }
    } else {
      switch (_direction) {
        case Axis.horizontal:
          innerConstraints = new BoxConstraints(maxHeight: constraints.maxHeight);
          break;
        case Axis.vertical:
          innerConstraints = new BoxConstraints(maxWidth: constraints.maxWidth);
          break;
      }
    }
    child.layout(innerConstraints, parentUsesSize: true);
    allocatedSize += _getMainSize(child);
    crossSize = math.max(crossSize, _getCrossSize(child));
  }
  child = childParentData.nextSibling;
}

5.3 Column
在讲解Row的时候,其实是按照Flex的一些布局行为来进行的,包括源码分析,也都是在用Flex进行分析的。Row和Column都是Flex的子类,只是direction参数不同。Column各方面同Row,因此在这里不再另行讲解。
在讲解Flex的时候,也说过是参照了web的Flex布局,如果有相关开发经验的同学,完全可以参照着去理解,这样子更容易去理解它们的用法和原理。
关于row和column转载链接:https://www.jianshu.com/p/0ce74751d970

6,Text

appBar: new AppBar(
    title: new Text('首页'),
    leading: new Icon(Icons.home),
    backgroundColor: Colors.blue,
    centerTitle: true,
    actions: <Widget>[
        // 非隐藏的菜单
        new IconButton(
            icon: new Icon(Icons.add_alarm),
            tooltip: 'Add Alarm',
            onPressed: () {}
        ),
        // 隐藏的菜单
        new PopupMenuButton<String>(
            itemBuilder: (BuildContext context) => <PopupMenuItem<String>>[
                this.SelectView(Icons.message, '发起群聊', 'A'),
                this.SelectView(Icons.group_add, '添加服务', 'B'),
                this.SelectView(Icons.cast_connected, '扫一扫码', 'C'),
            ],
            onSelected: (String action) {
                // 点击选项的时候
                switch (action) {
                    case 'A': break;
                    case 'B': break;
                    case 'C': break;
                }
            },
        ),
    ],
),
图示

toolbarOpacity → double

关于app的转载链接: https://www.jianshu.com/p/77f8b7ee8460

上一篇 下一篇

猜你喜欢

热点阅读