Widget介绍和基础组件
总算到了我们Flutter框架的重点内容了,前面章节说过在Flutter框架里面一切对象都是Widget,用这些就可以构建我们各式各样的界面了,所以Widget也可以说就是我们界面上所有的组件。但是为了能够实现组件的复用功能,Flutter框架真正渲染出来的并不是Widget,而是Element类,Widget只是用来描述一个组件的配置而已,也就是说,定义好的一个Widget可以被添加到UI树的任何一个节点Element上,即一个Widget可对应多个Element,具体的读者可看有关Widget和Element类的实现。
我们常用的组件被分为两大类,即有状态和无状态组件。无状态组件即那些一旦渲染后就不需要更改,也不用产生交互的组件,一般继承StatelessWidget类;有状态组件简单来说就是会发生改变,能产生界面交互的组件,一般继承StatefulWidget类,为了维护StatefulWidget类的状态,每一个类都会对应一个State类,一旦State被改变,我们就可以调用setState()函数来更新组件,State类里的函数大家可以看一下,和我们JAVA里的Activity的几个函数类似。
Flutter框架提供了超级丰富的组件库,如基础组件、Material组件以及Cupertino组件都可以用来构建我们的UI,话不多说,我们开启我们的UI之旅吧。
由于Material组件和Cupertino组件都是在基础组件之上的,所有不用再导入了,这里我们先使用遵循Material Design设计规范的Material组件。在说基础组件时我们要先说一下ListView组件,由于要用他来构建第一个入口页面。
00:00
ListView组件
打开lib/ui下的view.dart文件,这里来实现第一个页面的设计,等到介绍了自定义组件,这个节目就可设计了更美观轻巧,目前采用ListView组件来布局,由于这个布局组件比较简单且常用,效果与代码如图。

import 'package:flutter/material.dart';
import 'index.dart';
class View extends StatefulWidget{
@override
_ViewState createState()=> new _ViewState();
}
class _ViewState extends State<View>{
var isOpen= [true,true,true,true,true,true,true,true];
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: ListView(
children: <Widget>[
ListTile(
leading: const Icon(Icons.account_balance),
title: Text("基础组件"),
subtitle:Text("常用的基础组件"),
onTap: ()=>setState(() {
isOpen[0] = !isOpen[0];
}),
trailing:isOpen[0]?const Icon(Icons.keyboard_arrow_right):const Icon(Icons.keyboard_arrow_down)
),
Offstage(
offstage:isOpen[0],
child:ListTile(
leading: const Icon(Icons.radio_button_unchecked),
title: Text("路由及导航"),
onTap: ()=>{},
trailing:const Icon(Icons.keyboard_arrow_right)
),
),
Divider(height: 10.0,indent: 0,color: Colors.blue,thickness:2.0),
ListTile(
leading: const Icon(Icons.photo_library),
title: Text("容器类组件"),
subtitle:Text("用来容纳其他的组件"),
onTap: ()=>setState(() {
isOpen[1] = !isOpen[1];
}),
trailing:isOpen[1]?const Icon(Icons.keyboard_arrow_right):const Icon(Icons.keyboard_arrow_down)
),
Offstage(
offstage:isOpen[1],
child:ListTile(
leading: const Icon(Icons.settings),
title: Text("2"),
onTap: ()=>{},
trailing:const Icon(Icons.keyboard_arrow_right)
),
),
Divider(height: 10.0,indent: 0,color: Colors.blue,thickness:2.0),
ListTile(
leading: const Icon(Icons.blur_linear),
title: Text("布局类组件"),
subtitle:Text("用来进行界面布局"),
onTap: ()=>setState(() {
isOpen[2] = !isOpen[2];
}),
trailing:isOpen[2]?const Icon(Icons.keyboard_arrow_right):const Icon(Icons.keyboard_arrow_down)
),
Offstage(
offstage:isOpen[2],
child:ListTile(
leading: const Icon(Icons.settings),
title: Text("3"),
onTap: ()=>{},
trailing:const Icon(Icons.keyboard_arrow_right)
),
),
Divider(height: 10.0,indent: 0,color: Colors.blue,thickness:2.0),
ListTile(
leading: const Icon(Icons.chat_bubble_outline),
title: Text("对话框组件"),
subtitle:Text("一类用于提示的组件"),
onTap: ()=>setState(() {
isOpen[3] = !isOpen[3];
}),
trailing:isOpen[3]?const Icon(Icons.keyboard_arrow_right):const Icon(Icons.keyboard_arrow_down)
),
Divider(height: 10.0,indent: 0,color: Colors.blue,thickness:2.0),
ListTile(
leading: const Icon(Icons.redeem),
title: Text("Material组件"),
subtitle:Text("遵循Material Design设计规范的组件"),
onTap: ()=>setState(() {
isOpen[4] = !isOpen[4];
}),
trailing:isOpen[4]?const Icon(Icons.keyboard_arrow_right):const Icon(Icons.keyboard_arrow_down)
),
Divider(height: 10.0,indent: 0,color: Colors.blue,thickness:2.0),
ListTile(
leading: const Icon(Icons.phone_iphone),
title: Text("Cupertino组件"),
subtitle:Text("一类iOS风格的组件"),
onTap: ()=>setState(() {
isOpen[5] = !isOpen[5];
}),
trailing:isOpen[5]?const Icon(Icons.keyboard_arrow_right):const Icon(Icons.keyboard_arrow_down)
),
Divider(height: 10.0,indent: 0,color: Colors.blue,thickness:2.0),
ListTile(
leading: const Icon(Icons.show_chart),
title: Text("手势及动画"),
subtitle:Text("手势识别和动画处理"),
onTap: ()=>setState(() {
isOpen[6] = !isOpen[6];
}),
trailing:isOpen[6]?const Icon(Icons.keyboard_arrow_right):const Icon(Icons.keyboard_arrow_down)
),
Divider(height: 10.0,indent: 0,color: Colors.blue,thickness:2.0),
ListTile(
leading: const Icon(Icons.bubble_chart),
title: Text("自定义组件"),
subtitle:Text("自定义的一类组件,如字体,控件"),
onTap: ()=>setState(() {
isOpen[7] = !isOpen[7];
}),
trailing:isOpen[7]?const Icon(Icons.keyboard_arrow_right):const Icon(Icons.keyboard_arrow_down)
),
],
),
);
}
}
代码上把每个组件都写出来了,Scaffold脚手架后面再说,在其body属性下写了一个ListView组件,它有个children属性,可以容纳一些其它组件,如ListTile、Offstage等,代码上可以很清晰的看到各个组件的属性设置,其中Offstage组件是隐藏组件,用来将ListTile下的组件隐藏起来,点击后再展开,这样就方便的实现了分类分组的效果。
路由及导航
由于接下来各种组件,接口都要在新页面中展示,因此这一部分介绍路由及导航。所谓路由,就像是Android中一般指一个Activity,在iOS中指一个ViewController,它们之间的跳转由导航器Navigator管理,即一组Route对象。

