Flutter UI组件

基础组件

文本

  • 最简单的用法就是只提供文本内容就可以了。比如Text("Hello world");

  • TextStyle用于指定文本显示的样式如颜色、字体、粗细、背景等。

Text("Hello world",
  style: TextStyle(
    color: Colors.blue,
    fontSize: 18.0,
    height: 1.2,  
    fontFamily: "Courier",
    background: new Paint()..color=Colors.yellow,
    decoration:TextDecoration.underline,
    decorationStyle: TextDecorationStyle.dashed
  ),
);
image.png
  • 富文本
Text.rich(TextSpan(
    children: [
     TextSpan(
       text: "Home: "
     ),
     TextSpan(
       text: "https://flutterchina.club",
       style: TextStyle(
         color: Colors.blue
       ),  
       recognizer: _tapRecognizer // 点击响应
     ),
    ]
));
Flutter UI组件_第1张图片
image.png

按钮

  • RaisedButton 即"漂浮"按钮,它默认带有阴影和灰色背景。也就是常用的普通按钮
RaisedButton(
  child: Text("normal"),
  onPressed: () {},
);
Flutter UI组件_第2张图片
image.png
  • FlatButton即扁平按钮,默认背景透明并不带阴影。按下后,会有背景色
FlatButton(
  child: Text("normal"),
  onPressed: () {},
)
Flutter UI组件_第3张图片
image.png
  • OutlineButton默认有一个边框,不带阴影且背景透明。按下后,边框颜色会变亮、同时出现背景和阴影(较弱),
OutlineButton(
  child: Text("normal"),
  onPressed: () {},
)
Flutter UI组件_第4张图片
image.png
  • IconButton是一个可点击的Icon,不包括文字,默认没有背景,点击后会出现背景
IconButton(
  icon: Icon(Icons.thumb_up),
  onPressed: () {},
)
Flutter UI组件_第5张图片
image.png
  • RaisedButton、FlatButton、OutlineButton都有一个icon 构造函数,通过它可以轻松创建带图标的按钮
RaisedButton.icon(
  icon: Icon(Icons.send),
  label: Text("发送"),
  onPressed: _onPressed,
),
OutlineButton.icon(
  icon: Icon(Icons.add),
  label: Text("添加"),
  onPressed: _onPressed,
),
FlatButton.icon(
  icon: Icon(Icons.info),
  label: Text("详情"),
  onPressed: _onPressed,
),
Flutter UI组件_第6张图片
image.png

图片和ICON

  • 本地图片
  1. 在工程根目录下创建一个images目录,并将图片avatar.png拷贝到该目录。2.0x 3.0x分别是2倍图和3倍图的目录,图片名字保持一致。
Flutter UI组件_第7张图片
image.png
  1. pubspec.yaml中的flutter部分添加资源信息
Flutter UI组件_第8张图片
image.png
  1. 在代码中添加图片
Image.asset("images/avatar.png",
  width: 100.0,
)
  • 从网络加载图片
Image.network(
  "https://avatars2.githubusercontent.com/u/20411648?s=460&v=4",
  width: 100.0,
)
  • fit:该属性用于在图片的显示空间和图片本身大小不同时指定图片的适应模式
Flutter UI组件_第9张图片
image.png
  • Flutter封装了IconDataIcon来专门显示字体图标
Row(
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    Icon(Icons.accessible,color: Colors.green,),
    Icon(Icons.error,color: Colors.green,),
    Icon(Icons.fingerprint,color: Colors.green,),
  ],
)
Flutter UI组件_第10张图片
image.png

单选开关和复选框

Material 组件库中提供了Material风格的单选开关Switch和复选框Checkbox,它们都是继承自StatelessWidget,所以它们本身不会保存当前选择状态,因此它们的选中状态都是由父组件来管理的。当SwitchCheckbox被点击时,会触发它们的onChanged回调,我们可以在此回调中处理选中状态改变逻辑。

import 'package:flutter/material.dart';

class SelectDemo extends StatefulWidget {
  @override
  State createState() {

    return _SelectDemoState();
  }
}

