07-容器类Widget
容器类Widget
容器类Widget一般只是包装其子Widget,对其添加一些修饰(补白或背景色等)、变换(旋转或剪裁等)、或限制(大小等)
Padding
Padding可以给其子节点添加补白(填充),来看看它的定义:
Padding({
...
EdgeInsetsGeometry padding,
Widget child,
})
EdgeInsetsGeometry是一个抽象类,开发中,我们一般都使用EdgeInsets,它是EdgeInsetsGeometry的一个子类,定义了一些设置补白的便捷方法。
我们看看EdgeInsets提供的便捷方法:
- fromLTRB(double left, double top, double right, double bottom):分别指定四个方向的补白。
- all(double value) : 所有方向均使用相同数值的补白。
- only({left, top, right ,bottom }):可以设置具体某个方向的补白(可以同时指定多个方向)。
- symmetric({ vertical, horizontal }):用于设置对称方向的补白,vertical指top和bottom,horizontal指left和right。
ConstrainedBox和SizedBox
ConstrainedBox用于对齐子widget添加额外的约束。
例如 实现一个最小高度为50,宽度尽可能大的红色容器。
ConstrainedBox(
constraints: BoxConstraints(
minWidth: double.infinity, //宽度尽可能大
minHeight: 50.0 //最小高度为50像素
),
child: Container(
height: 5.0,
child: redBox
),
)
SizedBox用于给子widget指定固定的宽高,如:
SizedBox(
width: 80.0,
height: 80.0,
child: redBox
)
DecoratedBox
DecoratedBox可以在其子widget绘制前(或后)绘制一个装饰Decoration(如背景、边框、渐变等)。DecoratedBox定义如下:
const DecoratedBox({
Decoration decoration,
DecorationPosition position = DecorationPosition.background,
Widget child
})
- decoration:代表将要绘制的装饰,它类型为Decoration,Decoration是一个抽象类,它定义了一个接口 createBoxPainter(),子类的主要职责是需要通过实现它来创建一个画笔,该画笔用于绘制装饰。
- position:此属性决定在哪里绘制Decoration,它接收 DecorationPosition的枚举类型,该枚举类两个值:background:在子widget之后绘制,即背景装饰。foreground:在子widget之上绘制,即前景。
decoration通常会直接使用BoxDecoration,它是一个Decoration的子类,实现了常用的装饰元素的绘制。
BoxDecoration({
Color color, //颜色
DecorationImage image,//图片
BoxBorder border, //边框
BorderRadiusGeometry borderRadius, //圆角
List<BoxShadow> boxShadow, //阴影,可以指定多个
Gradient gradient, //渐变
BlendMode backgroundBlendMode, //背景混合模式
BoxShape shape = BoxShape.rectangle, //形状
})
Transform变换
Transform可以在其子Widget绘制时对其应用一个矩阵变换(transformation),Matrix4是一个4D矩阵,通过它我们可以实现各种矩阵操作。下面是一个例子:
Container(
color: Colors.black,
child: new Transform(
alignment: Alignment.topRight, //相对于坐标系原点的对齐方式
transform: new Matrix4.skewY(0.3), //沿Y轴倾斜0.3弧度
child: new Container(
padding: const EdgeInsets.all(8.0),
color: Colors.deepOrange,
child: const Text('Apartment for rent!'),
),
),
);
Container
Container是DecoratedBox、ConstrainedBox、Transform、Padding、Align等widget的一个组合widget。所以我们只需通过一个Container可以实现同时需要装饰、变换、限制的场景。下面是Container的定义:
Container({
this.alignment,
this.padding, //容器内补白,属于decoration的装饰范围
Color color, // 背景色
Decoration decoration, // 背景装饰
Decoration foregroundDecoration, //前景装饰
double width,//容器的宽度
double height, //容器的高度
BoxConstraints constraints, //容器大小的限制条件
this.margin,//容器外补白,不属于decoration的装饰范围
this.transform, //变换
this.child,
})
示例:
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),
),
)
可以看到Container通过组合多种widget来实现复杂强大的功能,在Flutter中,这也正是组合优先于继承的实例。
Scaffold、TabBar、底部导航
Material库提供了很多Widget,本节介绍一些常用的Widget,其余的读者可以查看文档。
大多数路由页都会包含一个导航栏,有些路由页可能会有抽屉菜单(Drawer)以及底部Tab导航菜单等。Flutter Material库提供了一个Scaffold Widget,它是一个路由页的骨架,可以非常容易的拼装出一个完整的页面。
我们实现一个页面,它包含:
一个导航栏
导航栏右边有一个分享按钮
有一个抽屉菜单
有一个底部导航
右下角有一个悬浮的动作按钮
实现代码如下:
class ScaffoldRoute extends StatefulWidget {
@override
_ScaffoldRouteState createState() => _ScaffoldRouteState();
}
class _ScaffoldRouteState extends State<ScaffoldRoute> {
int _selectedIndex = 1;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar( //导航栏
title: Text("App Name"),
actions: <Widget>[ //导航栏右侧菜单
IconButton(icon: Icon(Icons.share), onPressed: () {}),
],
),
drawer: new MyDrawer(), //抽屉
bottomNavigationBar: BottomNavigationBar( // 底部导航
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('Home')),
BottomNavigationBarItem(icon: Icon(Icons.business), title: Text('Business')),
BottomNavigationBarItem(icon: Icon(Icons.school), title: Text('School')),
],
currentIndex: _selectedIndex,
fixedColor: Colors.blue,
onTap: _onItemTapped,
),
floatingActionButton: FloatingActionButton( //悬浮按钮
child: Icon(Icons.add),
onPressed:_onAdd
),
);
}
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
void _onAdd(){
}
}
上面代码中我们用到了另外几个Widget:
AppBar
AppBar是一个Material风格的导航栏,它可以设置标题、导航栏菜单、底部Tab等。下面我们看看AppBar的定义:
AppBar({
Key key,
this.leading, //导航栏最左侧Widget,常见为抽屉菜单按钮或返回按钮。
this.automaticallyImplyLeading = true, //如果leading为null,是否自动实现默认的leading按钮
this.title,// 页面标题
this.actions, // 导航栏右侧菜单
this.bottom, // 导航栏底部菜单,通常为Tab按钮组
this.elevation = 4.0, // 导航栏阴影
this.centerTitle, //标题是否居中
this.backgroundColor,
... //其它属性见源码注释
})
TabBar
通过“bottom”属性来添加一个导航栏底部tab按钮组,Material组件库中提供了一个TabBar组件,它可以快速生成Tab菜单,示例:
class _ScaffoldRouteState extends State<ScaffoldRoute>
with SingleTickerProviderStateMixin {
TabController _tabController; //需要定义一个Controller
List tabs = ["新闻", "历史", "图片"];
@override
void initState() {
super.initState();
// 创建Controller
_tabController = TabController(length: tabs.length, vsync: this);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
... //省略无关代码
bottom: TabBar( //生成Tab菜单
controller: _tabController,
tabs: tabs.map((e) => Tab(text: e)).toList()),
),
... //省略无关代码
}
上面代码首先创建了一个TabController ,它是用于控制/监听Tab菜单切换。然后通过TabBar生成了一个底部菜单栏,TabBar的tabs属性接受一个Widget数组,表示每一个Tab子菜单,我们可以自定义,也可以像示例中一样直接使用Tab Widget,它也是Material组件库提供的Material风格的Tab菜单。
Tab Widget有三个可选参数,除了可以指定文字外,还可以指定Tab菜单图标,或者直接自定义Widget,定义如下:
Tab({
Key key,
this.text, // 菜单文本
this.icon, // 菜单图标
this.child, // 自定义Widget
})
TabBarView
通过TabBar我们只能生成一个静态的菜单,如果要实现Tab页,Material库提供了一个TabBarView组件,它可以很轻松的配合TabBar来实现同步切换和滑动状态同步,示例如下:
Scaffold(
appBar: AppBar(
... //省略无关代码
bottom: TabBar(
controller: _tabController,
tabs: tabs.map((e) => Tab(text: e)).toList()),
),
drawer: new MyDrawer(),
body: TabBarView(
controller: _tabController,
children: tabs.map((e) { //创建3个Tab页
return Container(
alignment: Alignment.center,
child: Text(e, textScaleFactor: 5),
);
}).toList(),
),
... // 省略无关代码
)
TabBar和TabBarView的controller是同一个!正是如此,TabBar和TabBarView正是通过同一个controller来实现菜单切换和滑动状态同步的。
Drawer
Scaffold的drawer和endDrawer属性可以分别接受一个Widget作为页面的左、右抽屉菜单,当用户手指重屏幕左/右向里滑动时便可打开抽屉菜单。示例如下:
class MyDrawer extends StatelessWidget {
const MyDrawer({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Drawer(
child: MediaQuery.removePadding(
context: context,
// DrawerHeader consumes top MediaQuery padding.
removeTop: true,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 38.0),
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: ClipOval(
child: Image.asset(
"imgs/avatar.png",
width: 80,
),
),
),
Text(
"Wendux",
style: TextStyle(fontWeight: FontWeight.bold),
)
],
),
),
Expanded(
child: ListView(
children: <Widget>[
ListTile(
leading: const Icon(Icons.add),
title: const Text('Add account'),
),
ListTile(
leading: const Icon(Icons.settings),
title: const Text('Manage accounts'),
),
],
),
),
],
),
),
);
}
}
FloatingActionButton
FloatingActionButton是Material设计规范中的一种特殊Button,通常悬浮在页面的某一个位置作为某种常用动作的快捷入口
底部Tab导航栏
可以通过Scaffold的bottomNavigationBar属性来设置底部导航,Material组件库提供的BottomNavigationBar和BottomNavigationBarItem两个Widget来实现Material风格的底部导航栏。
Material组件库中提供了一个BottomAppBar Widget,可以和FloatingActionButton配合实现"打洞"效果。源码如下:
bottomNavigationBar: BottomAppBar(
color: Colors.white,
shape: CircularNotchedRectangle(), // 底部导航栏打一个圆形的洞
child: Row(
children: [
IconButton(icon: Icon(Icons.home)),
SizedBox(), //中间位置空出
IconButton(icon: Icon(Icons.business)),
],
mainAxisAlignment: MainAxisAlignment.spaceAround, //均分底部导航栏横向空间
),
)
可以看到,上面代码中没有控制打洞位置的属性,实际上,打洞的位置取决于FloatingActionButton的位置,上面FloatingActionButton的位置为:
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
BottomAppBar的shape属性决定洞的外形,CircularNotchedRectangle实现了一个圆形的外形。
07-容器类Widget的更多相关文章
- 【Flutter】容器类组件简介
前言 容器类Widget和布局类Widget都作用于其子Widget,不同的是: 布局类Widget一般都需要接收一个widget数组(children),他们直接或间接继承自(或包含)MultiCh ...
- python单元测试框架 pyunit
概况 系统要求 使用PyUnit构建自己的测试 安装 测试用例介绍 创建一个简单测试用例 复用设置代码:创建固件 包含多个测试方法的测试用例类 将测试用例聚合成测试套件 嵌套测试用例 测试代码的放置位 ...
- Qt中设置widget背景颜色/图片的注意事项(使用样式表 setStyleSheet())
在Qt中设置widget背景颜色或者图片方法很多种:重写paintEvent() , 调色板QPalette , 样式表setStyleSheet等等. 但是各种方法都有其注意事项,如果不注意则很容易 ...
- Qt容器类(总结)(新发现的QQueue和QStack,注意全都是泛型)
Introduction Qt库提供了一组基于模板的一般化的容器类.这些容器可以存储指定的类型的元素.例如,如果你需要一个可变大小的Qstring数组,可以用QVector<QString> ...
- QT 信号槽connect中解决自定义数据类型或数组作为函数参数的问题——QT qRegisterMetaType 注册MetaType——关键:注册自定义数据类型或QMap等容器类
一般情况下信号槽直接连接方式不会出现问题,但是如果信号与槽在不同线程或Qt::QueuedConnection方式连接,可能会在连接期间报以下类似问题,如: QObject::connect: Can ...
- Qt——容器类(译)
注:本文是我对Qt官方文档的翻译,错误之处还请指正. 原文链接:Container Classes 介绍 Qt库提供了一套通用的基于模板的容器类,可以用这些类存储指定类型的项.比如,你需要一个大小可变 ...
- cocos代码研究(26)Widget子类RichView学习笔记
理论部分 一个显示多个RichElement的容器类. 我们可以使用它很容易显示带图片的文本,继承自 Widget. 代码实践 static RichText * create ()创建一个空的Ric ...
- java.lang.ClassCastException: android.widget.ImageButton异常处理
在调程序时总是出现异常关闭的现象,log显示: 03-26 07:58:09.528: E/AndroidRuntime(398): Caused by: java.lang.ClassCastExc ...
- m2014_c->c语言容器类工具列
转自:http://www.cnblogs.com/sniperHW/category/374086.html cocos2dx内存管理 摘要: cocos2dx基于引用计数管理内存,所有继承自CCO ...
随机推荐
- Linux 系统安装[Redhat]
系统下载 Linux操作系统各版本ISO镜像下载 系统安装 1.1. 分区知识 1.2. 磁盘分区命名以及编号 IDE盘: hda 第一块盘 hda1/第一块盘的第一个分区 hdb 第二块盘 h ...
- 乘风破浪:LeetCode真题_008_String to Integer (atoi)
乘风破浪:LeetCode真题_008_String to Integer (atoi) 一.前言 将整型转换成字符串,或者将字符串转换成整型,是经常出现的,也是必要的,因此我们需要熟练的掌握,当然也 ...
- 乘风破浪:LeetCode真题_003_Longest Substring Without Repeating Characters
乘风破浪:LeetCode真题_003_Longest Substring Without Repeating Characters 一.前言 在算法之中出现最多的就是字符串方面的问题了,关于字符串的 ...
- python源码学习(一)——python的总体架构
python源码学习(一)——python的总体架构 学习环境: 系统:ubuntu 12.04 STLpython版本:2.7既然要学习python的源码,首先我们要在电脑上安装python并且下载 ...
- linux的pthread_self与gettid的返回值和开销的区别
linux的pthread_self与gettid的返回值和开销的区别 linux的pthread_self与gettid的返回值和开销的区别 分类: 一些思考 2012-05-18 12:25 17 ...
- 51nod 1102 面积最大的矩形
题目地址在这儿 求取:以某矩形g[i]为最小值的区间的左右端点,得到一个临时解.所有临时解中的最大值即为解. 求取区间的方法可以用单调栈,也可以用下面这种十分简洁的类似于递归的方法.下面这种解法求出来 ...
- codeforces 666C Codeword
codeforces 666C Codeword 题意 q个询问,一种询问是给你一个字符串,还有一种是问长度为n的,包含当前字符串为子序列的字符串有多少个. 题解 容易写出式子,但是不好化简. 观察一 ...
- 内网渗透中的mimikatz
0x00 前言 上篇测试了中间人攻击利用框架bettercap,这次挑选一款更具代表性的工具--mimikatz 0x01 简介 mimikatz,很多人称之为密码抓取神器,但在内网渗透中,远不止这么 ...
- Gym 100633G Nano alarm-clocks
题目,给定n个时钟,要求把他们调成一样的时间.求最小的步数 思路:肯定是有一个时钟作为标准的啦,要找到这个时钟,怎么找呢?没其他方便的方法,暴力枚举.那么枚举后,怎么能快速地算到其他时钟转到这个时钟的 ...
- linux mysql access denied for user ‘root’@’localhost'(using password:YES)
linux安装完mysql后,使用程序连接报以上错误解决方法,重新设置密码,步骤如下 1.先停掉原来的服务 service mysqld stop 2.使用安全模式登陆,跳过密码验证 mysqld_s ...