!
一.mvvm+provider的介绍
mvvm 模式
provider( Flutter 状态管理框架)
Provider的核心实际上就是InheritedWidget,它实际上是对InheritedWidget的封装,让InheritedWidget在数据管理上能够更加方便的被开发者所使用。
InheritedWidget 能实现全局的状态更新,无法解决局部的问题。
如何使用provider
首先继承 ChangeNotifier
class BaseProvide with ChangeNotifier {
CompositeSubscription compositeSubscription = CompositeSubscription();
/// add [StreamSubscription] to [compositeSubscription]
///
/// 在 [dispose]的时候能进行取消
addSubscription(StreamSubscription subscription){
compositeSubscription.add(subscription);
}
@override
void dispose() {
super.dispose();
compositeSubscription.dispose();
}
}
class RegisterPageState extends State<RegisterPage>{
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => RegisterModel(),
child: switchStatusBar2DarkAppbar(
context: context,
title: "provider demo",
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ChildWidgetA(),
SizedBox(height: 24,),
ChildWidgetB()
],
),
)
)
);
}
}
class ChildWidgetA extends StatelessWidget {
@override
Widget build(BuildContext context) {
debugPrint('ChildWidgetA build');
var model = Provider.of<RegisterModel>(context);
return Container(
color: Colors.redAccent,
height: 108,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text('ChildA', style: new TextStyle(
color: Colors.black, fontSize: CommounUtil.getSp(15))),
Text('Model data: ${model.valueA}', style: new TextStyle(
color: Colors.black, fontSize: CommounUtil.getSp(15))),
RaisedButton(
onPressed: () => model.addA(),
child: Text('add'),
),
],
),
);
}
}
class ChildWidgetB extends StatelessWidget {
@override
Widget build(BuildContext context) {
debugPrint('ChildWidgetB build');
var model = Provider.of<RegisterModel>(context);
return Container(
color: Colors.blueAccent,
height: 108,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text('ChildB', style: new TextStyle(
color: Colors.black, fontSize: CommounUtil.getSp(15))),
Text('Model data: ${model.valueB}', style: new TextStyle(
color: Colors.black, fontSize: CommounUtil.getSp(15))),
RaisedButton(
onPressed: () => model.addB(),
child: Text('add'),
),
],
),
);
}
}
I/flutter (12301): ChildWidgetB build
I/flutter (12301): ChildWidgetA build
I/flutter (12301): ChildWidgetB build
I/flutter (12301): ChildWidgetA build
这种方式当viewmodel中有一个属性发生变化时,A和B都会build
单个局部刷新Selector
class ChildWidgetC extends StatelessWidget {
@override
Widget build(BuildContext context) {
debugPrint('ChildWidgetC build');
return Selector<RegisterModel, int>(
selector: (context, value) => value.valueA,
builder: (BuildContext context, value, Widget child) {
return Container(
color: Colors.redAccent,
height: 108,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text('ChildC',
style: new TextStyle(
color: Colors.black, fontSize: CommounUtil.getSp(15))),
Text('Model data: $value',
style: new TextStyle(
color: Colors.black, fontSize: CommounUtil.getSp(15))),
RaisedButton(
onPressed: () => context.read<RegisterModel>().addA(),
child: Text('add'),
),
],
),
);
});
}
}
class ChildWidgetD extends StatelessWidget {
@override
Widget build(BuildContext context) {
debugPrint('ChildWidgetD build');
return Container(
color: Colors.blueAccent,
height: 108,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text('ChildD',
style: new TextStyle(
color: Colors.black, fontSize: CommounUtil.getSp(15))),
Text('Model data: ${context.select((RegisterModel value) => value.valueB)}',
style: new TextStyle(
color: Colors.black, fontSize: CommounUtil.getSp(15))),
RaisedButton(
onPressed: () => context.read<RegisterModel>().addB(),
child: Text('add'),
),
],
),
);
}
}
有了Selector之后,就可以在同一个数据模型中,根据条件,筛选出不同的刷新条件了,这样就可以避免数据模型中的某个属性变换而引起的整个数据模型刷新了。
详细使用方法参考:pub.flutter-io.cn/packages/pr…
二.项目结构
-
api
--http_util.dart ------------------dio网络请求初始化封装
--net_util.dart ------------------get和post请求封装
-
base
--app_channel.dart ------------------定义不同的开发环境
--app_config.dart ------------------不同的开发环境的地址
--base_provider.dart ------------------provider基类
--base_responce.dart ------------------接口返回的数据类型
--base_widge.dart ------------------基本控件封装
--global_provider_manager.dart ------------------全局的model管理
-
common ------------------ 公共的方法
-
data ------------------ 接口数据解析
-
generated------------------国际化代码
-
http ------------------接口地址
-
l10n ------------------字符串
--intl_en.arb 英文
--intl_zh.arb 中文
-
model------------------各个页面请求model
--login_repository.dart
-
res
--colours.dart------------------颜色的定义
-
router
--application.dart------------------初始化router
--router_handers.dart------------------定义每个页面的路由
--routes.dart-----------------配置路由
-
ui------------------UI页面
-
utils------------------工具类
-
viewmodel------------------每个页面的viewmodel
-
main_dev.dart,main_test.dart,main_uat.dart,main_online.dart
------------------不同开发环境的程序入口
三.网络请求框架dio
class HttpUtil{
static HttpUtil instance;
Dio dio;
BaseOptions options;
static HttpUtil getInstance() {
print('getInstance');
if (instance == null) {
instance = new HttpUtil();
}
return instance;
}
HttpUtil() {
_initDio();
}
_initDio() {
final String token = AppConfig.appTools.getToken();
String baseRequestUrl=Channel.baseURL;
dio = new Dio()
..options = BaseOptions(
baseUrl: baseRequestUrl,
connectTimeout: 20000,
receiveTimeout: 20000)
..interceptors.add(HeaderInterceptor(token))
..interceptors.add(LogInterceptor(responseBody: true, requestBody: true));
}
}
class HeaderInterceptor extends Interceptor {
String token="";
HeaderInterceptor(String token){
this.token=token;
}
@override
onRequest(RequestOptions options) {
if (token != null && token.length > 0) {
options.headers.putIfAbsent('Authorization', () => 'Bearer' + ' ' + token);
}
return super.onRequest(options);
}
}
Stream<BaseResponce> get(String url, {Map<String, dynamic> params}) =>
Stream.fromFuture(_get(url, params: params)).asBroadcastStream();
Future<BaseResponce> _get(String url, {Map<String, dynamic> params}) async {
var response = await HttpUtil.getInstance().dio.get(url, queryParameters: params);
var res = BaseResponce.fromJson(response.data);
return res;
}
Stream<BaseResponce> post(String url,{dynamic body}) =>
Stream.fromFuture(_post(url, body)).asBroadcastStream();
Future<BaseResponce> _post(String url, dynamic body) async {
var response;
if(body==null){
response = await HttpUtil.getInstance().dio.post(url);
}else{
response = await HttpUtil.getInstance().dio.post(url, data: body);
}
var res = BaseResponce.fromJson(response.data);
if(res.code!=200){
}
return res;
}
详细使用地址:pub.flutter-io.cn/packages/di…
四.不同开发环境搭建
默认会有一个main.dart文件
根据四个环境拷贝该文件并命名为main_dev.dart,main_test.dart,main_uat.dart,main_online.dart
每个文件初始化不同的渠道:
五.国际化
1.添加依赖配置
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
flutter_localizations: #国际化
sdk: flutter
2.MaterialApp的localizationsDelegates
MaterialApp(
// theme: ThemeData(
// //项目配置字体,其他主题颜色配置的可以百度
//// fontFamily: Theme.of(context).platform == TargetPlatform.android? (localModel.localeIndex == 1 ? "HanSans":"DIN") : "IOSGILROY",
// ),
debugShowCheckedModeBanner: false,
locale: localModel.locale,
//国际化工厂代理
localizationsDelegates: [
// Intl 插件(需要安装) //S.of(context).title
S.delegate,
RefreshLocalizations.delegate, //下拉刷新
//系统控件 国际化
GlobalCupertinoLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate//文本方向等
],
supportedLocales: S.delegate.supportedLocales,
home: SpalshPage(),
),
3.使用本地化的值
安装插件flutterIntl
重启as,菜单栏的Tool下找到Flutter Intl 并选择Initalize for the project
使用Add Locale生成其他语言的arb文件
使用S.of(context).autoBySystem 可以获取该字符串
六.路由
1.导入依赖包
dependencies:
flutter:
sdk: flutter
# 路由跳转框架 https://segmentfault.com/a/1190000022349982
fluro: "^1.6.3"
2.全局初始化
final router = new Router();///初始化路由
Routes.configureRoutes(router);
Application.router = router;
3.定义路由页面
Handler homeHandler= Handler(handlerFunc: (BuildContext context, Map<String, List<String>> parameters){
return homePage();
});
Handler mineHandler= Handler(handlerFunc: (BuildContext context, Map<String, List<String>> parameters){
return minePage();
});
Handler loginHandler= Handler(handlerFunc: (BuildContext context, Map<String, List<String>> parameters){
return LoginPage();
});
Handler xieyiHandler= Handler(handlerFunc: (BuildContext context, Map<String, List<String>> parameters){
String type=parameters['type'].first;
return WebViewPage(type: type,);
});
Handler registerHandler= Handler(handlerFunc: (BuildContext context, Map<String, List<String>> parameters){
return RegisterPage();
});
4.配置路径
class Routes{
static String home='/home';
static String mine='/mine';
static String login='/login';
static String xieyi='/xieyi/:type';
static String register='/register';
static void configureRoutes(Router router){
router.define(home, handler: homeHandler);
router.define(mine, handler: mineHandler);
router.define(login, handler: loginHandler);
router.define(xieyi, handler: xieyiHandler);
router.define(register, handler: registerHandler);
}
}
详细使用地址:pub.flutter-io.cn/packages/fl…
七.rxdart
响应式编程
简单例子:
Rx.timer(1, new Duration(milliseconds: 700)).listen((event) {
requestPerMission(context);
});