class _SelectDemoState extends State {

  bool _switchSelected = true;  //维护单选开关状态
  bool _checkboxSelected = true; //维护复选框状态

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: Text('单选和多选例子'),
      ),
      body: Column(
        children: [
          Switch(
            value: _switchSelected,//当前状态
            onChanged:(value){
              //重新构建页面
              setState(() {
                _switchSelected = value;
              });
            },
          ),
          Checkbox(
            value: _checkboxSelected,
            activeColor: Colors.red, //选中时的颜色
            onChanged:(value){
              setState(() {
                _checkboxSelected = value;
              });
            } ,
          )
        ],
      ),
    );
  }
}
Flutter UI组件_第11张图片
image.png

输入框及表单

  • TextField用于文本输入,

  • 'controller':编辑框的控制器,通过它可以设置/获取编辑框的内容、选择编辑内容、监听编辑文本改变事件

  • InputDecoration:用于控制TextField的外观显示,如提示文本、背景颜色、边框等

  • keyboardType:用于设置该输入框默认的键盘输入类型

  • Flutter提供了一个Form 组件,它可以对输入框进行分组,然后进行一些统一操作,如输入内容校验、输入框重置以及输入内容保存。

  • 为了方便使用,Flutter提供了一个TextFormField组件,它继承自FormField类,也是TextField的一个包装类,所以除了FormField定义的属性之外,它还包括TextField的属性。

import 'package:flutter/material.dart';

class FormDemo extends StatefulWidget {
  @override
  State createState() {
    return _FormDemoState();
  }
}

class _FormDemoState extends State {

  TextEditingController _unameController = TextEditingController();
  TextEditingController _pwdController = TextEditingController();

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    //监听输入改变
    _unameController.addListener((){
      print(_unameController.text);
    });

