Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
G
gm_flutter
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
林生雨
gm_flutter
Commits
7300f49a
Commit
7300f49a
authored
Jun 29, 2020
by
林生雨
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
commit
parent
faeec501
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
1034 additions
and
210 deletions
+1034
-210
README.md
README.md
+76
-5
ClueApi.dart
lib/ClueModel/server/api/ClueApi.dart
+11
-0
ClueApi.serv.dart
lib/ClueModel/server/api/ClueApi.serv.dart
+168
-0
DemoPage.dart
lib/DemoPage.dart
+18
-5
DemoPage1.dart
lib/DemoPage1.dart
+55
-0
MainRouter.dart
lib/MainRouter/MainRouter.dart
+12
-0
MainRouterImpl.dart
lib/MainRouter/MainRouterImpl.dart
+11
-0
MainManager.dart
lib/MainRouter/manager/MainManager.dart
+28
-0
NetProxyPage.dart
lib/MainRouter/page/proxy/NetProxyPage.dart
+86
-0
TP.dart
lib/MainRouter/page/test/TP.dart
+98
-0
TestPage.dart
lib/MainRouter/page/test/TestPage.dart
+63
-0
MainApi.dart
lib/MainRouter/service/remote/api/MainApi.dart
+14
-0
MainApi.serv.dart
lib/MainRouter/service/remote/api/MainApi.serv.dart
+187
-0
KYCBean.dart
lib/MainRouter/service/remote/entity/KYCBean.dart
+105
-0
OkBean.dart
lib/MainRouter/service/remote/entity/OkBean.dart
+22
-0
TestBean.dart
lib/MainRouter/service/remote/entity/TestBean.dart
+50
-0
ConversationItemBean.dart
lib/commonModel/bean/ConversationItemBean.dart
+0
-0
PushBean.dart
lib/commonModel/bean/PushBean.dart
+0
-187
Api.dart
lib/commonModel/net/Api.dart
+3
-3
main.dart
lib/main.dart
+16
-10
main.mark.dart
lib/main.mark.dart
+11
-0
No files found.
README.md
View file @
7300f49a
# gm_flutter
flutter 项目介绍:
本项目架构模式 伪组件化+mvvm
伪组件化:
因为flutter比较年轻 用android组件化思想完成的flutter组件化
伪是因为 模块和模块之间仍然可以互相调用 开发时候需要注意
模块和模块之间需要 RouterCenterImpl().findxxx模块().模块方法(参数) 进行通信
模块和模块之前无任何耦合 都依赖common模块 以及一些第三方库
模块创建
需要创建这个模块对外提供方法的抽象类 并需要添加注解 @Router(模块名称, 实现类, 是否注册这个模块)
android组件化思想参考:http://wiki.wanmeizhensuo.com/pages/viewpage.action?pageId=36546074
mvvm:
主要用的flutter的Bloc
一个页面一个文件夹 page和model
-xxx
-xxxPage
-xxxModel
-xxxItem
....
我们开发统一用 StreamBuilder(stream:livedata.stream)
LiveData
<T>
这个就类似一个 通知
livaData.notifyView(data) 会通知到对应的 StreamBuilder
网络请求:
参考UserModel
结构:
xxxModel
-page
-service
-local 持久化本地数据
-remote 网络请求
-api 网络请求接口
-xxxApi
-entity 网络请求Json 解析的bean
-xxxBean
注解生成代码方式
比如接口: get方法 baseurl/api/getToken 参数uid (String/NSString)
Json:{xxxxxx}
A new flutter module project.
第一步:Json转成Bean 可以通过这个网址:https://javiercbk.github.io/json_to_dart/
直接json 复制进去 xxxBean 放到 entity文件夹下面
xxxBean
第二步在api 文件夹目录下 创建 xxxApi抽象类
@ServiceCenter() 注解标识
abstract class xxxApi{
@Get("api/getToken")
xxxBean getToken(
@Query("uid") String uid);
}
第三部 执行脚本 ./build.sh
因为flutter比较年轻 需要命令行生成代码
对生成 xxxApi.serv.dart 文件
调用: xxxApiImpl.getInstance().getToken(uid).listener((xxxbean){
doResult
}).onError((error){
doError
})
弹窗部分:
参考SendFailPicker
flutter 依赖包网址:https://pub.dev
可以根据库 到 https://pub.dev 看看第三方库的用法
本地持久化数据:
SharedPreferences s = await SharedPreferences.getInstance();
s.getString()......
权限库:
permission_handler
上拉下拉:
pull_to_refresh
百分比适配:
flutter_screenutil
图片库:
cached_network_image
extended_image
文件夹:
path_provider
## Getting Started
For help getting started with Flutter, view our online
[
documentation
](
https://flutter.dev/
)
.
lib/ClueModel/server/api/ClueApi.dart
0 → 100644
View file @
7300f49a
/*
* @author lsy
* @date 2020/6/28
**/
import
'package:flutter_common/Annotations/anno/Get.dart'
;
import
'package:flutter_common/Annotations/anno/ServiceCenter.dart'
;
@ServiceCenter
()
class
ClueApi
{
}
\ No newline at end of file
lib/ClueModel/server/api/ClueApi.serv.dart
0 → 100644
View file @
7300f49a
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// ServiceGenerator
// **************************************************************************
import
'dart:convert'
;
import
'dart:io'
;
import
'package:rxdart/rxdart.dart'
;
import
'package:dio/dio.dart'
;
import
'package:flutter/foundation.dart'
;
const
bool
inProduction
=
const
bool
.
fromEnvironment
(
"dart.vm.product"
);
class
ClueApiImpl
{
static
JsonEncoder
encoder
=
JsonEncoder
.
withIndent
(
' '
);
static
ClueApiImpl
_instance
;
ClueApiImpl
.
_
()
{}
static
ClueApiImpl
getInstance
()
{
if
(
_instance
==
null
)
{
_instance
=
ClueApiImpl
.
_
();
}
return
_instance
;
}
///==================base method==================
Future
<
Response
>
get
(
Dio
_dio
,
url
,
{
data
,
options
,
cancelToken
})
async
{
Response
response
;
try
{
response
=
await
_dio
.
get
(
url
,
queryParameters:
data
,
options:
options
,
cancelToken:
cancelToken
);
_printHttpLog
(
response
);
}
on
DioError
catch
(
e
)
{
print
(
'get error---------
$e
${formatError(e)}
'
);
throw
e
;
}
return
response
;
}
Future
<
Response
>
post
(
Dio
_dio
,
url
,
{
data
,
options
,
cancelToken
})
async
{
Response
response
;
try
{
response
=
await
_dio
.
post
(
url
,
data:
FormData
.
fromMap
(
data
),
options:
options
,
cancelToken:
cancelToken
);
_printHttpLog
(
response
);
}
on
DioError
catch
(
e
)
{
print
(
'get error---------
$e
${formatError(e)}
'
);
throw
e
;
}
return
response
;
}
Future
<
Response
>
put
(
Dio
_dio
,
url
,
{
data
,
options
,
cancelToken
})
async
{
Response
response
;
try
{
response
=
await
_dio
.
put
(
url
,
data:
FormData
.
fromMap
(
data
),
options:
options
,
cancelToken:
cancelToken
);
_printHttpLog
(
response
);
}
on
DioError
catch
(
e
)
{
print
(
'get error---------
$e
${formatError(e)}
'
);
throw
e
;
}
return
response
;
}
Future
<
Response
>
delete
(
Dio
_dio
,
url
,
{
data
,
options
,
cancelToken
})
async
{
Response
response
;
try
{
response
=
await
_dio
.
delete
(
url
,
data:
FormData
.
fromMap
(
data
),
options:
options
,
cancelToken:
cancelToken
);
_printHttpLog
(
response
);
}
on
DioError
catch
(
e
)
{
print
(
'get error---------
$e
${formatError(e)}
'
);
throw
e
;
}
return
response
;
}
Future
<
Response
>
upload
(
Dio
_dio
,
url
,
String
key
,
String
path
,
{
Map
<
String
,
dynamic
>
data
,
options
,
cancelToken
})
async
{
Response
response
;
print
(
"UPLOAD===> URL:
$url
{
$key
:
$path
} data:
$data
"
);
MultipartFile
file
=
await
MultipartFile
.
fromFile
(
path
,
filename:
path
.
substring
(
path
.
lastIndexOf
(
"/"
)
+
1
,
path
.
length
));
if
(
data
==
null
)
{
data
=
new
Map
<
String
,
dynamic
>();
}
data
.
putIfAbsent
(
key
,
()
=>
file
);
try
{
response
=
await
_dio
.
post
(
url
,
data:
FormData
.
fromMap
(
data
),
options:
options
,
cancelToken:
cancelToken
);
_printHttpLog
(
response
);
}
on
DioError
catch
(
e
)
{
print
(
'get error---------
$e
${formatError(e)}
'
);
throw
e
;
}
return
response
;
}
void
_printHttpLog
(
Response
response
)
{
if
(!
inProduction
)
{
try
{
printRespond
(
response
);
}
catch
(
ex
)
{
print
(
"Http Log"
+
" error......"
);
}
}
}
static
void
printRespond
(
Response
response
)
{
Map
httpLogMap
=
Map
();
httpLogMap
.
putIfAbsent
(
"requestMethod"
,
()
=>
"
${response.request.method}
"
);
httpLogMap
.
putIfAbsent
(
"requestUrl"
,
()
=>
"
${response.request.uri}
"
);
httpLogMap
.
putIfAbsent
(
"requestHeaders"
,
()
=>
response
.
request
.
headers
);
httpLogMap
.
putIfAbsent
(
"requestQueryParameters"
,
()
=>
response
.
request
.
queryParameters
);
if
(
response
.
request
.
data
is
FormData
)
{
httpLogMap
.
putIfAbsent
(
"requestDataFields"
,
()
=>
((
response
.
request
.
data
as
FormData
).
fields
.
toString
()));
}
httpLogMap
.
putIfAbsent
(
"respondData"
,
()
=>
json
.
decode
(
response
.
toString
()));
printJson
(
httpLogMap
);
}
static
void
printJson
(
Object
object
)
{
try
{
var
encoderString
=
encoder
.
convert
(
object
);
debugPrint
(
encoderString
);
}
catch
(
e
)
{
print
(
e
);
}
}
String
formatError
(
DioError
e
)
{
String
reason
=
""
;
if
(
e
.
type
==
DioErrorType
.
CONNECT_TIMEOUT
)
{
reason
=
"连接超时
${e.message}
"
;
}
else
if
(
e
.
type
==
DioErrorType
.
SEND_TIMEOUT
)
{
reason
=
"请求超时
${e.message}
"
;
}
else
if
(
e
.
type
==
DioErrorType
.
RECEIVE_TIMEOUT
)
{
reason
=
"响应超时
${e.message}
"
;
}
else
if
(
e
.
type
==
DioErrorType
.
RESPONSE
)
{
reason
=
"出现异常
${e.message}
"
;
}
else
if
(
e
.
type
==
DioErrorType
.
CANCEL
)
{
reason
=
"请求取消
${e.message}
"
;
}
else
{
reason
=
"未知错误
${e.message}
"
;
}
return
reason
;
}
}
lib/DemoPage.dart
View file @
7300f49a
...
@@ -4,6 +4,7 @@
...
@@ -4,6 +4,7 @@
**/
**/
import
'package:flutter/cupertino.dart'
;
import
'package:flutter/cupertino.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_boost/flutter_boost.dart'
;
class
DemoPage
extends
StatefulWidget
{
class
DemoPage
extends
StatefulWidget
{
@override
@override
...
@@ -17,6 +18,14 @@ class DemoState extends State<DemoPage> {
...
@@ -17,6 +18,14 @@ class DemoState extends State<DemoPage> {
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
return
Scaffold
(
appBar:
AppBar
(
appBar:
AppBar
(
leading:
GestureDetector
(
onTap:
()
{
FlutterBoost
.
singleton
.
closeByContext
(
context
);
},
child:
Container
(
color:
Colors
.
black
,
),
),
centerTitle:
true
,
centerTitle:
true
,
title:
Text
(
"ww"
),
title:
Text
(
"ww"
),
),
),
...
@@ -26,11 +35,15 @@ class DemoState extends State<DemoPage> {
...
@@ -26,11 +35,15 @@ class DemoState extends State<DemoPage> {
children:
<
Widget
>[
children:
<
Widget
>[
Column
(
Column
(
children:
<
Widget
>[
children:
<
Widget
>[
Container
(
GestureDetector
(
width:
50
,
onTap:
()
{
height:
50
,
FlutterBoost
.
singleton
.
open
(
"sample"
);
color:
Colors
.
redAccent
,
},
)
child:
Container
(
width:
50
,
height:
50
,
color:
Colors
.
redAccent
,
))
],
],
)
)
],
],
...
...
lib/DemoPage1.dart
0 → 100644
View file @
7300f49a
/*
* @author lsy
* @date 2020/6/20
**/
import
'package:flutter/cupertino.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_boost/flutter_boost.dart'
;
class
DemoPage1
extends
StatefulWidget
{
@override
State
<
StatefulWidget
>
createState
()
{
return
DemoState
();
}
}
class
DemoState
extends
State
<
DemoPage1
>
{
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
appBar:
AppBar
(
leading:
GestureDetector
(
onTap:
()
{
FlutterBoost
.
singleton
.
closeByContext
(
context
);
},
child:
Container
(
color:
Colors
.
black
,
),
),
centerTitle:
true
,
title:
Text
(
"ww"
),
),
body:
Stack
(
children:
<
Widget
>[
Stack
(
children:
<
Widget
>[
Column
(
children:
<
Widget
>[
GestureDetector
(
onTap:
()
{
FlutterBoost
.
singleton
.
open
(
"sample"
);
},
child:
Container
(
width:
50
,
height:
50
,
color:
Colors
.
yellow
,
))
],
)
],
)
],
),
);
}
}
lib/MainRouter/MainRouter.dart
0 → 100644
View file @
7300f49a
/*
* @author lsy
* @date 2019-12-31
**/
import
'package:flutter/cupertino.dart'
;
import
'package:flutter_common/Annotations/RouterBaser.dart'
;
import
'package:flutter_common/Annotations/anno/Router.dart'
;
import
'MainRouterImpl.dart'
;
@Router
(
"MainRouter"
,
MainRouterImpl
,
true
)
abstract
class
MainRouter
extends
RouterBaser
{
}
lib/MainRouter/MainRouterImpl.dart
0 → 100644
View file @
7300f49a
/*
* @author lsy
* @date 2019-12-31
**/
import
'package:flutter/src/widgets/framework.dart'
;
import
'MainRouter.dart'
;
class
MainRouterImpl
implements
MainRouter
{
}
lib/MainRouter/manager/MainManager.dart
0 → 100644
View file @
7300f49a
/*
* @author lsy
* @date 2020/6/29
**/
import
'package:flutter/services.dart'
;
import
'package:flutter_boost/flutter_boost.dart'
;
class
MainManager
{
MainManager
.
_
(){
FlutterBoost
.
singleton
.
channel
.
addMethodHandler
((
call
){
});
}
static
MainManager
_mainManager
;
static
MainManager
getInstance
(){
if
(
_mainManager
==
null
){
_mainManager
=
MainManager
.
_
();
}
return
_mainManager
;
}
}
\ No newline at end of file
lib/MainRouter/page/proxy/NetProxyPage.dart
0 → 100644
View file @
7300f49a
/*
* @author lsy
* @date 2020/5/14
**/
import
'package:flutter/cupertino.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_common/commonModel/toast/toast.dart'
;
import
'package:gm_flutter/MainRouter/service/remote/api/MainApi.serv.dart'
;
import
'package:gm_flutter/commonModel/base/BaseComponent.dart'
;
import
'package:gm_flutter/commonModel/base/BaseState.dart'
;
import
'package:gm_flutter/commonModel/net/DioUtil.dart'
;
class
NetProxyPage
extends
StatefulWidget
{
@override
State
<
StatefulWidget
>
createState
()
=>
NetProxyState
();
}
class
NetProxyState
extends
BaseState
<
NetProxyPage
>
{
TextEditingController
_editingController
=
TextEditingController
();
@override
Widget
buildItem
(
BuildContext
context
)
{
return
Scaffold
(
appBar:
baseAppBar
(
centerTitle:
true
,
backClick:
()
{
Navigator
.
pop
(
context
);
},
title:
"设置代理页面"
),
body:
Column
(
children:
<
Widget
>[
Container
(
height:
30
,
),
Container
(
height:
30
,
child:
baseTextField
(
_editingController
,
TextInputType
.
text
,
"请您输入代理网址 例如:172.9.30.10"
),
),
Expanded
(
child:
Container
(),
),
InkWell
(
onTap:
()
{
MainApiImpl
.
getInstance
()
.
isOk
(
DioUtil
.
getInstance
().
getDio
())
.
listen
((
event
)
{}).
onError
((
error
){
print
(
"LSYQQ
${error.toString()}
"
);
});
},
child:
Container
(
margin:
EdgeInsets
.
only
(
bottom:
30
,
left:
15
,
right:
15
),
height:
30
,
color:
Colors
.
blue
,
width:
double
.
maxFinite
,
alignment:
Alignment
.
center
,
child:
baseText
(
"测试代理调用接口"
,
15
,
Colors
.
white
),
)),
InkWell
(
onTap:
()
{
if
(
_editingController
.
text
==
null
)
{
Toast
.
show
(
context
,
"请输入代理"
);
return
;
}
DioUtil
().
setProxy
(
_editingController
.
text
);
Toast
.
show
(
context
,
"设置成功"
);
},
child:
Container
(
margin:
EdgeInsets
.
only
(
bottom:
30
,
left:
15
,
right:
15
),
height:
30
,
color:
Colors
.
blue
,
alignment:
Alignment
.
center
,
width:
double
.
maxFinite
,
child:
baseText
(
"设置代理"
,
15
,
Colors
.
white
),
))
],
),
);
}
@override
void
dispose
()
{
_editingController
.
dispose
();
super
.
dispose
();
}
}
lib/MainRouter/page/test/TP.dart
0 → 100644
View file @
7300f49a
/*
* @author lsy
* @date 2020/5/14
**/
import
'package:flutter/cupertino.dart'
;
import
'package:flutter/material.dart'
;
import
'package:gm_flutter/commonModel/base/BaseState.dart'
;
import
'package:gm_flutter/commonModel/util/WidgetUtil.dart'
;
class
TP
extends
StatefulWidget
{
@override
State
<
StatefulWidget
>
createState
()
=>
TPState
();
}
class
TPState
extends
BaseState
<
TP
>
{
final
key
=
GlobalKey
<
State
>();
@override
Widget
buildItem
(
BuildContext
context
)
{
// if(context!=null){
// throw RecorderRunningException("?????");
// }throw
return
Scaffold
(
appBar:
AppBar
(
actions:
<
Widget
>[
GestureDetector
(
onTap:
()
async
{
},
child:
Container
(
width:
60
,
height:
56
,
color:
Colors
.
red
,
),
)
],
),
body:
Container
(
width:
double
.
maxFinite
,
height:
double
.
maxFinite
,
alignment:
Alignment
.
center
,
child:
InkWell
(
onTap:
()
async
{
RenderBox
box
=
key
.
currentContext
.
findRenderObject
();
Offset
offset
=
box
.
localToGlobal
(
Offset
.
zero
);
final
result
=
await
showMenu
(
context:
context
,
position:
WidgetUtil
.
buttonMenuPosition
(
key
.
currentContext
),
items:
<
PopupMenuItem
<
String
>>[
new
PopupMenuItem
<
String
>(
value:
'value01'
,
child:
Container
(
height:
5
,
width:
5
,
color:
Colors
.
blue
,
)),
new
PopupMenuItem
<
String
>(
value:
'value02'
,
child:
new
Text
(
'Item Two'
)),
new
PopupMenuItem
<
String
>(
value:
'value03'
,
child:
new
Text
(
'Item Three'
)),
]);
},
child:
Container
(
key:
key
,
width:
20
,
height:
20
,
color:
Colors
.
black
,
)),
),
);
}
}
class
My
extends
PopupMenuEntry
{
@override
State
<
StatefulWidget
>
createState
()
=>
MyState
();
@override
double
get
height
=>
50
;
@override
bool
represents
(
value
)
{
return
value
;
}
}
class
MyState
extends
State
<
My
>
{
@override
Widget
build
(
BuildContext
context
)
{
return
Container
(
margin:
EdgeInsets
.
all
(
15
),
height:
50
,
width:
50
,
color:
Colors
.
red
,
);
}
}
lib/MainRouter/page/test/TestPage.dart
0 → 100644
View file @
7300f49a
/*
* @author lsy
* @date 2020/5/13
**/
import
'package:flutter/cupertino.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_common/commonModel/util/JumpUtil.dart'
;
import
'package:gm_flutter/MainRouter/page/proxy/NetProxyPage.dart'
;
import
'package:gm_flutter/commonModel/base/BaseComponent.dart'
;
import
'package:gm_flutter/commonModel/base/BaseState.dart'
;
import
'TP.dart'
;
class
TestPage
extends
StatefulWidget
{
@override
State
<
StatefulWidget
>
createState
()
=>
TestState
();
}
class
TestState
extends
BaseState
<
TestPage
>
{
//Add test here
List
<
Widget
>
innerList
()
{
List
<
Widget
>
list
=
new
List
();
//在这加入
list
.
add
(
listItem
(
"设置代理页面"
,
()
{
JumpUtil
.
jumpToPageRight
(
context
,
NetProxyPage
());
}));
list
.
add
(
listItem
(
"测试测试"
,
()
{
JumpUtil
.
jumpToPageRight
(
context
,
TP
());
}));
return
list
;
}
@override
Widget
buildItem
(
BuildContext
context
)
{
return
Scaffold
(
appBar:
baseAppBar
(
title:
"测试页面"
,
centerTitle:
true
,
backClick:
()
{
Navigator
.
pop
(
context
);
}),
body:
Container
(
child:
Column
(
children:
innerList
(),
),
),
);
}
Widget
listItem
(
String
text
,
VoidCallback
callback
)
{
return
GestureDetector
(
onTap:
callback
,
child:
Container
(
margin:
EdgeInsets
.
only
(
left:
16
,
right:
16
,
top:
8
,
bottom:
8
),
decoration:
BoxDecoration
(
color:
Colors
.
blue
,
borderRadius:
BorderRadius
.
circular
(
5.0
)),
width:
double
.
maxFinite
,
height:
60
,
alignment:
Alignment
.
center
,
child:
baseText
(
text
,
18
,
Colors
.
white
),
));
}
}
lib/MainRouter/service/remote/api/MainApi.dart
0 → 100644
View file @
7300f49a
/*
* @author lsy
* @date 2020/5/14
**/
import
'package:flutter_common/Annotations/anno/Get.dart'
;
import
'package:flutter_common/Annotations/anno/ServiceCenter.dart'
;
import
'package:gm_flutter/MainRouter/service/remote/entity/TestBean.dart'
;
@ServiceCenter
()
abstract
class
MainApi
{
@Get
(
"api/demo/test"
)
TestBean
isOk
();
}
\ No newline at end of file
lib/MainRouter/service/remote/api/MainApi.serv.dart
0 → 100644
View file @
7300f49a
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// ServiceGenerator
// **************************************************************************
import
'dart:convert'
;
import
'dart:io'
;
import
'package:rxdart/rxdart.dart'
;
import
'package:dio/dio.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:gm_flutter/MainRouter/service/remote/entity/TestBean.dart'
;
const
bool
inProduction
=
const
bool
.
fromEnvironment
(
"dart.vm.product"
);
class
MainApiImpl
{
static
JsonEncoder
encoder
=
JsonEncoder
.
withIndent
(
' '
);
static
MainApiImpl
_instance
;
MainApiImpl
.
_
()
{}
static
MainApiImpl
getInstance
()
{
if
(
_instance
==
null
)
{
_instance
=
MainApiImpl
.
_
();
}
return
_instance
;
}
Stream
<
TestBean
>
isOk
(
Dio
_dio
,
)
{
return
Stream
.
fromFuture
(
get
(
_dio
,
'api/demo/test'
)).
flatMap
((
value
)
{
if
(
value
!=
null
&&
(
value
.
statusCode
>=
200
&&
value
.
statusCode
<
300
))
{
return
Stream
.
fromFuture
(
compute
(
parseTestBean
,
value
.
toString
()));
}
else
{
throw
Exception
(
"--未知网络错误--"
);
}
});
}
///==================base method==================
Future
<
Response
>
get
(
Dio
_dio
,
url
,
{
data
,
options
,
cancelToken
})
async
{
Response
response
;
try
{
response
=
await
_dio
.
get
(
url
,
queryParameters:
data
,
options:
options
,
cancelToken:
cancelToken
);
_printHttpLog
(
response
);
}
on
DioError
catch
(
e
)
{
print
(
'get error---------
$e
${formatError(e)}
'
);
throw
e
;
}
return
response
;
}
Future
<
Response
>
post
(
Dio
_dio
,
url
,
{
data
,
options
,
cancelToken
})
async
{
Response
response
;
try
{
response
=
await
_dio
.
post
(
url
,
data:
FormData
.
fromMap
(
data
),
options:
options
,
cancelToken:
cancelToken
);
_printHttpLog
(
response
);
}
on
DioError
catch
(
e
)
{
print
(
'get error---------
$e
${formatError(e)}
'
);
throw
e
;
}
return
response
;
}
Future
<
Response
>
put
(
Dio
_dio
,
url
,
{
data
,
options
,
cancelToken
})
async
{
Response
response
;
try
{
response
=
await
_dio
.
put
(
url
,
data:
FormData
.
fromMap
(
data
),
options:
options
,
cancelToken:
cancelToken
);
_printHttpLog
(
response
);
}
on
DioError
catch
(
e
)
{
print
(
'get error---------
$e
${formatError(e)}
'
);
throw
e
;
}
return
response
;
}
Future
<
Response
>
delete
(
Dio
_dio
,
url
,
{
data
,
options
,
cancelToken
})
async
{
Response
response
;
try
{
response
=
await
_dio
.
delete
(
url
,
data:
FormData
.
fromMap
(
data
),
options:
options
,
cancelToken:
cancelToken
);
_printHttpLog
(
response
);
}
on
DioError
catch
(
e
)
{
print
(
'get error---------
$e
${formatError(e)}
'
);
throw
e
;
}
return
response
;
}
Future
<
Response
>
upload
(
Dio
_dio
,
url
,
String
key
,
String
path
,
{
Map
<
String
,
dynamic
>
data
,
options
,
cancelToken
})
async
{
Response
response
;
print
(
"UPLOAD===> URL:
$url
{
$key
:
$path
} data:
$data
"
);
MultipartFile
file
=
await
MultipartFile
.
fromFile
(
path
,
filename:
path
.
substring
(
path
.
lastIndexOf
(
"/"
)
+
1
,
path
.
length
));
if
(
data
==
null
)
{
data
=
new
Map
<
String
,
dynamic
>();
}
data
.
putIfAbsent
(
key
,
()
=>
file
);
try
{
response
=
await
_dio
.
post
(
url
,
data:
FormData
.
fromMap
(
data
),
options:
options
,
cancelToken:
cancelToken
);
_printHttpLog
(
response
);
}
on
DioError
catch
(
e
)
{
print
(
'get error---------
$e
${formatError(e)}
'
);
throw
e
;
}
return
response
;
}
void
_printHttpLog
(
Response
response
)
{
if
(!
inProduction
)
{
try
{
printRespond
(
response
);
}
catch
(
ex
)
{
print
(
"Http Log"
+
" error......"
);
}
}
}
static
void
printRespond
(
Response
response
)
{
Map
httpLogMap
=
Map
();
httpLogMap
.
putIfAbsent
(
"requestMethod"
,
()
=>
"
${response.request.method}
"
);
httpLogMap
.
putIfAbsent
(
"requestUrl"
,
()
=>
"
${response.request.uri}
"
);
httpLogMap
.
putIfAbsent
(
"requestHeaders"
,
()
=>
response
.
request
.
headers
);
httpLogMap
.
putIfAbsent
(
"requestQueryParameters"
,
()
=>
response
.
request
.
queryParameters
);
if
(
response
.
request
.
data
is
FormData
)
{
httpLogMap
.
putIfAbsent
(
"requestDataFields"
,
()
=>
((
response
.
request
.
data
as
FormData
).
fields
.
toString
()));
}
httpLogMap
.
putIfAbsent
(
"respondData"
,
()
=>
json
.
decode
(
response
.
toString
()));
printJson
(
httpLogMap
);
}
static
void
printJson
(
Object
object
)
{
try
{
var
encoderString
=
encoder
.
convert
(
object
);
debugPrint
(
encoderString
);
}
catch
(
e
)
{
print
(
e
);
}
}
String
formatError
(
DioError
e
)
{
String
reason
=
""
;
if
(
e
.
type
==
DioErrorType
.
CONNECT_TIMEOUT
)
{
reason
=
"连接超时
${e.message}
"
;
}
else
if
(
e
.
type
==
DioErrorType
.
SEND_TIMEOUT
)
{
reason
=
"请求超时
${e.message}
"
;
}
else
if
(
e
.
type
==
DioErrorType
.
RECEIVE_TIMEOUT
)
{
reason
=
"响应超时
${e.message}
"
;
}
else
if
(
e
.
type
==
DioErrorType
.
RESPONSE
)
{
reason
=
"出现异常
${e.message}
"
;
}
else
if
(
e
.
type
==
DioErrorType
.
CANCEL
)
{
reason
=
"请求取消
${e.message}
"
;
}
else
{
reason
=
"未知错误
${e.message}
"
;
}
return
reason
;
}
}
TestBean
parseTestBean
(
String
value
)
{
return
TestBean
.
fromJson
(
json
.
decode
(
value
));
}
lib/MainRouter/service/remote/entity/KYCBean.dart
0 → 100644
View file @
7300f49a
/*
* @author lsy
* @date 2020/5/14
**/
class
KYCBean
{
int
error
;
String
message
;
Data
data
;
KYCBean
({
this
.
error
,
this
.
message
,
this
.
data
});
KYCBean
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
error
=
json
[
'error'
];
message
=
json
[
'message'
];
data
=
json
[
'data'
]
!=
null
?
new
Data
.
fromJson
(
json
[
'data'
])
:
null
;
}
Map
<
String
,
dynamic
>
toJson
()
{
final
Map
<
String
,
dynamic
>
data
=
new
Map
<
String
,
dynamic
>();
data
[
'error'
]
=
this
.
error
;
data
[
'message'
]
=
this
.
message
;
if
(
this
.
data
!=
null
)
{
data
[
'data'
]
=
this
.
data
.
toJson
();
}
return
data
;
}
}
class
Data
{
bool
needScan
;
List
<
Questions
>
questions
;
Data
({
this
.
needScan
,
this
.
questions
});
Data
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
needScan
=
json
[
'need_scan'
];
if
(
json
[
'questions'
]
!=
null
)
{
questions
=
new
List
<
Questions
>();
json
[
'questions'
].
forEach
((
v
)
{
questions
.
add
(
new
Questions
.
fromJson
(
v
));
});
}
}
Map
<
String
,
dynamic
>
toJson
()
{
final
Map
<
String
,
dynamic
>
data
=
new
Map
<
String
,
dynamic
>();
data
[
'need_scan'
]
=
this
.
needScan
;
if
(
this
.
questions
!=
null
)
{
data
[
'questions'
]
=
this
.
questions
.
map
((
v
)
=>
v
.
toJson
()).
toList
();
}
return
data
;
}
}
class
Questions
{
String
identifier
;
String
question
;
List
<
Choices
>
choices
;
Questions
({
this
.
identifier
,
this
.
question
,
this
.
choices
});
Questions
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
identifier
=
json
[
'identifier'
];
question
=
json
[
'question'
];
if
(
json
[
'choices'
]
!=
null
)
{
choices
=
new
List
<
Choices
>();
json
[
'choices'
].
forEach
((
v
)
{
choices
.
add
(
new
Choices
.
fromJson
(
v
));
});
}
}
Map
<
String
,
dynamic
>
toJson
()
{
final
Map
<
String
,
dynamic
>
data
=
new
Map
<
String
,
dynamic
>();
data
[
'identifier'
]
=
this
.
identifier
;
data
[
'question'
]
=
this
.
question
;
if
(
this
.
choices
!=
null
)
{
data
[
'choices'
]
=
this
.
choices
.
map
((
v
)
=>
v
.
toJson
()).
toList
();
}
return
data
;
}
}
class
Choices
{
int
id
;
String
image
;
String
text
;
Choices
({
this
.
id
,
this
.
image
,
this
.
text
});
Choices
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
id
=
json
[
'id'
];
image
=
json
[
'image'
];
text
=
json
[
'text'
];
}
Map
<
String
,
dynamic
>
toJson
()
{
final
Map
<
String
,
dynamic
>
data
=
new
Map
<
String
,
dynamic
>();
data
[
'id'
]
=
this
.
id
;
data
[
'image'
]
=
this
.
image
;
data
[
'text'
]
=
this
.
text
;
return
data
;
}
}
lib/MainRouter/service/remote/entity/OkBean.dart
0 → 100644
View file @
7300f49a
/*
* @author lsy
* @date 2020/6/3
**/
class
OkBean
{
String
hello
;
String
appName
;
OkBean
({
this
.
hello
,
this
.
appName
});
OkBean
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
hello
=
json
[
'Hello'
];
appName
=
json
[
'app_name'
];
}
Map
<
String
,
dynamic
>
toJson
()
{
final
Map
<
String
,
dynamic
>
data
=
new
Map
<
String
,
dynamic
>();
data
[
'Hello'
]
=
this
.
hello
;
data
[
'app_name'
]
=
this
.
appName
;
return
data
;
}
}
lib/MainRouter/service/remote/entity/TestBean.dart
0 → 100644
View file @
7300f49a
/*
* @author lsy
* @date 2020/6/28
**/
class
TestBean
{
String
hello
;
Data
data
;
bool
success
;
TestBean
({
this
.
hello
,
this
.
data
,
this
.
success
});
TestBean
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
hello
=
json
[
'Hello'
];
data
=
json
[
'data'
]
!=
null
?
new
Data
.
fromJson
(
json
[
'data'
])
:
null
;
success
=
json
[
'success'
];
}
Map
<
String
,
dynamic
>
toJson
()
{
final
Map
<
String
,
dynamic
>
data
=
new
Map
<
String
,
dynamic
>();
data
[
'Hello'
]
=
this
.
hello
;
if
(
this
.
data
!=
null
)
{
data
[
'data'
]
=
this
.
data
.
toJson
();
}
data
[
'success'
]
=
this
.
success
;
return
data
;
}
}
class
Data
{
String
serviceName
;
bool
success
;
String
message
;
Data
({
this
.
serviceName
,
this
.
success
,
this
.
message
});
Data
.
fromJson
(
Map
<
String
,
dynamic
>
json
)
{
serviceName
=
json
[
'service_name'
];
success
=
json
[
'success'
];
message
=
json
[
'message'
];
}
Map
<
String
,
dynamic
>
toJson
()
{
final
Map
<
String
,
dynamic
>
data
=
new
Map
<
String
,
dynamic
>();
data
[
'service_name'
]
=
this
.
serviceName
;
data
[
'success'
]
=
this
.
success
;
data
[
'message'
]
=
this
.
message
;
return
data
;
}
}
lib/commonModel/bean/ConversationItemBean.dart
deleted
100644 → 0
View file @
faeec501
This diff is collapsed.
Click to expand it.
lib/commonModel/bean/PushBean.dart
deleted
100644 → 0
View file @
faeec501
/*
* @author lsy
* @date 2020/5/28
**/
import
'dart:typed_data'
;
class
PushBean
{
int
newMessage
=
0
;
String
userKey
;
List
<
int
>
state
=
[];
String
name
;
int
time
;
String
headUrl
;
String
content
;
int
doctorUserId
;
bool
isStarFromDoctor
;
int
conversation_id
;
String
place
;
Map
<
int
,
MessageItem
>
messageList
=
{};
}
const
CARD_TYPE_DARY
=
"CARD_TYPE_DARY"
;
const
CARD_TYPE_NORMAL
=
"CARD_TYPE_NORMAL"
;
const
CARD_TYPE_VOICE
=
"CARD_TYPE_VOICE"
;
const
CARD_TYPE_PIC
=
"CARD_TYPE_PIC"
;
const
CARD_TYPE_MONEY
=
"CARD_TYPE_MONEY"
;
const
CARD_TYPE_BEAUTY
=
"CARD_TYPE_BEAUTY"
;
const
CARD_TYPE_TIME
=
"CARD_TYPE_TIME"
;
class
MessageItem
{
int
id
;
int
sendTime
;
String
cardType
;
bool
isMe
;
String
headUrl
;
String
sendState
;
MessageDiary
diary
;
MessageNormal
normal
;
MessageTime
time
;
MessageMoney
money
;
MessageVoice
voice
;
MessageBeauty
beauty
;
MessageNativePic
pic
;
MessageItem
(
this
.
id
,
this
.
sendTime
,
{
this
.
cardType
,
this
.
isMe
,
this
.
pic
,
this
.
sendState
,
this
.
diary
,
this
.
normal
,
this
.
time
,
this
.
money
,
this
.
voice
,
this
.
beauty
,
this
.
headUrl
});
static
MessageItem
buildTime
(
int
id
,
int
sendTime
,
String
time
)
{
return
MessageItem
(
id
,
sendTime
,
cardType:
CARD_TYPE_TIME
,
time:
MessageTime
(
time
));
}
static
MessageItem
buildNormal
(
int
id
,
int
sendTime
,
bool
isMe
,
String
content
,
String
url
)
{
return
MessageItem
(
id
,
sendTime
,
cardType:
CARD_TYPE_NORMAL
,
isMe:
isMe
,
headUrl:
url
,
normal:
MessageNormal
(
content
));
}
static
MessageItem
buildMeNativePic
(
int
id
,
int
sendTime
,
String
url
,
String
nativePic
,
String
recordPath
)
{
return
MessageItem
(
id
,
sendTime
,
cardType:
CARD_TYPE_PIC
,
isMe:
true
,
headUrl:
url
,
pic:
MessageNativePic
(
null
,
nativePic
,
null
,
recordPath
));
}
static
MessageItem
buildNetPic
(
int
id
,
int
sendTime
,
bool
isMe
,
String
url
,
String
headUrl
)
{
return
MessageItem
(
id
,
sendTime
,
cardType:
CARD_TYPE_PIC
,
isMe:
isMe
,
headUrl:
headUrl
,
pic:
MessageNativePic
(
url
,
null
,
null
,
null
));
}
static
MessageItem
buildBeauty
(
int
id
,
int
sendTime
,
String
headUrl
,
String
showUrl
,
String
title
,
String
money
,
String
beautyId
)
{
return
MessageItem
(
id
,
sendTime
,
cardType:
CARD_TYPE_BEAUTY
,
isMe:
true
,
headUrl:
headUrl
,
beauty:
MessageBeauty
(
beautyId
,
showUrl
,
title
,
money
));
}
static
MessageItem
buildMoney
(
int
id
,
int
sendTime
,
String
headUrl
,
String
picUrl
)
{
return
MessageItem
(
id
,
sendTime
,
cardType:
CARD_TYPE_MONEY
,
isMe:
true
,
headUrl:
headUrl
,
money:
MessageMoney
(
picUrl
));
}
static
MessageItem
buildDiary
(
int
id
,
int
sendTime
,
String
headUrl
,
String
leftUrl
,
String
rightUrl
,
String
userName
,
List
<
String
>
tags
,
String
jumpUrl
)
{
return
MessageItem
(
id
,
sendTime
,
cardType:
CARD_TYPE_DARY
,
isMe:
true
,
headUrl:
headUrl
,
diary:
MessageDiary
(
leftUrl
,
rightUrl
,
userName
,
tags
,
jumpUrl
));
}
static
MessageItem
buildVoice
(
int
id
,
int
sendTime
,
bool
isMe
,
String
headUrl
,
String
path
,
String
url
,
String
during
)
{
return
MessageItem
(
id
,
sendTime
,
cardType:
CARD_TYPE_VOICE
,
isMe:
isMe
,
headUrl:
headUrl
,
voice:
MessageVoice
(
during
,
path
,
url
));
}
}
class
MessageNativePic
{
String
url
;
String
nativePath
;
Uint8List
data
;
String
recordPath
;
MessageNativePic
(
this
.
url
,
this
.
nativePath
,
this
.
data
,
this
.
recordPath
);
}
class
MessageBeauty
{
String
id
;
String
url
;
String
title
;
String
money
;
MessageBeauty
(
this
.
id
,
this
.
url
,
this
.
title
,
this
.
money
);
}
class
MessageMoney
{
String
url
;
MessageMoney
(
this
.
url
);
}
class
MessageVoice
{
String
during
;
String
url
;
String
path
;
MessageVoice
(
this
.
during
,
this
.
path
,
this
.
url
);
}
class
MessageTime
{
String
time
;
MessageTime
(
this
.
time
);
}
class
MessageNormal
{
String
content
;
MessageNormal
(
this
.
content
);
}
class
MessageDiary
{
String
leftUrl
;
String
rightUrl
;
String
userName
;
String
jumpUrl
;
List
<
String
>
tags
;
MessageDiary
(
this
.
leftUrl
,
this
.
rightUrl
,
this
.
userName
,
this
.
tags
,
this
.
jumpUrl
);
}
lib/commonModel/net/Api.dart
View file @
7300f49a
...
@@ -13,18 +13,18 @@ import 'DioUtil.dart';
...
@@ -13,18 +13,18 @@ import 'DioUtil.dart';
/**
/**
* 生产环境
* 生产环境
*/
*/
const
String
APP_HOST_RELEASE
=
"http
://profession.paas-merchant.env
"
;
const
String
APP_HOST_RELEASE
=
"http
s://x6cgr5y5-gengmei.mock.coding.io
"
;
/**
/**
* 测试环境
* 测试环境
*/
*/
//const String APP_HOST_DEBUG = "http://backend.paas-merchant.envs";
//const String APP_HOST_DEBUG = "http://backend.paas-merchant.envs";
//const String APP_HOST_DEBUG = "http://doctor.paas-merchant.env";
//const String APP_HOST_DEBUG = "http://doctor.paas-merchant.env";
const
String
APP_HOST_DEBUG
=
"http
://profession.paas-merchant.env
"
;
const
String
APP_HOST_DEBUG
=
"http
s://x6cgr5y5-gengmei.mock.coding.io
"
;
/**
/**
* 开发环境
* 开发环境
*/
*/
const
String
APP_HOST_DEV
=
"http
://profession.paas-merchant.env
"
;
const
String
APP_HOST_DEV
=
"http
s://x6cgr5y5-gengmei.mock.coding.io
"
;
class
Api
{
class
Api
{
static
String
BUILD_CONFIG
;
static
String
BUILD_CONFIG
;
...
...
lib/main.dart
View file @
7300f49a
...
@@ -7,6 +7,7 @@ import 'package:flutter_common/commonModel/util/WindowUtil.dart';
...
@@ -7,6 +7,7 @@ import 'package:flutter_common/commonModel/util/WindowUtil.dart';
import
'package:gm_flutter/commonModel/base/BaseComponent.dart'
;
import
'package:gm_flutter/commonModel/base/BaseComponent.dart'
;
import
'DemoPage.dart'
;
import
'DemoPage.dart'
;
import
'DemoPage1.dart'
;
import
'commonModel/base/BaseUtil.dart'
;
import
'commonModel/base/BaseUtil.dart'
;
import
'commonModel/nav/NavigationService.dart'
;
import
'commonModel/nav/NavigationService.dart'
;
...
@@ -26,6 +27,7 @@ void main() {
...
@@ -26,6 +27,7 @@ void main() {
};
};
runZonedGuarded
(()
{
runZonedGuarded
(()
{
WidgetsFlutterBinding
.
ensureInitialized
();
WidgetsFlutterBinding
.
ensureInitialized
();
runApp
(
runApp
(
MyAppWidget
(),
MyAppWidget
(),
);
);
...
@@ -51,6 +53,9 @@ class MyApp extends State<MyAppWidget> {
...
@@ -51,6 +53,9 @@ class MyApp extends State<MyAppWidget> {
'demoPage'
:
(
pageName
,
params
,
_
)
{
'demoPage'
:
(
pageName
,
params
,
_
)
{
return
DemoPage
();
return
DemoPage
();
},
},
'demoPage1'
:
(
pageName
,
params
,
_
)
{
return
DemoPage1
();
},
});
});
// FlutterBoost.singleton.addBoostContainerLifeCycleObserver((state, settings) {
// FlutterBoost.singleton.addBoostContainerLifeCycleObserver((state, settings) {
// });
// });
...
@@ -63,16 +68,17 @@ class MyApp extends State<MyAppWidget> {
...
@@ -63,16 +68,17 @@ class MyApp extends State<MyAppWidget> {
return
MaterialApp
(
return
MaterialApp
(
theme:
ThemeData
(),
theme:
ThemeData
(),
builder:
FlutterBoost
.
init
(
postPush:
_onRoutePushed
),
builder:
FlutterBoost
.
init
(
postPush:
_onRoutePushed
),
home:
isDebug
// home: isDebug
?
Container
(
// ? Container(
color:
Colors
.
red
,
// color: Colors.red,
)
// )
:
Container
(
// : Container(
color:
Colors
.
white
,
// color: Colors.white,
child:
Center
(
// child: Center(
child:
loadingItem
(),
// child: loadingItem(),
),
// ),
));
// )
);
}
}
void
_onRoutePushed
(
void
_onRoutePushed
(
...
...
lib/main.mark.dart
View file @
7300f49a
...
@@ -5,9 +5,12 @@
...
@@ -5,9 +5,12 @@
// **************************************************************************
// **************************************************************************
//ClueRouterImpl is resign : true
//ClueRouterImpl is resign : true
//MainRouterImpl is resign : true
import
"package:gm_flutter/ClueModel/ClueRouterImpl.dart"
;
import
"package:gm_flutter/ClueModel/ClueRouterImpl.dart"
;
import
"package:gm_flutter/ClueModel/ClueRouter.dart"
;
import
"package:gm_flutter/ClueModel/ClueRouter.dart"
;
import
"package:gm_flutter/MainRouter/MainRouterImpl.dart"
;
import
"package:gm_flutter/MainRouter/MainRouter.dart"
;
import
"package:flutter_common/Annotations/RouterBaser.dart"
;
import
"package:flutter_common/Annotations/RouterBaser.dart"
;
...
@@ -36,6 +39,7 @@ class RouterCenterImpl {
...
@@ -36,6 +39,7 @@ class RouterCenterImpl {
void
init
()
{
void
init
()
{
map
.
putIfAbsent
(
"ClueRouter"
,
()
=>
ClueRouterImpl
());
map
.
putIfAbsent
(
"ClueRouter"
,
()
=>
ClueRouterImpl
());
map
.
putIfAbsent
(
"MainRouter"
,
()
=>
MainRouterImpl
());
}
}
RouterBaser
getModel
(
String
modelName
)
{
RouterBaser
getModel
(
String
modelName
)
{
...
@@ -48,4 +52,11 @@ class RouterCenterImpl {
...
@@ -48,4 +52,11 @@ class RouterCenterImpl {
}
}
return
map
[
"ClueRouter"
]
as
ClueRouter
;
return
map
[
"ClueRouter"
]
as
ClueRouter
;
}
}
MainRouter
findMainRouter
()
{
if
(
map
[
"MainRouter"
]
==
null
)
{
return
null
;
}
return
map
[
"MainRouter"
]
as
MainRouter
;
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment