IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> MVC,深入浅出Android开发 -> 正文阅读

[移动开发]MVC,深入浅出Android开发

abstract class IBaseView {
showLoading();

hideLoading();
}


##### 封装Presenter层

`Presenter`层的公共接口只需提供对`View`层的注册以及反注册,如下面代码所示

abstract class IBasePresenter {
void onAttachView(V view);

void onDetachView();
}


下面对`Presenter`层的代码实现上面所提供的接口,如下面代码所示

abstract class BasePresenter extends IBasePresenter {
V view;

@override
void onAttachView(IBaseView view) {
this.view = view;
}

@override
void onDetachView() {
this.view = null;
}
}


##### 封装State基类

在`State`基类中,需要提供`Presenter`的初始化的方法、loading状态、数据初始化以及视图的构建等,如下面代码所示

abstract class BaseState<T extends StatefulWidget, P extends BasePresenter,
V extends IBaseView> extends State implements IBaseView {
P presenter;

bool isLoading = false;

P initPresenter();

Widget buildBody(BuildContext context);

void initData() {
}

@override
void initState() {
super.initState();
presenter = initPresenter();
if (presenter != null) {
presenter.onAttachView(this);
}
initData();
}

@override
void dispose() {
super.dispose();
if (presenter != null) {
presenter.onDetachView();
presenter = null;
}
}

@override
@mustCallSuper
Widget build(BuildContext context) {
return new Scaffold(
body: buildBody(context),
);
}

@override
void showLoading() {
setState(() {
isLoading = true;
});
}

@override
void hideLoading() {
setState(() {
isLoading = false;
});
}
}


到此,`MVP`框架已经封装完,下面只需对登录界面做相应的实现即可。

##### 实现登录逻辑

在进行登录时,`Presenter`层需要向`View`层提供登录接口,当进行登录完毕后,需要向`View`层进行登录状态的反馈,所以`View`需要提供登录成功、失败两个接口,如下面代码所示

abstract class ILoginPresenter extends BasePresenter {
void login(String name, String password);
}

abstract class ILoginView extends IBaseView {
void onLoginSuccess(UserBean userBean);

void onLoginFailed();
}


当相关接口定义完毕后,首先实现登录的`Presenter`层的代码,如下面代码所示

class LoginPresenter extends ILoginPresenter {
@override
void login(String name, String password) async {
if (view != null) {
view.showLoading();
}
final login = await LoginManager.instance.login(name, password);
//授权成功
if (login != null) {
final user = await LoginManager.instance.getMyUserInfo();
if (user != null) {
if (view != null) {
view.hideLoading();
view.onLoginSuccess(user);
} else {
view.hideLoading();
view.onLoginFailed();
}
}
} else {
if (view != null) {
view.hideLoading();
view.onLoginFailed();
}
}
}
}


然后对登录`State`的代码进行实现,如下面代码所示

class _LoginPageState extends BaseState<LoginPage, LoginPresenter, ILoginView>
implements ILoginView {

@override
void initData() {
super.initData();
}

@override
Widget buildBody(BuildContext context) {
return null;
}

@override
LoginPresenter initPresenter() {
return LoginPresenter();
}

@override
void onLoginSuccess(UserBean userBean) {
NavigatorUtil.goHome(context, userBean);
}

@override
void onLoginFailed() {
ToastUtil.showToast(‘登录失败,请重新登录’);
}
}


相关代码已经封装完毕,最后只需调用登录相关逻辑,如下面代码所示

_login() {
if (presenter != null) {
String name = _nameController.text;
String password = _passwordController.text;
presenter.login(name, password);
}
}


BloC
----

关于什么是BloC,可以参考[\[Flutter Package\]状态管理之BLoC的封装]( )和[Flutter | 状态管理探索篇——BLoC(三)]( )。

### 架构视图

![](https://user-gold-cdn.xitu.io/2019/7/15/16bf5068bccb5d8c?imageView2/0/w/1280/h/960/ignore-error/1)

### 程序入口

`main.dart`是程序的入口,完成登录界面的启动,相关代码如下所示

void main() => runApp(BlocApp());

class BlocApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primaryColor: Colors.black,
),
home: BlocProvider(
child: LoginPage(),
bloc: LoginBloc(),
),
);
}
}


上面代码跟`MVC`和`MVP`有不同之处,传入`home`的对象是`BlocProvider`,且其包含了`child`和`bloc`实例。如下面代码所示

class BlocProvider extends StatefulWidget {
final T bloc;
final Widget child;

BlocProvider({
Key key,
@required this.child,
@required this.bloc,
}) : super(key: key);

@override
_BlocProviderState createState() {
return _BlocProviderState();
}

static T of(BuildContext context) {
final type = _typeOf<BlocProvider>();
BlocProvider provider = context.ancestorWidgetOfExactType(type);
return provider.bloc;
}

static Type _typeOf() => T;
}