    //监听密码改变
    _pwdController.addListener((){
      print(_pwdController.text);
    });
  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: Text('输入框表单验证例子'),
      ),
      body: Padding(
        padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24),
        child: Form(
          child: Column(
            children: [
              TextFormField(
                autofocus: true,
                controller: _unameController,
                decoration: InputDecoration(
                  labelText: '用户名',
                  hintText: '用户名或邮箱',
                  icon: Icon(Icons.person),
                ),
                validator: (v) {
                  return v.trim().length > 0 ? null : "用户名不能为空";
                },
              ),
              TextFormField(
                obscureText: true,
                controller: _pwdController,
                decoration: InputDecoration(
                  labelText: '密码',
                  hintText: '您的登录密码',
                  icon: Icon(Icons.lock),
                ),
                validator: (v) {
                  return v.trim().length > 5 ? null : "密码不能少于6位";
                },
              ),
              Padding(
                padding: const EdgeInsets.only(top: 28),
                child: Row(
                  children: [
                    Expanded(
                      child: Builder(
                        builder: (context) {
                          return RaisedButton(
                            padding: const EdgeInsets.all(15),
                            child: Text('登录'),
                            color: Theme.of(context).primaryColor,
                            textColor: Colors.white,
                            onPressed: () {
                              if(Form.of(context).validate()){
                                //验证通过提交数据
                                print('用户名:${_unameController.text}');
                                print('密码:${_pwdController.text}');
                              }
                            },
                          );
                        }
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
Flutter UI组件_第12张图片
image.png

进度指示器

  • LinearProgressIndicator是一个线性、条状的进度条
//  模糊进度条(会执行一个动画),蓝色条一直在移动
LinearProgressIndicator(
  backgroundColor: Colors.grey[200],
  valueColor: AlwaysStoppedAnimation(Colors.blue),
),
// 进度条是静止的,停在50%的位置
LinearProgressIndicator(
  backgroundColor: Colors.grey[200],
  valueColor: AlwaysStoppedAnimation(Colors.blue),
  value: .5, 
)
Flutter UI组件_第13张图片
image.png
  • CircularProgressIndicator是一个圆形进度条
// 模糊进度条(会执行一个旋转动画)
CircularProgressIndicator(
  backgroundColor: Colors.grey[200],
  valueColor: AlwaysStoppedAnimation(Colors.blue),
),
//进度条显示50%,会显示一个半圆
CircularProgressIndicator(
  backgroundColor: Colors.grey[200],
  valueColor: AlwaysStoppedAnimation(Colors.blue),
  value: .5,
),
Flutter UI组件_第14张图片
image.png

容器类组件

填充

  • Padding可以给其子节点添加填充(留白),和边距效果类似。边距用EdgeInsets来表示
    Padding(
      //上下左右各添加16像素补白
      padding: EdgeInsets.all(16.0),
      child: Column(
        //显式指定对齐方式为左对齐,排除对齐干扰
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Padding(
            //左边添加8像素补白
            padding: const EdgeInsets.only(left: 8.0),
            child: Text("Hello world"),
          ),
          Padding(
            //上下各添加8像素补白
            padding: const EdgeInsets.symmetric(vertical: 8.0),
            child: Text("I am Jack"),
          ),
          Padding(
            // 分别指定四个方向的补白
            padding: const EdgeInsets.fromLTRB(20.0,.0,20.0,20.0),
            child: Text("Your friend"),
          )
        ],
      ),
    )
Flutter UI组件_第15张图片
image.png

尺寸限制类容器

  • ConstrainedBox用于对子组件添加额外的约束,比如,最大最小的宽或者高。
Widget redBox=DecoratedBox(
  decoration: BoxDecoration(color: Colors.red),
);

ConstrainedBox(
  constraints: BoxConstraints(
    minWidth: double.infinity, //宽度尽可能大
    minHeight: 50.0 //最小高度为50像素
  ),
  child: Container(
      // 最小高度是50,所以这里的高度5不起作用。
      height: 5.0, 
      child: redBox 
  ),
)
Flutter UI组件_第16张图片
image.png
  • SizedBox用于给子元素指定固定的宽高
SizedBox(
  width: 80.0,
  height: 80.0,
  child: redBox
)
Flutter UI组件_第17张图片
image.png
  • UnconstrainedBox不会对子组件产生任何限制,它允许其子组件按照其本身大小绘制。假如设置的宽高没有起效果,(像上面的高度5),那么很有可能是父组件做了限制。这个时候,就可以通过UnconstrainedBox来消除这种影响。
AppBar(
  title: Text(title),
  actions: [
      // 消除AppBar对子组件的尺寸限制
      UnconstrainedBox(
            child: SizedBox(
              // 如果没有UnconstrainedBox,20 * 20的尺寸设置将无效
              width: 20,
              height: 20,
              child: CircularProgressIndicator(
                strokeWidth: 3,
                valueColor: AlwaysStoppedAnimation(Colors.white70),
              ),
          ),
      )
  ],
)
Flutter UI组件_第18张图片
image.png
Flutter UI组件_第19张图片
image.png

装饰容器

DecoratedBox可以在其子组件绘制前(或后)绘制一些装饰(Decoration),如背景、边框、渐变等。

DecoratedBox(
    decoration: BoxDecoration(
      gradient: LinearGradient(colors:[Colors.red,Colors.orange[700]]), //背景渐变
      borderRadius: BorderRadius.circular(3.0), //3像素圆角
      boxShadow: [ //阴影
        BoxShadow(
            color:Colors.black54,
            offset: Offset(2.0,2.0),
            blurRadius: 4.0
        )
      ]
    ),
  child: Padding(padding: EdgeInsets.symmetric(horizontal: 80.0, vertical: 18.0),
    child: Text("Login", style: TextStyle(color: Colors.white),),
  )
)
Flutter UI组件_第20张图片
image.png

变换

  • Transform可以实现平移,旋转,缩放,
import 'dart:math' as math;

DecoratedBox(
  decoration:BoxDecoration(color: Colors.red),
  child: Transform.rotate(
    //旋转90度
    angle:math.pi/2 ,
    child: Text("Hello world"),
  ),
);
Flutter UI组件_第21张图片
image.png
  • RotatedBox也可以实现旋转,相比Transform.rotate更加彻底。
         Container(
            margin: EdgeInsets.symmetric(vertical: 40),
            child: DecoratedBox(
              decoration: BoxDecoration(
                color: Colors.red,
              ),
              child: RotatedBox(
                // 旋转90度(1/4圈)
                quarterTurns: 1,
                child: Text('Hello world'),
              ),
            ),
          ),
Flutter UI组件_第22张图片
image.png

容器

  • Container是DecoratedBox、ConstrainedBox、Transform、Padding、Align等组件组合的一个多功能容器,可以实现同时需要装饰、变换、限制的场景。
Container(
  margin: EdgeInsets.only(top: 50.0, left: 120.0), //容器外填充
  constraints: BoxConstraints.tightFor(width: 200.0, height: 150.0), //卡片大小
  decoration: BoxDecoration(//背景装饰
      gradient: RadialGradient( //背景径向渐变
          colors: [Colors.red, Colors.orange],
          center: Alignment.topLeft,
          radius: .98
      ),
      boxShadow: [ //卡片阴影
        BoxShadow(
            color: Colors.black54,
            offset: Offset(2.0, 2.0),
            blurRadius: 4.0
        )
      ]
  ),
  transform: Matrix4.rotationZ(.2), //卡片倾斜变换
  alignment: Alignment.center, //卡片内文字居中
  child: Text( //卡片文字
    "5.20", style: TextStyle(color: Colors.white, fontSize: 40.0),
  ),
);
Flutter UI组件_第23张图片
image.png

剪裁

比如圆形的头像之类的。

import 'package:flutter/material.dart';

class ClipTestRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // 头像  
    Widget avatar = Image.asset("imgs/avatar.png", width: 60.0);
    return Center(
      child: Column(
        children: [
          avatar, //不剪裁
          ClipOval(child: avatar), //剪裁为圆形
          ClipRRect( //剪裁为圆角矩形
            borderRadius: BorderRadius.circular(5.0),
            child: avatar,
          ), 
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Align(
                alignment: Alignment.topLeft,
                widthFactor: .5,//宽度设为原来宽度一半,另一半会溢出
                child: avatar,
              ),
              Text("你好世界", style: TextStyle(color: Colors.green),)
            ],
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ClipRect(//将溢出部分剪裁
                child: Align(
                  alignment: Alignment.topLeft,
                  widthFactor: .5,//宽度设为原来宽度一半
                  child: avatar,
                ),
              ),
              Text("你好世界",style: TextStyle(color: Colors.green))
            ],
          ),
        ],
      ),
    );
  }
}
Flutter UI组件_第24张图片
image.png

布局类组件

线性布局

RowColumn来实现线性布局

Column(
  //测试Row对齐方式,排除Column默认居中对齐的干扰
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(" hello world "),
        Text(" I am Jack "),
      ],
    ),
    Row(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(" hello world "),
        Text(" I am Jack "),
      ],
    ),
    Row(
      mainAxisAlignment: MainAxisAlignment.end,
      textDirection: TextDirection.rtl,
      children: [
        Text(" hello world "),
        Text(" I am Jack "),
      ],
    ),
    Row(
      crossAxisAlignment: CrossAxisAlignment.start,  
      verticalDirection: VerticalDirection.up,
      children: [
        Text(" hello world ", style: TextStyle(fontSize: 30.0),),
        Text(" I am Jack "),
      ],
    ),
  ],
);
Flutter UI组件_第25张图片
image.png