注册路由表和MaterialPageRoute类
注册路由表就是将所有需要的路由放入一个Map<String, WidgetBuilder> routes中,每个路由都有他自己的名字,也叫路由命名。而MaterialPageRoute类是PageRoute的子类,可以针对不同平台,实现与平台页面切换动画风格一致的路由切换动画,我们在main.dart文件中的路由表添加”routeExp”: (BuildContext context) => new RouteExp()进行注册。
既然是导航,就得有起点和终点,起点就是我们主界面上基础组件分类下的路由及导航按钮,终点是一个新的页面,这个页面我们也将用于路由示例的主页面,那么在lib/view下新建basis/routeExp.dart文件
加入以下内容,路由跳转分为无参有参和路由是否入栈等情况,代码已经一目了然
class RouteExp extends StatefulWidget {
final information;
RouteExp({Key key, this.information}) : super(key: key);
@override
_RouteExpState createState() => new _RouteExpState();
}
class _RouteExpState extends State<RouteExp> {
var information;
// _RouteExpState({Key key, this.information});
@override
Widget build(BuildContext context) {
final TextEditingController _controller = TextEditingController();
var _input;
_controller.addListener(() {
_input = _controller.text;
});
_receiveData(BuildContext context) async {
information = await Navigator.push(context,
new MaterialPageRoute(builder: (context) => new Route2Exp()));
}
// TODO: implement build
return Scaffold(
appBar: AppBar(title: Text("路由及导航示例页面1")),
body: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(information == null ? '没有返回数据' : '您好:$information'),
TextField(
controller: _controller,
maxLength: 30,
maxLines: 1,
decoration: InputDecoration(
fillColor: Colors.grey.shade400,
filled: true,
helperText: '请输入',
prefixIcon: Icon(Icons.person),
),
),
RaisedButton(
onPressed: () => Navigator.push(context,
new MaterialPageRoute(builder: (context) => new Route2Exp())),
child: Text('Navigator.push'),
),
RaisedButton(
onPressed: () => Navigator.pushReplacement(context,
new MaterialPageRoute(builder: (context) => new Route2Exp())),
child: Text('Navigator.pushReplacement'),
),
RaisedButton(
onPressed: () => Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => new Route2Exp(
information: _input,
))),
child: Text("Navigator.push+SendData"),
),
RaisedButton(
onPressed: () => _receiveData(context),
child: Text("Navigator.push+ReceiveData"),
)
],
),
);
}
}
代码中提到的AppBar已经home.dart文件中的bottomNavigationBar分别表明顶部导航按钮和底部导航按钮,这两个组件只需要知道它的属性即可使用,至于PopupMenuItem是一个比较常用的弹出组件,后面会专门讲到。

后记
这几章主要是贴代码为主,在这简单回顾一下:
- 新建Flutter工程,在main函数中使用我们的MaterialApp。
- StatefulWidget和StatelessWidget的区别,一个是有状态的,要用继承State类的子类来管理,该类由createState创建,之后凡是遇到更新UI的地方直接调用setState()函数即可,注意不使用此函数是无法达到更新的目的的,可以简单理解为通知系统UI重绘。
- 建立系统主题和语言的ChangeNotifierProvider,用来同步整个应用,采用的是订阅通知的模式。
- 注册路由表,为我们所有的路由命名,统一管理,不同的路由跳转方式有不同的结果。
- 在yaml文件中进行资源导入,注意其写法以及导入包的版本。
Flutter框架由于采用代码布局,所有嵌套的层次深度都很高,也是学习的一大障碍,但是好歹这个障碍是可以依靠编程习惯去解决的,在组织代码结构和建立文件时,必定要有清晰的思路和松耦合的原则。在之后所有遇到的有状态组件,都将会提供一个继承该组件的自定义的版本,等所有的组件介绍完成之后就可以用这些基本组件出组合新的组件了,期待哦!
码字不易,请勿直接抄袭,如果对您有协助或者感兴趣,欢迎转载,点赞或评论,关注或私信@小宇的代码日记可获得更多源码哦!
















- 最新
- 最热
只看作者