Widget
Flutter中一切皆为Widget,Widget是Flutter应用界面的基本构成单元,每个widget都与最终的用户界面展示紧密相关. 一个widget可以定义如下:
- 一个结构元素(一个按钮,一个菜单)
- 一个风格元素(字体,配色方案等)
- 布局(padding,margin)
- 等等
什么是widget
abstract class Widget extends DiagnosticableTree {
const Widget({ this.key });
final Key key;
@protected
@factory
Element createElement();
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
}
复制代码
- widget 是个抽象类
- 其中 canUpdate() 决定是否重新绘制
widget分类
StatelessWidget 关键代码
内部没有保存状态,UI创建后就不会改变
abstract class StatelessWidget extends Widget {
const StatelessWidget({ Key key }) : super(key: key);
@override
StatelessElement createElement() => StatelessElement(this);
@protected
Widget build(BuildContext context);
}
复制代码
StateFullWidget
内部保存了状态,调用setState方法,变成UI
abstract class StatefulWidget extends Widget {
const StatefulWidget({ Key key }) : super(key: key);
@override
StatefulElement createElement() => StatefulElement(this);
@protected
@factory
State createState();
}
复制代码
State
关键代码
abstract class State<T extends StatefulWidget> with Diagnosticable {
T get widget => _widget;
T _widget;
BuildContext get context => _element;
StatefulElement _element;
bool get mounted => _element != null;
@protected
@mustCallSuper
void initState() {
}
@mustCallSuper
@protected
void didUpdateWidget(covariant T oldWidget) { }
@protected
@mustCallSuper
void reassemble() { }
@protected
void setState(VoidCallback fn) {
final dynamic result = fn() as dynamic;
_element.markNeedsBuild();
}
@protected
@mustCallSuper
void deactivate() { }
@protected
@mustCallSuper
void dispose() {
}
@protected
Widget build(BuildContext context);
@protected
@mustCallSuper
void didChangeDependencies() { }
}
复制代码
State生命周期
- initState():被插入到widget树中时被调用一次
- didChangeDependencies():当state对象的依赖发生变化时调用
- build():构建widget时调用
- didUpdateWidget():widget重新构建时调用
- deactivate(): 当state对象从树中被移除时调用
- dispose(): 当state对象从树中被永久移除时调用,一般在此方法中释放资源
Widget&Element&RenderObject
- widget是为element描述需要的配置,负责创建element,决定element是否需要更新
- element表示widget配置树的特定位置的一个实例,同时持有widget和renderobject,负责管理widget配置和renderobject渲染
- renderobject表示渲染树的一个对象,负责真正的渲染工作,比如测量,位置,绘制等
Flutter常用基础组件
常用StatelessWidget
Text
- 文本显示控件
_buildText() {
TextStyle style = TextStyle(
color: Colors.green,//文字颜色
fontSize: 20,//文字大小
fontStyle: FontStyle.italic,//设置斜体
fontWeight: FontWeight.bold);//设置加粗
return Text("我是一段文字", style: style);
}
复制代码
Container
- 容器布局,常用作给子widget设置边距,背景,圆角等效果
_buildContainer() {
return Container(
alignment: Alignment.center,//居中对齐
padding: EdgeInsets.all(10),//设置内边距
margin: EdgeInsets.only(left: 20, right: 20),//设置外边距
decoration: BoxDecoration(//设置装饰器
color: Colors.greenAccent,//设置颜色
borderRadius: BorderRadius.all(Radius.circular(5)),//设置圆角
gradient: LinearGradient(colors: [Colors.deepPurple, Colors.pinkAccent])),//设置渐变色背景
child: _buildText(),
);
}
复制代码
Icon 图标
- Icon可以是矢量图,所以可以设置大小和颜色
_buildIcon() {
return Icon(Icons.android,//图标源
size: 50, //大小
color: Colors.greenAccent//图标颜色
);
}
复制代码
CloseButton&BackButton
CloseButton(),
BackButton()
复制代码
Chip
- Material的设计小部件
_buildChip() {
return Chip(
backgroundColor: Colors.greenAccent,//背景颜色
avatar: Icon(Icons.phonelink_off_rounded,color: Colors.pink,),//图标,通过这个设置图标大小
label: Text('我是Chip的文字'),//文字
labelPadding: EdgeInsets.only(left: 20),//文字的边距
labelStyle: TextStyle(color: Colors.white, fontSize: 10),//文字样式
padding: EdgeInsets.only(left: 10, right: 10, top: 5, bottom: 5),//Chip内边距
elevation: 5,
);
}
复制代码
Divider
- 分割线widget
_buildDivider() {
return Divider(
height: 50,//分割线占据的高度
thickness: 5,//分割线高度
indent: 20,//左边距
endIndent: 20,//右边距
color: Colors.blue,//颜色
);
}
复制代码
Card
- 卡片widget,布局中经常用到
_buildCart() {
return Card(
margin: EdgeInsets.all(10),//card外边距
color: Colors.greenAccent,//背景色
shadowColor: Colors.pinkAccent,//
elevation: 5,//阴影
shape: RoundedRectangleBorder(//圆角形状,默认也为RoundedRectangleBorder
borderRadius: BorderRadius.all(Radius.circular(6.0))//设置圆角大小
),
child: Container(
padding: EdgeInsets.only(left: 10, top: 5, right: 10, bottom: 5),
child: _buildText(),
));
}
复制代码
AlterDialog
- 弹窗
_buildRaisedButton(BuildContext context) {
return RaisedButton(
onPressed: () {
showDialog(//显示弹框的方法
context: context,
builder: (context) {
return _buildAlterDialog(context);
});
},
child: Text('显示弹框'));
}
AlertDialog _buildAlterDialog(BuildContext context) {
return AlertDialog(
title: Text('我是标题了'),
content: Text('我是内容啊'),
actions: [
//配置下面的按钮
FlatButton(
onPressed: () {
Navigator.of(context).pop(); //关闭弹框
},
child: Text("确认"))
],
);
}
复制代码
FloatingActionButton
- 也是Material下的一个widget
_buildFloatingActionButton() {
return FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
);
}
复制代码
Column&Row
- 垂直容器布局,和Android中Linerlayout差不多
Colum 有主轴和交叉轴的概念,Column是垂直布局,它的主轴就是垂直方向,交叉轴是水平方向;Row 是水平方向布局,它的主轴是水平方向,它的交叉轴 就是垂直方向
_buildColumn() {
return Column(
mainAxisSize: MainAxisSize.max, //主轴方向的大小
//主轴位置,Column是是垂直布局,所以垂直方向是主轴
mainAxisAlignment: MainAxisAlignment.start, //垂直方向从顶部开始
//交叉轴位置,和主轴十字交叉的轴,也是水平方向()
crossAxisAlignment: CrossAxisAlignment.end,//水平方向
children: [
//填充其中的子widget
_buildText(),
_buildText(),
],
);
}
复制代码
常用StatefulWidget
Image
- 图片控件,可以显示网络图片和项目中的资源图片
Image.network(
'https://sf3-ttcdn-tos.pstatp.com/img/user-avatar/96119b38be4be1dafc95fcbff0d9ee30~300x300.image',
fit: BoxFit.cover,//填充方式,跟Android里中scaleType的几种方式差不多
width: 60,
height: 60,
)
复制代码
TextField
- 文本输入组件
TextField(
maxLength: 11,//最大长度
keyboardType: TextInputType.number,//输入键盘类型
// obscureText: true,//是否明文显示
inputFormatters: [
FilteringTextInputFormatter.digitsOnly//输入类型
],
textInputAction: TextInputAction.search,//键盘右下角文字
controller: TextEditingController(text: '86'),//默认文本
decoration: InputDecoration(
contentPadding: EdgeInsets.only(left: 10, right: 10),
hintText: '请您输入电话号码',
hintStyle: TextStyle(fontSize: 20, color: Colors.black12)),
)
复制代码
AppBar
- 标题栏
_buildAppBar() {
return AppBar(
title: Text('我是标题'), //设置标题
actions: [CloseButton(), BackButton()],//设置一些后面的按钮
centerTitle: true, //标题居中
toolbarHeight: kToolbarHeight,//标题栏高度
textTheme: TextTheme(), //设置样式
);
}
复制代码
RefreshIndicator
- 刷新组件,child为ListView或CustomScrollView
_buildList() {
return RefreshIndicator(
child: ListView(
children: [
Text('列表内容1'),
Text('列表内容1'),
Text('列表内容1'),
Text('列表内容1'),
Text('列表内容1'),
],
),
onRefresh: handRefresh);
}
Future<Null> handRefresh() async {
await Future.delayed(Duration(microseconds: 1000));
return null;
}
复制代码
PageView
- 轮播的widget跟Android中ViewPage差不多
_buildPageView() {
return Container(
height: 100,
margin: EdgeInsets.only(top: 10),//设置距离顶部高度
child: PageView(//PageView必须设置高度,所以用一个Container包裹起来
children: [//设置每个item
Container(
alignment: Alignment.center,//item居中显示
margin: EdgeInsets.only(left: 20, right: 20),//设置item左右边距
decoration: BoxDecoration(
color: Colors.greenAccent,
borderRadius: BorderRadius.all(Radius.circular(5))),//设置圆角
child: Text('page1'),
),
Container(
alignment: Alignment.center,
margin: EdgeInsets.only(left: 20, right: 20),
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.all(Radius.circular(5))),
child: Text('page2'),
),
Container(
alignment: Alignment.center,
margin: EdgeInsets.only(left: 20, right: 20),
decoration: BoxDecoration(
color: Colors.lightGreen,
borderRadius: BorderRadius.all(Radius.circular(5))),
child: Text('page3'),
),
],
),
);
}
复制代码
BottomNavigationBar
- 底部导航栏
class HomePage extends State<HomePageState> {
int _currentIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: _buildAppBar(),//包含状态栏
bottomNavigationBar: BottomNavigationBar(//底部导航
currentIndex: _currentIndex,//当前选中位置
onTap: (index) {//点击回调
setState(() {
_currentIndex = index;
});
},
items: [//items,至少为2个
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: '首页',
activeIcon: Icon(
Icons.home,
color: Colors.deepPurple,
)),
BottomNavigationBarItem(
icon: Icon(Icons.list),//默认状态图标
label: '列表',
activeIcon: Icon(//选中状态图标
Icons.list,
color: Colors.deepPurple,
)),
],
),
body: _currentIndex == 0 ? _buildMain() : _buildList(),//内容
floatingActionButton: _buildFloatingActionButton(),//按钮
);
}
}
复制代码
Scaffold
- 容器布局,提供了一些列的widget,能帮助我们快速搭建一个页面,从上面的代码中可以看到,其中有actionbar,底部导航,floatactionbar等供我们一键生成组件
MaterialApp
- 一般作为程序入口,配置App整体的风格,样式,默认属性等等
void main() {
runApp(MyFirstApp());
}
class MyFirstApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '使用materialApp',
theme: ThemeData(
//这里能配置整个App的各种属性,比如主题色,文本输入框获取焦点后颜色等等
primarySwatch: Colors.deepPurple,//设置主题色
visualDensity: VisualDensity.adaptivePlatformDensity,//设置单位适配方式
),
home: HomePage(),
);
}
}
复制代码
Flutter其他widget
ClipOval
- ClipOval将其子widget裁剪成圆形
Opacity
- Opacity 对子widget进行透明度改变
_buildOpacityAndClipOval() {
return Opacity(
opacity: 0.5,//透明度
child: ClipOval(
child: Image.network(
'https://sf3-ttcdn-tos.pstatp.com/img/user-avatar/96119b38be4be1dafc95fcbff0d9ee30~300x300.image',
fit: BoxFit.cover,
width: 60,
height: 60,
),
),
);
}
复制代码
ClipRRect和ClipRect
- ClipRRect:将子widget裁剪成圆角矩形
- ClipRect:将子widget裁剪成矩形
_buildClipRect() {
return ClipRRect(
borderRadius: BorderRadius.circular(5),//设置圆角
clipBehavior: Clip.antiAlias,//抗锯齿
child: Image.network(
'https://sf3-ttcdn-tos.pstatp.com/img/user-avatar/96119b38be4be1dafc95fcbff0d9ee30~300x300.image',
fit: BoxFit.cover,
width: 60,
height: 60,
),
);
}
复制代码
PhysicalModel
- PhysicalModel也是负责裁切子widget的
_buildPhysicalModel() {
return PhysicalModel(
color: Colors.green,
elevation: 5,
borderRadius: BorderRadius.circular(5),
child: Text('呵呵呵呵呵呵'),
);
}
复制代码
Center
- 让子widget居中显示
Sizebox
- 给子widget设置宽高,如果子widget用设置宽高的属性则用不到了
_buildCenter() {
return SizedBox(
width: 180,
height: 180,
child: Center(
child: Image.network(
'https://sf3-ttcdn-tos.pstatp.com/img/user-avatar/96119b38be4be1dafc95fcbff0d9ee30~300x300.image',
fit: BoxFit.cover,
width: 60,
height: 60,
),
),
);
}
复制代码
Padding
- 就是给子widget设置边距的
FractionallySizedBox
- FractionallySizedBox可以让子widget在水平或者垂直方向上充满屏幕
_buildFractionallySizedBox() {
return FractionallySizedBox(
widthFactor: 1,
child: Container(
alignment: Alignment.center,
color: Colors.green,
height: 50,
child: Text('呵呵呵呵呵呵'),
));
}
复制代码
Stack和 Positioned
- Stack和Android中Framlayout一样,子widget是层层覆盖的
- Positioned可以调整子widget在Stack中的位置来显示特殊的效果
_buildStack() {
return Stack(
children: [//设置子widget组
Image.network(
'https://sf3-ttcdn-tos.pstatp.com/img/user-avatar/96119b38be4be1dafc95fcbff0d9ee30~300x300.image',
fit: BoxFit.cover,
width: 80,
height: 80,
),
Positioned(//通过Positioned调整位置
left: 0,//距离左边
bottom: 0,//距离底部
child: Image.network(
'https://sf1-scmcdn2-tos.pstatp.com/xitu_juejin_web/img/wechat.ce329e6.png',
fit: BoxFit.cover,
width: 40,
height: 40,
)),
],
);
}
复制代码
Wrap
- 当有多个子widget的时候,会自动换行
_buildWrap() {
return Wrap(
children: [
Container(
margin: EdgeInsets.all(10),
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
color: Colors.green, borderRadius: BorderRadius.circular(10)),
child: Text('我是一个兵,来自老百姓'),
),
Container(
margin: EdgeInsets.all(10),
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
color: Colors.green, borderRadius: BorderRadius.circular(10)),
child: Text('打倒日本小鬼子,是我的本领'),
),
Container(
margin: EdgeInsets.all(10),
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
color: Colors.green, borderRadius: BorderRadius.circular(10)),
child: Text('保卫人民是我的责任'),
),
],
);
}
复制代码
Expaned
- 让子widget铺满某个水平或垂直方向,跟Android中的widget(权重)差不多
//外层是Column
List<Widget> _buildExpanded() {
var list = <Widget>[];
list.add(Text("你好"));
list.add(Expanded(
child: Container(
decoration: BoxDecoration(color: Colors.green),
child: Text('我要充满高度'),
)));
return list;
}
复制代码
关于Flutter布局的一些思考
其实刚开始接触这些widget,多少有些抵触,跟Android现有的编写界面组件相比,有点儿太零碎了,实现一种效果,可能有很多种方法,布局的实现感觉是根据个人的习惯决定的,而且要想重构一些页面的布局,不得挺费劲么........目前了解的比较少吧 所以会有这么简单的问题,等以后了解的更多了说不定就不抵触了呢...
真的是选择困难症的一记猛药啊,目前先这样吧...