弹性布局

弹性布局允许子组件按照一定比例来分配父容器空间。

        //Flex的两个子widget按1:2来占据水平空间  
        Flex(
          direction: Axis.horizontal,
          children: [
            Expanded(
              flex: 1,
              child: Container(
                height: 30.0,
                color: Colors.red,
              ),
            ),
            Expanded(
              flex: 2,
              child: Container(
                height: 30.0,
                color: Colors.green,
              ),
            ),
          ],
        ),
image.png

流式布局

RowColum,如果子widget超出屏幕范围,则会报溢出错误。Wrap可以让溢出部分自动折行。

Wrap(
  spacing: 8.0, // 主轴(水平)方向间距
  runSpacing: 4.0, // 纵轴(垂直)方向间距
  alignment: WrapAlignment.center, //沿主轴方向居中
  children: [
    new Chip(
      avatar: new CircleAvatar(backgroundColor: Colors.blue, child: Text('A')),
      label: new Text('Hamilton'),
    ),
    new Chip(
      avatar: new CircleAvatar(backgroundColor: Colors.blue, child: Text('M')),
      label: new Text('Lafayette'),
    ),
    new Chip(
      avatar: new CircleAvatar(backgroundColor: Colors.blue, child: Text('H')),
      label: new Text('Mulligan'),
    ),
    new Chip(
      avatar: new CircleAvatar(backgroundColor: Colors.blue, child: Text('J')),
      label: new Text('Laurens'),
    ),
  ],
)
Flutter UI组件_第26张图片
image.png

