Evil Mouth's Blog

Android View - Flutter Widget

August 13, 2020

主要列举 Android 常用的 View 对应 Flutter 的 Widget

TextView - Text

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello World!"
    android:textColor="@android:colorholo_red_dark"
    android:textSize="18sp" />
Text(
  'Hello World!',
  style: TextStyle( // 文本样式
    color: Colors.red,
    fontSize: 18,
  ),
)

富文本

在 Android 实现,比如部分文字是可点击的、不同颜色的,是通过 Spannable 实现,而在 Flutter 通过组合 TextSpan Widget 并包裹在 Text.rich 里面

Text.rich(TextSpan())

宽高限制

在 Android 的 View 中有个通用属性 maxWidth,将它设置给 TextView 可以控制其最大宽度。而在 Flutter 的 Text 中并没有该属性,而是通过组合 ConstrainedBox 控制

ConstrainedBox(
  constraints: BoxConstraints( // 对子的约束
    maxWidth: 50,
  ),
  child: Text('123456789'),
)

Button - RaisedButton

<Button
    android:text="Click Me!"
    android:onClick="xxx"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
RaisedButton(
  onPressed: () {}, // 点击回调
  child: Text('Click Me!'),
)

RaisedButton 并没有提供直接的 text 属性,而是通过包裹一个 child 去显示按钮上的文字,而且该 child 可以是任何 Widget 而不必须是 Text

从而也可看出 Flutter 的各个 Widget 都比较轻量,各自担起各自的职责,而不是像 Android View 一样拥有各种通用属性导致 View 臃肿

普通文本如何点击

在 Android 中,TextView 继承自 View,而 View 都可以设置点击事件。而在 Flutter 中,Text 是没有提供像 RaisedButton 的 onPressed 回调的,这样说明各个 Widget 只负责各自的功能,那 Text 如何实现点击呢

GestureDetector(
  onTap: () {}, // 点击回调
  child: Text('this is Text, try click me'),
),

EditText - TextField

<EditText
    android:hint="Your Password"
    android:inputType="textPassword"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
TextField(
  obscureText: true, // 输入的内容变**
  decoration: InputDecoration(
    labelText: 'Your Password',
  ),
  controller: _controller, // 通过控制器控制
  onChanged: (value) {}, // 文本变化
)

这里有必要介绍下 controller,controller 这个概念在 Flutter 的很多 Widget 都会出现,在 TextField 旨在控制输入框的文本、光标等。

ImageView - Image

<ImageView
    android:src="@mipmap/ic_launcher"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
Image(image: AssetImage('images/ic_launcher.png'))

ImageProvider 是什么

上面 Flutter Image 例子中 image 属性实际是一个 ImageProvider 对象,主要是为 Image 提供图片,常用的有

  • AssetImage
  • FileImage
  • NetworkImage

ImageProvider 内部还实现了图片的缓存

控制长宽比例

利用 AspectRatio

AspectRatio(
  aspectRatio: 3 / 2,
  child: Image(
      image: NetworkImage(
          'https://flutter.github.io/assets-for-api-doassets/widgets/owl-2.jpg')),
)

圆形图片 圆角图片

1、CircleAvatar

CircleAvatar(
  backgroundImage: AssetImage('images/ic_launcher.png'),
)

2、Material

Material(
  child: Image.asset('images/ic_launcher.png'),
  borderRadius: BorderRadius.circular(20.0),
  shadowColor: Colors.grey,
  elevation: 5.0,
)

3、BoxDecoration

Container(
  decoration: BoxDecoration(
    borderRadius: BorderRadius.circular(20.0),
  ),
  child: Image.asset('images/ic_launcher.png'),
)

SeekBar - Slider

<SeekBar
    android:progress="50"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>
Slider(
  value: _seekBarValue,
  divisions: 5, // 分块
  label: 'yep', // 气泡文案
  onChanged: (val) {}, // 0-1值监听
)

LinearLayout - Row / Column

<LinearLayout
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <View
        android:background="@android:color/holo_red_dark"
        android:layout_width="0dp"
        android:layout_weight="2"
        android:layout_height="50dp"/>

    <Space
        android:layout_weight="1"
        android:layout_width="0dp"
        android:layout_height="0dp"/>

    <View
        android:background="@android:color/holo_blue_dark"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="50dp"/>
</LinearLayout>
Row(children: [
  Expanded(
    flex: 2, // 占比
    child: Container(
      color: Colors.red,
      height: 50,
    ),
  ),
  Spacer(), // 占空间
  Expanded(
    child: Container(
      color: Colors.blue,
      height: 50,
    ),
  ),
])

在 Flutter 中,线性布局以 Column 和 Row 实现,其中 LinearLayout 的 weight 在 Flutter 以 Expanded 代替,Expanded 继承 Flexible。Spacer 也是个 Expanded,只是 child 是个空 Widget

FrameLayout - Stack

用 Stack 实现堆叠的视图

Stack(
  children: <Widget>[
    Padding(
      padding: const EdgeInsets.all(8.0),
      child: Container(
        width: 100,
        height: 100,
        color: Colors.red,
      ),
    ),
    Container(
      width: 90,
      height: 90,
      color: Colors.green,
    ),
    Container(
      width: 80,
      height: 80,
      color: Colors.blue,
    ),
  ],
)

margin 属性去哪了

Android Layout 中有个 LayoutParams 对象,一般都是 MarginLayoutParams,子 View 可以使用 margin 属性去实现一些间距

而在 Flutter 中,则是通过嵌套 Padding、Container 等 Widget

或是在 Row 中,两个 Widget 直接加一个 SizeBox(width: 10)来实现

RelativeLayout - 组合

在 Flutter 中,是没有 RelativeLayout 以及其各种 below、above 等属性。而是通过组合各种上面所说的 Widget 去实现(Container、Padding、Row、Column、Stack 等等)

Row(
  children: [
    Expanded(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Container(
            padding: const EdgeInsets.only(bottom: 8),
            child: Text(
              'Oeschinen Lake Campground',
              style: TextStyle(
                fontWeight: FontWeight.bold,
              ),
            ),
          ),
          Text(
            'Kandersteg, Switzerland',
            style: TextStyle(
              color: Colors.grey[500],
            ),
          ),
        ],
      ),
    ),
    Icon(
      Icons.star,
      color: Colors.red[500],
    ),
    Text('41'),
  ],
)

— Evil Mouth