閱讀這遍文章須要具有如下知識:redux
這是我我的的項目結構,僅供參考。有些與官方的 example 不太同樣。例如官方 todoList 的 demo 中是把 connector 與 state 寫在一塊的,我這裏將 connector 單獨抽出了一個文件。api
fish_redux 有一套本身的路由,有三種 路由方式數組
AppRoutes 包含了 State, Reducer, pages && connector,能夠將 store 中的數據 分發到各個頁面。其組裝方式跟 Page 差很少哦。bash
例:app
final AbstractRoutes appRoutes = AppRoutes<AppState>(
preloadedState: AppState.initState(),
pages: {
'homePage': HomePageConnector() + HomePage(),
},
reducer: buildReducer()
);
return MaterialApp(
home: appRoutes.buildPage('homePage', null),
onGenerateRoute: (RouteSettings settings) {
return MaterialPageRoute<Object>(builder: (BuildContext context) {
return appRoutes.buildPage(settings.name, settings.arguments);
});
},
);
/// 想要使用 AppRoutes 發起一個 action,須要將它保存起來,使用它的 store
/// 像下面這樣
appRoutes.store.dispatch(AppActionCreator.someEvent());
複製代碼
PageRoutes 使用起來就比較簡單,由於其不須要定義與分配 state,每一個頁面的 store 在其自身的 Page 裏定義。async
例:ide
final AbstractRoutes pageRoutes = PageRoutes(
pages: <String, Page<Object, dynamic>>{
'homePage': HomePage(),
},
);
return MaterialApp(
home: pageRoutes.buildPage('homePage', null),
onGenerateRoute: (RouteSettings settings) {
return MaterialPageRoute<Object>(builder: (BuildContext context) {
return pageRoutes.buildPage(settings.name, settings.arguments);
});
},
);
複製代碼
HybridRoutes 接受一個 route 數組,數組裏既能夠接受 AppRoutes 也能夠接受 PageRoutes函數
例:性能
final AbstractRoutes hybridRoutes = HybridRoutes(routes: <AbstractRoutes>[
appRoutes,
pageRoutes
]);
複製代碼
這個store是我本身的叫法,類比於 redux 的 store,定義全局的 state.fetch
一、 定義 store 數據
使用 class 來聲明全局 state,在 聲明 page 的時候注入
例:
class PageState implements Cloneable <PageState> {
String someState;
@override
PageState clone() {
return PageState()
..someState = someState
}
}
複製代碼
類比於 redux 的 connect
class HeaderConnector extends ConnOp<PageState, HeaderState> {
@override
HeaderState get(PageState state) {
final HeaderState headerState = HeaderState(
someState: state.someState,
);
return headerState;
}
@override
void set(PageState state, HeaderState headerState) {
state.someState = headerState.someState;
}
}
複製代碼
類比於 redux 的 action,當發起 action 的時候,根據 action 名字匹配 reducer
例:
enum HomeAction {
setSomeState,
}
class HomeActionCreator {
static Action setSomeState(String someState) {
return Action(HomeAction.setSomeState, payload: someState);
}
}
複製代碼
官方推薦的作法是像上面同樣用兩個類管理 action
類比 redux 的 reducer,在這裏處理髮起的 action,並更新 state
例:
Reducer<PageState> buildHomeReducer() {
return asReducer({
HomeAction.setSomeState: setSomeStateHandle,
});
}
PageState setSomeStateHandle(PageState state, Action action) {
final newSomeState = action.payload;
return state.clone()..someState = newSomeState;
}
複製代碼
類比 redux 中的 Middleware,在 dispatch 時調用
例:
import 'package:fish_redux/fish_redux.dart';
Middleware<T> logMiddleware1<T>({
String tag = 'redux',
String Function(T) monitor,
}) {
return ({Dispatch dispatch, Get<T> getState}) {
return (Dispatch next) {
print('1');
return next;
};
};
}
複製代碼
return next
後會執行下一個中間件。每個 Component 都是一個組件,它對視圖(view),修改數據(reducer), 非修改數據操做(effect)這三個邏輯進行了剝離。
Component = View + Effect(可選) + Reducer(可選) + Dependencies(可選)
例:
class HeaderComponent extends Component<HeaderState> {
HeaderComponent()
: super(
view: buildView,
effect: buildEffect(),
reducer: buildReducer(),
dependencies: Dependencies<HeaderState>(
adapter: SomeAdapter(),
slots: <String, Dependent<HeaderState>>{
'avatar': AvatarConnector() + AvaterComponent()
}),
);
}
複製代碼
dependencies 是一個表達組件之間依賴關係的結構。它包含兩個屬性 adapter 和 slots。
view
中被 ViewService.buildComponent
調用使用adapter 是對 listView 的封裝優化。適用於長列表的渲染。其有三種實現方式。
class SomeDynamicAdapter extends DynamicFlowAdapter<PageState> {
SomeDynamicAdapter()
: super(
pool: <String, Component<Object>>{
'task': Task(),
},
connector: Connector(),
// reducer: buildReducer(),
// filter: bindReducerFilter(),
// effect: bindEffect(),
// higherEffect: bindHeightEffect(),
// onError: onError(),
);
}
class Connector extends ConnOp<PageState, List<ItemBean>> {
@override
List<ItemBean> get(PageState state) {
return state.taskList
.map<ItemBean>((TaskState data) => ItemBean('task', data))
.toList(growable: true);
}
}
複製代碼
Connect
類ItemBean
方法將 data
傳進 task
組件staticFlowAdapter 接收 map 類型的數據
例:
class SomeStaticAdapter extends StaticFlowAdapter<PageState> {
SomeStaticAdapter()
: super(
slots: [
SomeComponent().asDependent(SomeComponenConnectt()),
TaskAdapter().asDependent(TaskAdapterConnect()),
],
);
}
複製代碼
CustomFlowAdapter 接受的屬性與 Component 同樣, 惟一的不一樣是 Adapter 的視圖部分返回的是一個 ListAdapter
例:
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/material.dart';
class CommentAdapter extends Adapter<PageState> {
CommentAdapter()
: super(
adapter: buildCommentAdapter,
);
}
ListAdapter buildCommentAdapter(PageState state, Dispatch dispatch, ViewService service) {
final List builders = Collections.compact([
_buildCommentHeader(state, dispatch, service),
]);
if (state.commentList.length > 0) {
builders.addAll(_buildCommentList(state, dispatch, service));
} else {
builders.addAll(_buildEmpty(state, dispatch, service));
}
return ListAdapter(
(BuildContext buildContext, int index) => builders[index](buildContext, index),
builders.length,
);
}
複製代碼
commentList.length > 0
就在 ListAdapter 中加入一個 評論列表,若是沒有評論就加入一個空視圖的 widget。view 是一個輸出 Widget 的上下文無關的函數。其負責視圖層的構建,由 state 驅動。
例:
Widget buildView(HeaderState state, Dispatch dispatch, ViewService viewService) {
final ListAdapter useAdapter = viewService.buildAdapter();
return FlatButton(
child: Container(
child: ListView(
children: <Widget>[
viewService.buildComponent('avatar'),
ListView.builder(
itemBuilder: useAdapter.itemBuilder,
itemCount: useAdapter.itemCount)
],
),
),
onPressed: () {
dispatch(HeaderActionCreator.profileOpen(state.profileIsOpen));
},
);
}
複製代碼
參數:
Effect 是一個處理全部反作用的函數。我把它分爲兩種,一種是對生命週期的回調,一種是對非處理數據事件的回調。這裏面不作任何數據的處理,若是須要處理數據的話就發起一個 action 到 reducer 裏處理。
例:
Effect<PageState> buildEffect() {
return combineEffects(<Object, Effect<PageState>>{
Lifecycle.initState: _initStateEffect,
'onClick': _onClickEffect,
});
}
void _initStateEffect(Action action, Context context)async {
final response = await fetch(
'GET',
'https://xxx.api.xxx',
params: {
'size': 10,
'page': 1,
}
);
context.dispatch(HomeActionCreator.onSetTaskDataAction(response));
}
複製代碼
context.dispatch
了一個 action 去 reducer 中處理。用來構建頁面,每一個頁面都有一個 Page 而且有一個 store。在這裏初始化 store,配置 Middleware,對 Redux 作 AOP 管理。它繼承於 Component
例:
// page, 繼承自 Component,PageState 是 store 裏定義的 state
class HomePage extends Page<PageState, Map<String, dynamic>> {
HomePage()
: super(
view: buildView,
initState: initState,
effect: buildEffect(),
reducer: buildHomeReducer(),
dependencies: Dependencies<PageState>(
adapter: SomeAdapter(),
slots: <String, Dependent<PageState>>{
'header': HeaderConnector() + HeaderComponent(),
}
),
middleware: <Middleware<PageState>>[ // 中間件
logMiddleware1(tag: 'HomePage'),
logMiddleware2(tag: 'HomePage'),
],
);
}
複製代碼
除了上面那些 Component 還有其餘幾個比較簡單或不經常使用的配置,如:OnError 、HigherEffect 等,有時間再補充。