层叠布局

层叠布局和Web中的绝对定位、Android中Frame布局是相似的,子组件可以根据距父容器四个角的位置来确定自身的位置。绝对定位允许子组件堆叠起来(按照代码中声明的顺序)。Flutter中使用StackPositioned这两个组件来配合实现绝对定位。Stack允许子组件堆叠,而Positioned用于根据Stack的四个角来确定子组件的位置。

//通过ConstrainedBox来确保Stack占满屏幕
ConstrainedBox(
  constraints: BoxConstraints.expand(),
  child: Stack(
    alignment:Alignment.center , //指定未定位或部分定位widget的对齐方式
    children: [
      Container(child: Text("Hello world",style: TextStyle(color: Colors.white)),
        color: Colors.red,
      ),
      Positioned(
        left: 18.0,
        child: Text("I am Jack"),
      ),
      Positioned(
        top: 18.0,
        child: Text("Your friend"),
      )        
    ],
  ),
);
Flutter UI组件_第27张图片
image.png

对齐与相对定位

  • Align组件可以调整子组件的位置,并且可以根据子组件的宽高来确定自身的的宽高
Container(
  height: 120.0,
  width: 120.0,
  color: Colors.blue[50],
  child: Align(
    alignment: Alignment.topRight,
    child: FlutterLogo(
      size: 60,
    ),
  ),
)
Flutter UI组件_第28张图片
image.png
  • Center继承自Align,它比Align只少了一个alignment 参数;
DecoratedBox(
  decoration: BoxDecoration(color: Colors.red),
  child: Center(
    child: Text("xxx"),
  ),
),
DecoratedBox(
  decoration: BoxDecoration(color: Colors.red),
  child: Center(
    widthFactor: 1,
    heightFactor: 1,
    child: Text("xxx"),
  ),
)
Flutter UI组件_第29张图片
image.png

可滚动组件

SingleChildScrollView

SingleChildScrollView类似于Android中的ScrollView,它只能接收一个子组件。

Scrollbar( // 显示进度条
      child: SingleChildScrollView(
        padding: EdgeInsets.all(16.0),
        child: Center(
          child: Column( 
            //动态创建一个List  
            children: str.split("") 
                //每一个字母都用一个Text显示,字体为原来的两倍
                .map((c) => Text(c, textScaleFactor: 2.0,)) 
                .toList(),
          ),
        ),
      ),
    )
Flutter UI组件_第30张图片
image.png

ListView

  • 默认构造函数有一个children参数,它接受一个Widget列表(List)。这种方式适合只有少量的子组件的情况。
ListView(
  shrinkWrap: true, 
  padding: const EdgeInsets.all(20.0),
  children: [
    const Text('I\'m dedicating every day to you'),
    const Text('Domestic life was never quite my style'),
    const Text('When you smile, you knock me out, I fall apart'),
    const Text('And I thought I was so smart'),
  ],
)
Flutter UI组件_第31张图片
image.png
  • ListView.builder适合列表项比较多(或者无限)的情况
