今天通過一個實例講解一下Component的使用闲询。下面完成下圖的個人信息界面涤浇。該界面分為基本信息界面和工作信息界面宙拉。兩塊信息通過兩個不同的網(wǎng)絡(luò)請求獲取的笨触,工作信息塊可以點擊右邊的按鈕來展開或收縮詳情萨西,點擊右下角可刷新基本信息界面。
1. 不使用component的實現(xiàn)
- 新建info_page文件夾旭旭,實現(xiàn)page
class InfoPage extends Page<PageState, Map<String, dynamic>> {
InfoPage() :super(
initState: initState,
effect: buildEffect(),
view: buildView,
reducer: buildReducer(),
);
}
接下來添加state,action葱跋,effect持寄,view,reducer文件
- state.dart
class PageState implements Cloneable<PageState> {
String avatar;
String name;
int age;
String company;
String job;
String detail;
PageState();
@override
PageState clone() {
return PageState()..avatar = avatar
..name = name
..age = age
..company = company
..job = job
..detail = detail
;
}
}
PageState initState(Map<String, dynamic> params) {
return PageState();
}
- action.dart
enum PageAction {
updateBase, //更新基本信息
updateJob, //更新工作信息
onRefresh //刷新事件
}
class PageActionCreator {
static Action updateBaseAction(String avatar, String name, int age) {
return Action(
PageAction.updateBase,
payload: <String, dynamic>{'avatar': avatar, 'name': name, 'age': age},
);
}
static Action updateJobAction(String company, String job, String detail) {
return Action(
PageAction.updateJob,
payload: <String, dynamic>{'company': company, 'job': job, 'detail': detail},
);
}
static Action onRefreshAction() {
return Action(
PageAction.onRefresh,
);
}
}
- view.dart
Widget buildView(PageState state, dispatch, ViewService viewService) {
print('_buildView');
return Scaffold(
appBar: AppBar(title: Text('個人信息'),),
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Card(
margin: EdgeInsets.all(15),
child: Padding(
padding: EdgeInsets.all(20),
child: Row(
children: <Widget>[
FadeInImage.assetNetwork(
placeholder: 'images/timg.jpg',
image: state.avatar ?? '',
height: 60,
width: 60,
),
Padding(
padding: EdgeInsets.only(left: 30),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(state.name ?? ''),
Padding(
padding: EdgeInsets.only(top: 10),
child: Text(state.age == null ? '' : state.age.toString()),
)
],
),
),
],
),
),
),
Card(
margin: EdgeInsets.all(15),
child: Padding(
padding: EdgeInsets.all(20),
child: MyExpand(
<Widget>[
Text('公司:${state.company}'),
Padding(
padding: EdgeInsets.only(top: 10),
child: Text('職位:${state.job}'),
),
Padding(
padding: EdgeInsets.only(top: 10),
child: Text('詳情:${state.detail}'),
)
],
),
),
),
],
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.refresh),
onPressed: (){
dispatch(PageActionCreator.onRefreshAction());
}
),
);
}
- 這里MyExpand 是我們自定義的一個可收縮伸展的控件
import 'package:flutter/material.dart';
class MyExpand extends StatefulWidget {
final List<Widget> widgets;
// MyExpand(this.widgets):super(){
// assert(widgets != null && widgets.length > 2);
// };
MyExpand(this.widgets);
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return MyExpandState();
}
}
class MyExpandState extends State<MyExpand> {
bool expand = false;
List<Widget> widgets = <Widget>[];
@override
void initState() {
super.initState();
// widgets = widget.widgets;
}
@override
Widget build(BuildContext context) {
if(expand == true) {
widgets.clear();
widgets.addAll(widget.widgets);
} else {
widgets.clear();
widgets.add(widget.widgets[0]);
widgets.add(widget.widgets[1]);
}
return Stack(
children: <Widget>[
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: widgets,
),
Align(
alignment: Alignment.topRight,
child: IconButton(
icon: Icon(expand == true ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down),
onPressed: () {
setState(() {
expand = !expand;
});
},
),
),
],
);
}
}
- effect.dart
Effect<PageState> buildEffect() {
return combineEffects(<Object, Effect<PageState>>{
Lifecycle.initState: _init,
PageAction.onRefresh: _onRefresh,
});
}
void _init(Action action, Context<PageState> ctx) {
println("Effect: _init");
_getBaseInfo().then((_){
ctx.dispatch(PageActionCreator.updateBaseAction(_.avatar, _.name, _.age));
});
_getJobInfo().then((_){
ctx.dispatch(PageActionCreator.updateJobAction(_.company, _.job, _.detail));
});
}
Future<BaseInfo> _getBaseInfo() async { //模擬通過網(wǎng)絡(luò)獲取基本信息
return BaseInfo('http://b-ssl.duitang.com/uploads/item/201510/08/20151008192345_uPC5U.jpeg', '小恐龍', 24);
}
Future<JobInfo> _getJobInfo() async { //模擬通過網(wǎng)絡(luò)獲取工作信息
return JobInfo('xxx傳媒有限公司', '設(shè)計師', 'woshixiangqing');
}
void _onRefresh(Action action, Context<PageState> ctx) {
_getBaseInfo().then((_){
ctx.dispatch(PageActionCreator.updateBaseAction(_.avatar, _.name, _.age));
});
}
- 添加兩個model類
class BaseInfo {
String avatar;
String name;
int age;
BaseInfo(this.avatar, this.name, this.age);
}
class JobInfo {
String company;
String job;
String detail;
JobInfo(this.company, this.job, this.detail);
}
實現(xiàn)效果如下
2. 使用component的實現(xiàn)
下面使用Component進(jìn)行改造
2.1 改造基本信息
2.1.1 添加BaseComponent
在info_page 文件夾下面添加base_component文件夾娱俺,由于基本信息塊只顯示信息沒有動作事件稍味,只需添加state, component, view三個文件
- Component與Page類似,實際上Page是Component的子類
class BaseComponent extends Component<BaseState> {
BaseComponent():super(
view: buildView,
);
}
- BaseState只有頭像荠卷,名稱模庐,年齡三個屬性
class BaseState implements Cloneable<BaseState> {
String avatar;
String name;
int age;
BaseState();
@override
BaseState clone() {
return BaseState()..avatar = avatar
..name = name
..age = age
;
}
}
BaseState initState(Map<String, dynamic> params) {
return BaseState();
}
- view從Page的view中抽取
Widget buildView(
BaseState state,
Dispatch dispatch,
ViewService viewService,
) {
return Card(
margin: EdgeInsets.all(15),
child: Padding(
padding: EdgeInsets.all(20),
child: Row(
children: <Widget>[
FadeInImage.assetNetwork(
placeholder: 'images/timg.jpg',
image: state.avatar ?? '',
height: 60,
width: 60,
),
Padding(
padding: EdgeInsets.only(left: 30),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(state.name ?? ''),
Padding(
padding: EdgeInsets.only(top: 10),
child: Text(state.age == null ? '' : state.age.toString()),
)
],
),
),
],
),
),
);
}
2.1.2 實現(xiàn)BaseComponent與Page的連接
在info_page下的state文件實現(xiàn)BaseConnector,BaseConnector繼承自ConnOp油宜,復(fù)寫get和set方法掂碱,get方法數(shù)據(jù)從page傳到Component怜姿,set方法數(shù)據(jù)從Component傳到page。
class BaseConnector extends ConnOp<PageState, BaseState> {
@override
BaseState get(PageState page) {
final BaseState sub = BaseState();
sub.avatar = page.avatar;
sub.name = page.name;
sub.age = page.age;
return sub;
}
@override
void set(PageState page, BaseState sub) {
page.avatar = sub.avatar;
page.name = sub.name;
page.age = sub.age;
}
}
在info_page的page文件下添加dependencies
class InfoPage extends Page<PageState, Map<String, dynamic>> {
InfoPage() :super(
initState: initState,
effect: buildEffect(),
view: buildView,
reducer: buildReducer(),
dependencies: Dependencies<PageState>(
slots: <String, Dependent<PageState>>{
'base': BaseConnector() + BaseComponent()
}),
);
}
在info_page的view文件下疼燥,將剛剛基本信息塊用下面的代碼代替
viewService.buildComponent('base'),
重啟應(yīng)用沧卢,效果還是和原來一樣
2.2 改造工作信息塊
與2.1基本相同
未完待續(xù)...