class _BlocProviderState extends State<BlocProvider> {
static final String TAG = “_BlocProviderState”;

@override
void initState() {
super.initState();
LogUtil.v('initState ’ + T.toString(), tag: TAG);
}

@override
Widget build(BuildContext context) {
LogUtil.v('build ’ + T.toString(), tag: TAG);
return widget.child;
}

@override
void dispose() {
super.dispose();
LogUtil.v('dispose ’ + T.toString(), tag: TAG);
widget.bloc.dispose();
}
}


### 登录流程

BLoC能够允许我们分离业务逻辑,不用考虑什么时候需要刷新屏幕,一切交给StreamBuilder和BLoC就可以完成,所以登录页面继承`StatelessWidget`即可。如下面代码所示

class LoginPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: bloc.stream,
initialData: initialData(),
builder: (BuildContext context,
AsyncSnapshot<LoadingBean> snapshot) {
}
);
}
}


*   `stream`代表了这个stream builder监听的流,这里监听的是`LoginBloc`的stream;
*   `initData`代表初始的值,因为在首次渲染的时候,还未与用户产生交互,也就不会有事件从流中流出,所以需要给首次渲染一个初始值;
*   `builder`函数接收一个位置参数BuildContext和一个snapshot,snapshot就是这个流输出的数据的一个快照,我们可以通过snapshot.data访问快照中的数据,StreamBuilder中的builder是一个AsyncWidgetBuilder,它能够异步构建widget,当检测到有数据从流中流出时,将会重新构建。

#### 创建BloC

首先完成`BloC`基类的封装,基类需要只需要满足登录状态,如下面代码所示

class LoadingBean {
bool isLoading;
T data;

LoadingBean({this.isLoading, this.data});

@override
String toString() {
return ‘LoadingBean{isLoading: $isLoading, data: $data}’;
}
}

abstract class BaseBloc {
static final String TAG = “BaseBloc”;

BehaviorSubject _subject = BehaviorSubject();

Sink get sink => _subject.sink;

Stream get stream => _subject.stream;

void dispose() {
_subject.close();
sink.close();
}
}


#### 创建BloC实例

在登录的`BloC`实例中,完成整个登录过程,我们需要监听账号、密码的输入状态,密码的是否可见状态,以及登录状态,如下面代码所示

class LoginBloc extends BaseBloc<LoadingBean> {
LoadingBean bean;

LoginBloc() {
bean = LoadingBean(
isLoading: false,
data: LoginBlocBean(
name: ‘’,
password: ‘’,
obscure: true,
),
);
}

changeObscure() {
}

changeName(String name) {
}

changePassword(String password) {
}

login(BuildContext context) async {
}

void _showLoading() {
bean.isLoading = true;
sink.add(bean);
}

void _hideLoading() {
bean.isLoading = false;
sink.add(bean);
}
}


#### 文本监听

创建账号和密码两个`TextEditingController`实例,并完成其事件监听,如下面代码所示

final TextEditingController _nameController = new TextEditingController();
final TextEditingController _passwordController = new TextEditingController();

LoginBloc bloc = BlocProvider.of(context);

_nameController.addListener(() {
bloc.changeName(_nameController.text);
});
_passwordController.addListener(() {
bloc.changePassword(_passwordController.text);
});


当文本发生改变时,会调用`LoginBloc`里相应的改变方法,并对相应的文本进行重新复杂,在通过`sink.add()`更新界面,如下面代码所示

changeName(String name) {
bean.data.name = name;
sink.add(bean);
}

changePassword(String password) {
bean.data.password = password;
sink.add(bean);
}


#### 清空账号输入框

与`MVC`一致,可以参考`MVC`。

#### 密码是否可见

需要改变可见状态,调用`LoginBloc`中的`changeObscure`方法,如下面代码所示

changeObscure() {
bean.data.obscure = !bean.data.obscure;
sink.add(bean);
}


#### 触发登录

需要进行网络请求,控制loading的展示和隐藏,这里需要调用`LoginBloc`中的`login`方法,当登录成功后,则跳转主页展示基本信息,不成功则toast提示,如下面代码所示

login(BuildContext context) async {
_showLoading();

final login =
    await LoginManager.instance.login(bean.data.name, bean.data.password);
//授权成功
if (login != null) {
  final user = await LoginManager.instance.getMyUserInfo();
  if (user != null) {
    NavigatorUtil.goHome(context, user);
  } else {
    ToastUtil.showToast('登录失败,请重新登录');
  }
} else {
  ToastUtil.showToast('登录失败,请重新登录');
}

_hideLoading();

}


Redux
-----

`Redux`是网页开发着广泛使用的设计模式,比如用在React.js中。关于它的介绍可以参考文章[Flutter主题切换之flutter redux]( )。

### 架构视图