ListView.builder(
    itemCount: 100,
    itemExtent: 50.0, //强制高度为50.0
    itemBuilder: (BuildContext context, int index) {
      return ListTile(title: Text("$index"));
    }
)
Flutter UI组件_第32张图片
image.png

GridView

GridView(
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
      crossAxisCount: 3, //横轴三个子widget
      childAspectRatio: 1.0 //宽高比为1时,子widget
  ),
  children:[
    Icon(Icons.ac_unit),
    Icon(Icons.airport_shuttle),
    Icon(Icons.all_inclusive),
    Icon(Icons.beach_access),
    Icon(Icons.cake),
    Icon(Icons.free_breakfast)
  ]
)
Flutter UI组件_第33张图片
image.png

功能型组件

异步UI更新

FutureBuilder会依赖一个Future,它会根据所依赖的Future的状态来动态构建自身。

Future mockNetworkData() async {
  return Future.delayed(Duration(seconds: 2), () => "我是从互联网上获取的数据");
}

Widget build(BuildContext context) {
  return Center(
    child: FutureBuilder(
      future: mockNetworkData(),
      builder: (BuildContext context, AsyncSnapshot snapshot) {
        // 请求已结束
        if (snapshot.connectionState == ConnectionState.done) {
          if (snapshot.hasError) {
            // 请求失败,显示错误
            return Text("Error: ${snapshot.error}");
          } else {
            // 请求成功,显示数据
            return Text("Contents: ${snapshot.data}");
          }
        } else {
          // 请求未结束,显示loading
          return CircularProgressIndicator();
        }
      },
    ),
  );
}
Flutter UI组件_第34张图片
image.png

对话框

//点击该按钮后弹出对话框
RaisedButton(
  child: Text("对话框1"),
  onPressed: () async {
    //弹出对话框并等待其关闭
    bool delete = await showDeleteConfirmDialog1();
    if (delete == null) {
      print("取消删除");
    } else {
      print("已确认删除");
      //... 删除文件
    }
  },
),

// 弹出对话框
Future showDeleteConfirmDialog1() {
  return showDialog(
    context: context,
    builder: (context) {
      return AlertDialog(
        title: Text("提示"),
        content: Text("您确定要删除当前文件吗?"),
        actions: [
          FlatButton(
            child: Text("取消"),
            onPressed: () => Navigator.of(context).pop(), // 关闭对话框
          ),
          FlatButton(
            child: Text("删除"),
            onPressed: () {
              //关闭对话框并返回true
              Navigator.of(context).pop(true);
            },
          ),
        ],
      );
    },
  );
}
Flutter UI组件_第35张图片
image.png

手势识别

class _ScaleTestRouteState extends State<_ScaleTestRoute> {
  double _width = 200.0; //通过修改图片宽度来达到缩放效果

  @override
  Widget build(BuildContext context) {
   return Center(
     child: GestureDetector(
        //指定宽度,高度自适应
        child: Image.asset("./images/sea.png", width: _width),
        onScaleUpdate: (ScaleUpdateDetails details) {
          setState(() {
            //缩放倍数在0.8到10倍之间
            _width=200*details.scale.clamp(.8, 10.0);
          });
        },
      ),
   );
  }
}
Flutter UI组件_第36张图片
image.png

指针事件

...
//定义一个状态,保存当前指针位置
PointerEvent _event;
...
Listener(
  child: Container(
    alignment: Alignment.center,
    color: Colors.blue,
    width: 300.0,
    height: 150.0,
    child: Text(_event?.toString()??"",style: TextStyle(color: Colors.white)),
  ),
  onPointerDown: (PointerDownEvent event) => setState(()=>_event=event),
  onPointerMove: (PointerMoveEvent event) => setState(()=>_event=event),
  onPointerUp: (PointerUpEvent event) => setState(()=>_event=event),
)
Flutter UI组件_第37张图片
image.png

实例

Demo例子

你可能感兴趣的:(Flutter UI组件)