![](https://user-gold-cdn.xitu.io/2019/7/15/16bf50695c6d9f68?imageView2/0/w/1280/h/960/format/png/ignore-error/1)

### 程序入口

`main.dart`是程序的入口,完成登录界面的启动,相关代码如下所示

void main() {
final store = new Store(
appReducer,
initialState: AppState.initial(),
middleware: [
LoginMiddleware(),
],
);

runApp(
ReduxApp(
store: store,
),
);
}

class ReduxApp extends StatelessWidget {
final Store store;

const ReduxApp({Key key, this.store}) : super(key: key);

@override
Widget build(BuildContext context) {
return StoreProvider(
store: store,
child: StoreConnector<AppState, _ViewModel>(
converter: _ViewModel.fromStore,
builder: (context, vm) {
return MaterialApp(
theme: ThemeData(
primaryColor: Colors.black,
),
home: LoginPage(),
);
},
),
);
}
}

class _ViewModel {
_ViewModel();

static _ViewModel fromStore(Store store) {
return _ViewModel();
}
}


在程序的入口处,对`Store`进行了初始化工作,完成了对`reducer`、`state`、`middleware`初始化工作。

#### 定义action

完成登录需要有请求登录、请求加载中、请求错误、请求成功等几个状态,如下面代码所示

class FetchLoginAction {
final BuildContext context;
final String userName;
final String password;

FetchLoginAction(this.context, this.userName, this.password);
}

class ReceivedLoginAction {
ReceivedLoginAction(
this.token,
this.userBean,
);

final String token;
final UserBean userBean;
}

class RequestingLoginAction {}

class ErrorLoadingLoginAction {}


#### 初始化state

目前只有一个登录功能,所以只需一个登录的state,如下面代码所示

class AppState {
final LoginState loginState;

AppState({
this.loginState,
});

factory AppState.initial() => AppState(
loginState: LoginState.initial(),
);
}

class LoginState {
final bool isLoading;
final String token;

LoginState({this.isLoading, this.token});

factory LoginState.initial() {
return LoginState(
isLoading: false,
token: ‘’,
);
}

LoginState copyWith({bool isLoading, String token}) {
return LoginState(
isLoading: isLoading ?? this.isLoading,
token: token ?? this.token,
);
}
}


#### 初始化reducer

目前只有一个登录功能,所以只需一个登录的reducer,如下面代码所示

AppState appReducer(AppState state, action) {
return AppState(
loginState: loginReducer(state.loginState, action),
);
}

final loginReducer = combineReducers([
TypedReducer<LoginState, RequestingLoginAction>(_requestingLogin),
TypedReducer<LoginState, ReceivedLoginAction>(_receivedLogin),
TypedReducer<LoginState, ErrorLoadingLoginAction>(_errorLoadingLogin),
]);


#### 初始化middleware

登录的中间件暂时只对其做个简单的初始化过程,如下面代码所示

class LoginMiddleware extends MiddlewareClass {
static final String TAG = “LoginMiddleware”;

@override
void call(Store store, action, NextDispatcher next) {
}
}


### 登录流程

Redux能够允许我们分离业务逻辑,不用考虑什么时候需要刷新屏幕,一切交给StoreConnector可以完成,所以登录页面继承`StatelessWidget`即可。如下面代码所示

class LoginPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StoreConnector<AppState, LoginPageViewModel>(
distinct: true,
converter: (store) => LoginPageViewModel.fromStore(store, context),
builder: (_, viewModel) => LoginPageContent(viewModel),
);
}
}


`LoginPageViewModel`只负责登录状态以及登录行为,如下面代码所示

typedef OnLogin = void Function(String name, String password);

class LoginPageViewModel {
static final String TAG = “LoginPageViewModel”;

final OnLogin onLogin;
final bool isLoading;

LoginPageViewModel({this.onLogin, this.isLoading});

static LoginPageViewModel fromStore(
Store store, BuildContext context) {
return LoginPageViewModel(
isLoading: store.state.loginState.isLoading,
onLogin: (String name, String password) {
LogUtil.v(‘name is $name, password is $password’, tag: TAG);
store.dispatch(FetchLoginAction(context, name, password));
},
);
}
}


#### 文本监听

与`MVC`一致,可以参考`MVC`。

#### 清空账号输入框

与`MVC`一致,可以参考`MVC`。

#### 密码是否可见

与`MVC`一致,可以参考`MVC`。

#### 触发登录

在进行登录时,我们只需调用`LoginPageViewModel`内的`onLogin`方法,该方法会通过`store`分发出`FetchLoginAction`,此时中间件`LoginMiddleware`会收到该行为,并对其进行处理。

@override
void call(Store store, action, NextDispatcher next) {
next(action);
if (action is FetchLoginAction) {

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-09-01 12:02:26  更:2021-09-01 12:02:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年10日历 -2024/10/22 13:59:59-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码