Commit 21f25c43 authored by 杜欣's avatar 杜欣

Merge branch 'test' into featrue/duxin

# Conflicts:
#	lib/ClueModel/server/api/ClueApi.dart
#	lib/ClueModel/server/api/ClueApi.serv.dart
parents 3280ec6b cc889c68
......@@ -2,6 +2,7 @@
* @author lsy
* @date 2020/6/24
**/
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:gm_flutter/ClueModel/ClueRouter.dart';
import 'package:gm_flutter/ClueModel/page/PlansCompareFeed/PlansCompareFeedPage.dart';
......@@ -15,14 +16,14 @@ import 'package:gm_flutter/ClueModel/page/top/TopPage.dart';
class ClueRouterImpl implements ClueRouter {
@override
Widget getProjectDetailsPage() {
return ProjectDetailsPage();
return ProjectDetailsPage(1);
}
@override
Widget getLevelOnePage() {
return LevelOnePage();
}
@override
Widget getLevelTwoPage() {
return LevelTwoPage();
......@@ -30,7 +31,7 @@ class ClueRouterImpl implements ClueRouter {
@override
Widget getPlansCompareFeed() {
return PlansCompareFeedPage();
return PlansCompareFeedPage(123,1);
}
@override
......@@ -38,6 +39,7 @@ class ClueRouterImpl implements ClueRouter {
return PlanPage();
}
@override
Widget getPlanCompareDetailPage() {
return PlanCompareDetailPage();
}
......
/*
* @Author: zx
* @Date: 2020-07-03 20:54:59
* @Last Modified by: zx
* @Last Modified time: 2020-07-04 12:20:45
*/
import 'package:dio/dio.dart';
import 'package:flutter_common/commonModel/live/BaseModel.dart';
import 'package:flutter_common/commonModel/live/LiveData.dart';
import 'package:gm_flutter/ClueModel/server/api/ClueApi.serv.dart';
import 'package:gm_flutter/commonModel/GMBase.dart';
import 'package:gm_flutter/commonModel/rx/RxDispose.dart';
import 'package:flutter_common/commonModel/toast/NativeToast.dart';
import 'package:gm_flutter/commonModel/bean/Pair.dart';
import 'package:gm_flutter/ClueModel/server/entity/DiscussLowPriceModelBean.dart';
import 'package:gm_flutter/ClueModel/server/entity/DiscussLowPriceAuthBean.dart';
class DiscussLowPriceModel extends BaseModel {
LiveData<Pair<int, DiscussLowPriceModelBean>> popViewLive = LiveData();
RxDispose rxDispose = RxDispose();
fetchPopviewData(String planId, String hospitalId, String doctorId) {
ClueApiImpl.getInstance()
.getPopviewData(
DioUtil.getInstance().getDio(), planId, hospitalId, doctorId)
.listen((event) {
if (event.error == 0) {
if (event.data == null) {
popViewLive.notifyView(Pair(ENDLOADING, null));
} else {
popViewLive.notifyView(Pair(ENDLOADING, event.data));
}
} else {
NativeToast.showNativeToast(event.message);
popViewLive.notifyView(Pair(FAIL, null));
}
})
.addToDispose(rxDispose)
.onError((err) {
popViewLive.notifyView(Pair(FAIL, null));
NativeToast.showNativeToast(err.message);
});
}
givePhoneAuth(int leadPhoneRequestId, VoidCallback callback) {
popViewLive.notifyView(Pair(LOADING, null));
ClueApiImpl.getInstance()
.givePhoneAuth(DioUtil.getInstance().getDio(), leadPhoneRequestId)
.listen((event) {
if (event.error == 0) {
} else {
NativeToast.showNativeToast(event.message);
}
callback();
})
.addToDispose(rxDispose)
.onError((err) {
NativeToast.showNativeToast(err.message);
callback();
});
}
@override
void dispose() {
popViewLive.dispost();
rxDispose.dispose();
}
}
/*
* @Author: zx
* @Date: 2020-07-03 13:53:16
* @Last Modified by: zx
* @Last Modified time: 2020-07-04 12:48:35
*/
import 'package:flutter/material.dart';
import 'package:flutter_common/commonModel/picker/base/BaseCenterPicker.dart';
import 'package:gm_flutter/ClueModel/server/entity/DiscussLowPriceModelBean.dart';
import 'package:gm_flutter/commonModel/base/BaseComponent.dart';
import 'package:gm_flutter/ClueModel/page/DiscussLowPrice/DiscussLowPriceModel.dart';
import 'package:gm_flutter/commonModel/bean/Pair.dart';
import 'package:gm_flutter/commonModel/util/DartUtil.dart';
class DiscussLowPricePopView {
static void showPopView(BuildContext content) {
BaseCenterPicker()
..setPicker(_PopView())
..show(content);
}
}
class _PopView implements ICenterPicker {
VoidCallback dismissCallback;
double contentWidth;
DiscussLowPriceModel _model = new DiscussLowPriceModel();
@override
Widget build(BuildContext context, int alp) {
contentWidth = MediaQuery.of(context).size.width - 72;
return StreamBuilder<Pair<int, DiscussLowPriceModelBean>>(
stream: _model.popViewLive.stream,
initialData: _model.popViewLive.data ?? Pair(LOADING, null),
builder: (c, data) {
if (data.data.first == LOADING) {
return loadingItem();
}
if (data.data.second == null || data.data.first == FAIL) {
return errorItem(contentWidth, 308.5, () {});
}
return Opacity(
opacity: alp / 255.0,
child: Container(
width: contentWidth,
height: 308.5,
child: Stack(
alignment: AlignmentDirectional.topCenter,
children: <Widget>[
Positioned(
top: 42.5,
child: setupBodyView(),
),
Positioned(
top: 62.5,
right: 20,
width: 13.5,
height: 13.5,
child: GestureDetector(
onTap: () {
dismissCallback();
},
child: Image.asset(
'assets/discuss_loe_price_cancel.png'),
)),
Container(
width: 85,
height: 85,
decoration: BoxDecoration(boxShadow: [
BoxShadow(
color: Colors.black12,
offset: Offset(0.0, 15.0),
blurRadius: 15.0,
spreadRadius: 1.0)
], shape: BoxShape.circle),
child: ClipOval(
child: Image.network(
_model.popViewLive.data.second.icon,
fit: BoxFit.cover),
),
)
]),
));
});
}
setupBodyView() {
return Container(
height: 266,
width: contentWidth,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(7),
color: Colors.white,
),
child: Container(
alignment: Alignment.center,
margin: EdgeInsets.only(bottom: 0, top: 44),
child: detailView(),
),
);
}
detailView() {
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.only(bottom: 13.5),
alignment: AlignmentDirectional.center,
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
width: 16,
height: 16,
child: Image.asset(
'assets/discuss_loe_price_like.png',
)),
Container(
margin: EdgeInsets.only(left: 4),
child: baseText(_model.popViewLive.data.second.tip, 14,
Color(0xff848484)),
)
]),
),
Container(
margin: EdgeInsets.only(bottom: 25),
child: baseText(
_model.popViewLive.data.second.title, 18, Color(0xff464646),
bold: true)),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(7),
color: Color(0xffECECEC),
),
alignment: Alignment.center,
margin: EdgeInsets.only(bottom: 12),
width: contentWidth - 70,
height: 37,
child: baseText(_model.popViewLive.data.second.authorizePhone, 16,
Color(0xff484848))),
Container(
margin: EdgeInsets.only(bottom: 14),
width: contentWidth - 70,
height: 37,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(7),
// color: Color.fromARGB(alp, 58, 195, 189)
// ),
child: Image.asset(
"assets/discuss_loe_price_auth.png",
),
).gestureDetector(() {
// 一键授权
_model.givePhoneAuth(111, () {
dismissCallback();
});
}),
Container(
width: contentWidth - 70,
margin: EdgeInsets.only(bottom: 23.5),
child: baseText('授权后,您的手机将以“虚拟号”方式提供给机构', 11, Color(0xff999999))),
]);
}
@override
void dispose() {
_model.dispose();
}
@override
void initState(VoidCallback dismissCall, BuildContext context) {
this.dismissCallback = dismissCall;
_model.fetchPopviewData('1', '2', '3');
}
}
......@@ -53,5 +53,6 @@ class PlanCompareDetailModel extends BaseModel {
void dispose() {
stateLive.dispost();
detailLive.dispost();
headerLive.dispost();
}
}
......@@ -47,13 +47,7 @@ class PlanCompareDetailPageState extends BaseState<PlanCompareDetailPage> {
Navigator.pop(context);
}),
body: Container(
child:
// head(),
// planPopularityView()
// planNormalEffectiveAttrsView()
// setupHome([], []),
reloadPage(),
child: reloadPage(),
));
}
......
class CallBackCompareItem {
checkedItem(int id) {}
}
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_common/commonModel/toast/NativeToast.dart';
import 'package:gm_flutter/ClueModel/server/entity/PlansCompareFeed.dart';
import 'package:gm_flutter/commonModel/base/BaseComponent.dart';
import 'package:gm_flutter/commonModel/base/BaseState.dart';
class PlansCompareFeedItemView extends StatelessWidget {
import 'CallBackCompareItem.dart';
class PlansCompareFeedItemView extends StatefulWidget {
Plans plan;
int groupValue = 1;
CallBackCompareItem compareItem;
Set<int> _setCompare;
PlansCompareFeedItemView(this.plan, this.compareItem, this._setCompare);
@override
State<StatefulWidget> createState() => _PlansCompareFeedItemViewState();
}
class _PlansCompareFeedItemViewState
extends BaseState<PlansCompareFeedItemView> {
ImageIcon _checked = ImageIcon(AssetImage("assets/icon_compare_select.png"));
PlansCompareFeedItemView(this.plan);
ImageIcon _unchecked =
ImageIcon(AssetImage("assets/icon_compare_unselect.png"));
ImageIcon _icon = ImageIcon(AssetImage("assets/icon_compare_unselect.png"));
@override
Widget build(BuildContext context) {
return Container(child: getItem());
Widget buildItem(BuildContext context) {
return Container(
margin: EdgeInsets.only(left: 15, right: 15), child: getItem());
}
getItem() {
if (plan.planType == 1) {
if (widget.plan.planType == 2) {
return getFirstLevelItem();
} else {
return getSecondLevelItem();
......@@ -26,21 +45,19 @@ class PlansCompareFeedItemView extends StatelessWidget {
width: double.maxFinite,
height: 90,
alignment: Alignment.centerLeft,
padding: EdgeInsets.only(left: 15, right: 15, top: 20, bottom: 20),
padding: EdgeInsets.only(top: 20, bottom: 20),
child: Row(
children: <Widget>[
Radio(
value: 1,
groupValue: groupValue,
onChanged: (value) {
groupValue = value;
}),
IconButton(
icon: _icon,
onPressed: () => onClickCheckBox(_icon),
),
Container(
margin: EdgeInsets.only(left: 15, right: 15),
child: ClipRRect(
borderRadius: BorderRadius.circular(2.0),
child: Image.network(
plan.projectImage,
widget.plan.projectImage,
width: 50,
height: 50,
fit: BoxFit.fill,
......@@ -56,42 +73,20 @@ class PlansCompareFeedItemView extends StatelessWidget {
margin: EdgeInsets.only(top: 2, bottom: 5),
child: Row(
children: <Widget>[
Text(
plan.name,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: Color(0xFF282828)),
maxLines: 1,
),
Text(
"好评率 ",
style:
TextStyle(fontSize: 11, color: Color(0xFF282828)),
maxLines: 1,
),
Text(
plan.positiveRate,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: Color(0xFFFF5963)),
maxLines: 1,
),
baseText(widget.plan.name, 14, Color(0xFF282828),
bold: true),
baseText(" 好评率 ", 11, Color(0xFF282828)),
baseText(widget.plan.positiveRate, 14, Color(0xFFFF5963),
bold: true),
],
),
),
Row(
children: <Widget>[
Text(
'¥${plan.minPrice}-${plan.maxPrice}',
style: TextStyle(fontSize: 13, color: Color(0xFFFF5963)),
),
Text(
"销量${plan.salesCount}",
style: TextStyle(fontSize: 11, color: Color(0xFF282828)),
maxLines: 1,
)
baseText('¥${widget.plan.minPrice}-${widget.plan.maxPrice}',
13, Color(0xFFFF5963)),
baseText(" 销量${widget.plan.salesCount}", 11,
Color(0xFF282828))
],
)
],
......@@ -104,58 +99,55 @@ class PlansCompareFeedItemView extends StatelessWidget {
getSecondLevelItem() {
return Container(
width: double.maxFinite,
height: 77,
alignment: Alignment.centerLeft,
margin: EdgeInsets.only(left: 15, right: 15),
child: Row(
children: <Widget>[
Radio(
value: 1,
groupValue: groupValue,
onChanged: (value) {
groupValue = value;
}),
Container(
margin: EdgeInsets.only(left: 15.0),
height: 77,
margin: EdgeInsets.only(right: 15.0),
alignment: Alignment.centerLeft,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 13.0, bottom: 12),
child: Text(
plan.name,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: Color(0xFF282828)),
maxLines: 1,
),
),
Row(
children: <Widget>[
Text(
'¥${plan.minPrice}-${plan.maxPrice}',
style: TextStyle(fontSize: 13, color: Color(0xFFFF5963)),
),
Container(
alignment: Alignment.centerRight,
child: Text(
"指导价:¥${plan.guidePrice}",
style:
TextStyle(fontSize: 11, color: Color(0xFF282828)),
maxLines: 1,
),
),
],
)
],
child: IconButton(
icon: _icon,
onPressed: () => onClickCheckBox(_icon),
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 13.0, bottom: 12),
child: baseText(widget.plan.name, 14, Color(0xFF282828),
bold: true),
),
Row(
children: <Widget>[
baseText('¥${widget.plan.minPrice}-${widget.plan.maxPrice}',
13, Color(0xFFFF5963)),
Container(
width: 160,
alignment: Alignment.centerRight,
child: baseText("指导价:¥${widget.plan.guidePrice}", 11,
Color(0xFF282828))),
],
),
],
)
],
),
);
}
onClickCheckBox(ImageIcon icon) {
if (icon == _checked) {
_icon = _unchecked;
} else {
if (widget._setCompare.length == 2) {
NativeToast.showNativeToast("不许再选了两个够了!");
return;
}
_icon = _checked;
}
setState(() {
widget.compareItem.checkedItem(widget.plan.planId);
});
}
}
......@@ -10,9 +10,9 @@ class PlansCompareFeedModel extends BaseModel {
LiveData<PlansCompareFeed> liveData = LiveData();
RxDispose rxDispose = RxDispose();
void init() {
void init(int _planType, int _planId) {
ClueApiImpl.getInstance()
.getPlansCompareFeed(DioUtil.getInstance().getDio(), 123, 1)
.getPlansCompareFeed(DioUtil.getInstance().getDio(), _planId, _planType)
.listen((event) {
liveData.notifyView(event);
})
......
......@@ -5,38 +5,48 @@
**/
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'package:gm_flutter/ClueModel/page/PlansCompareFeed/PlansCompareFeedModel.dart';
import 'package:gm_flutter/ClueModel/server/entity/PlansCompareFeed.dart';
import 'package:gm_flutter/commonModel/base/BaseComponent.dart';
import 'package:gm_flutter/commonModel/base/BaseState.dart';
import 'CallBackCompareItem.dart';
import 'PlansCompareFeedItemView.dart';
class PlansCompareFeedPage extends StatefulWidget {
int _planType;
int _planId;
PlansCompareFeedPage(this._planType, this._planId);
@override
State<StatefulWidget> createState() => PlansCompareFeedState();
State<StatefulWidget> createState() => _PlansCompareFeedState();
}
class PlansCompareFeedState extends BaseState<PlansCompareFeedPage> {
PlansCompareFeedModel _model = new PlansCompareFeedModel();
class _PlansCompareFeedState extends BaseState<PlansCompareFeedPage>
implements CallBackCompareItem {
PlansCompareFeedModel _model;
Color _compareColor;
Set<int> _setCompare;
@override
void initState() {
super.initState();
_model.init();
_model = new PlansCompareFeedModel();
_model.init(widget._planType, widget._planId);
_compareColor = Color(0xFFCCCCCC);
_setCompare = Set();
}
@override
void dispose() {
_model.dispose();
super.dispose();
_model.dispose();
}
@override
Widget buildItem(BuildContext context) {}
@override
Widget build(BuildContext context) {
Widget buildItem(BuildContext context) {
return Scaffold(
appBar: baseAppBar(
title: "方案对比",
......@@ -50,31 +60,6 @@ class PlansCompareFeedState extends BaseState<PlansCompareFeedPage> {
);
}
getList(List<Plans> plans) {
return Stack(alignment: Alignment.bottomCenter, //指定未定位或部分定位widget的对齐方式
children: <Widget>[
ListView.builder(
itemCount: plans.length,
itemBuilder: (BuildContext context, int position) {
return PlansCompareFeedItemView(plans[position]);
}),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {},
child: Container(
margin:
EdgeInsets.only(left: 15, right: 15, top: 7.5, bottom: 7.5),
width: double.maxFinite,
height: 40,
decoration: BoxDecoration(
color: Color(0xff51CDC7),
borderRadius: BorderRadius.circular(20)),
alignment: Alignment.center,
child: baseText("对比", 14, Colors.white, bold: true),
))
]);
}
getBody() {
return StreamBuilder(
stream: _model.liveData.stream,
......@@ -96,4 +81,51 @@ class PlansCompareFeedState extends BaseState<PlansCompareFeedPage> {
},
);
}
getList(List<Plans> plans) {
return Stack(alignment: Alignment.bottomCenter, children: <Widget>[
ListView.builder(
itemCount: plans.length,
itemBuilder: (BuildContext context, int position) {
return PlansCompareFeedItemView(plans[position], this, _setCompare);
}),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => onClickCompare(),
child: Container(
margin: EdgeInsets.only(left: 15, right: 15, top: 7.5, bottom: 7.5),
width: double.maxFinite,
height: 40,
decoration: BoxDecoration(
color: _compareColor, borderRadius: BorderRadius.circular(20)),
alignment: Alignment.center,
child: baseText("对比", 14, Colors.white, bold: true),
))
]);
}
onClickCompare() {
if (_compareColor == Color(0xFFCCCCCC)) return;
setState(() {
Map<String, dynamic> map = {};
map["ids"] = _setCompare;
FlutterBoost.singleton.open("sample", urlParams: map);
});
}
@override
checkedItem(int id) {
if (_setCompare.contains(id)) {
_setCompare.remove(id);
} else {
_setCompare.add(id);
}
setState(() {
if (_setCompare.length == 2) {
_compareColor = Color(0xff51CDC7);
} else {
_compareColor = Color(0xFFCCCCCC);
}
});
}
}
......@@ -6,52 +6,48 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:gm_flutter/ClueModel/server/entity/ProjectDetailsItem.dart';
import 'package:gm_flutter/commonModel/base/BaseComponent.dart';
class ProjectDetailsItemView extends StatelessWidget {
Groups listData;
List<Widget> listAttrs = [];
ProjectDetailsItemView(this.listData);
ProjectDetailsItemView(this.listData) {
init();
}
@override
Widget build(BuildContext context) {
List<Widget> tiles = [];
init() {
for (var item in listData.attrs) {
tiles.add(getItem(item));
listAttrs.add(getAttrItem(item));
}
var column = Column(
}
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 15.0),
margin: EdgeInsets.only(bottom: 16.0),
alignment: Alignment.centerLeft,
width: double.maxFinite,
height: 55,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/bg_project_detail_item.png"),
fit: BoxFit.cover)),
child: Text(
listData.name,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Color(0xFF000000),
fontSize: 15.0,
),
),
),
padding: EdgeInsets.only(left: 15.0),
margin: EdgeInsets.only(bottom: 16.0),
alignment: Alignment.centerLeft,
height: 55,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/bg_project_detail_item.png"),
fit: BoxFit.cover)),
child: baseText(listData.name, 15, Colors.black, bold: true)),
Column(
children: tiles,
children: listAttrs,
),
SizedBox(
height: 5,
)
],
);
return Container(
margin: EdgeInsets.only(bottom: 5),
child: column,
);
}
getItem(Attrs attrs) {
var row = Container(
getAttrItem(Attrs attrs) {
return Container(
width: double.maxFinite,
margin: EdgeInsets.only(bottom: 25, left: 15, right: 15),
child: Row(
......@@ -60,29 +56,13 @@ class ProjectDetailsItemView extends StatelessWidget {
margin: EdgeInsets.only(right: 25.0),
alignment: Alignment.centerLeft,
width: 78,
child: Text(
attrs.attrName,
style: TextStyle(
color: Color(0xFF999999),
fontSize: 13.0,
),
),
child: baseText(attrs.attrName, 13, Color(0xFF999999)),
),
Container(
alignment: Alignment.centerLeft,
child: Text(
attrs.attrValue,
style: TextStyle(
color: Color(0xFF282828),
fontSize: 14.0,
),
),
),
alignment: Alignment.centerLeft,
child: baseText(attrs.attrValue, 14, Color(0xFF282828))),
],
),
);
return Container(
child: row,
);
}
}
......@@ -16,9 +16,9 @@ class ProjectDetailsModel extends BaseModel {
LiveData<ProjectDetailsItem> liveData = LiveData();
RxDispose rxDispose = RxDispose();
void init() {
void init(int _planId) {
ClueApiImpl.getInstance()
.getProjectDetails(DioUtil.getInstance().getDio(), 123)
.getProjectDetails(DioUtil.getInstance().getDio(), _planId)
.listen((event) {
liveData.notifyView(event);
})
......
......@@ -13,17 +13,22 @@ import 'package:gm_flutter/commonModel/base/BaseState.dart';
import '../../server/entity/ProjectDetailsItem.dart';
class ProjectDetailsPage extends StatefulWidget {
int _planId;
ProjectDetailsPage(this._planId);
@override
State<StatefulWidget> createState() => ProjectDetailsState();
State<StatefulWidget> createState() => _ProjectDetailsState();
}
class ProjectDetailsState extends BaseState<ProjectDetailsPage> {
ProjectDetailsModel _model = new ProjectDetailsModel();
class _ProjectDetailsState extends BaseState<ProjectDetailsPage> {
ProjectDetailsModel _model;
@override
void initState() {
super.initState();
_model.init();
_model = new ProjectDetailsModel();
_model.init(widget._planId);
}
@override
......@@ -33,10 +38,7 @@ class ProjectDetailsState extends BaseState<ProjectDetailsPage> {
}
@override
Widget buildItem(BuildContext context) {}
@override
Widget build(BuildContext context) {
Widget buildItem(BuildContext context) {
return Scaffold(
appBar: baseAppBar(
title: "项目说明",
......@@ -60,12 +62,24 @@ class ProjectDetailsState extends BaseState<ProjectDetailsPage> {
}
ProjectDetailsItem item = data.data;
if (item == null || item.data == null || item.data.groups == null) {
return emptyItem(MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height);
return emptyItem(MediaQuery
.of(context)
.size
.width,
MediaQuery
.of(context)
.size
.height);
}
if (item.error != 0) {
return errorItem(MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height, () {});
return errorItem(MediaQuery
.of(context)
.size
.width,
MediaQuery
.of(context)
.size
.height, () {});
}
return ListView.builder(
itemCount: item.data.groups.length,
......
......@@ -23,9 +23,32 @@ class LevelOneItem extends StatelessWidget {
return PlanItem(context);
} else if (cards.cardType == "hospital") {
return HospitalItem(context);
} else if (cards.cardType == "doctor") {
return DoctorItem(context);
} else if (cards.cardType == "diary") {
return DiaryItem(context);
}
}
static double getCardTypeHeight(Cards cards) {
if (cards.cardType == "plan") {
return 118;
} else if (cards.cardType == "hospital") {
return 149;
} else if (cards.cardType == "doctor") {
return 127;
} else if (cards.cardType == "diary") {
double image = (cards.diary.images == null ||
cards.diary.images.isEmpty ||
cards.diary.images.length < 2)
? 0
: 175;
double text = 22 * (cards.diary.content.length / 22) + 24.5;
return 40 + image + text + 55;
}
return 0;
}
Widget DoctorItem(BuildContext context) {
if (cards.doctor == null && isDebug) {
throw new Exception();
......@@ -85,7 +108,7 @@ class LevelOneItem extends StatelessWidget {
),
Positioned(
bottom: 15,
right: 187,
right: 137,
child: askWidget(),
),
Positioned(
......@@ -93,6 +116,15 @@ class LevelOneItem extends StatelessWidget {
bottom: 25,
child: compareWidget(),
),
Positioned(
left: 0,
bottom: 0,
child: Container(
width: MediaQuery.of(context).size.width - 30,
height: 0.5,
color: Color(0xffE5E5E5),
),
)
],
),
),
......@@ -151,7 +183,7 @@ class LevelOneItem extends StatelessWidget {
),
Positioned(
bottom: 15,
right: 187,
right: 137,
child: askWidget(),
),
Positioned(
......@@ -168,6 +200,15 @@ class LevelOneItem extends StatelessWidget {
left: 57,
top: 43.5,
child: FiveStarView(int.parse(cards.hospital.star), 5),
),
Positioned(
left: 0,
bottom: 0,
child: Container(
width: MediaQuery.of(context).size.width - 30,
height: 0.5,
color: Color(0xffE5E5E5),
),
)
],
),
......@@ -208,7 +249,7 @@ class LevelOneItem extends StatelessWidget {
),
Positioned(bottom: 15, right: 0, child: lowPriceWidget()),
Positioned(
right: 152,
right: 137,
bottom: 15,
child: askWidget(),
),
......@@ -221,7 +262,7 @@ class LevelOneItem extends StatelessWidget {
Color(0xff666666)),
),
Positioned(
left: 15,
left: 0,
bottom: 0,
child: Container(
width: MediaQuery.of(context).size.width - 30,
......@@ -283,4 +324,182 @@ class LevelOneItem extends StatelessWidget {
],
);
}
Widget DiaryItem(BuildContext context) {
if (cards.diary == null && isDebug) {
throw new Exception();
}
return Container(
margin: EdgeInsets.only(left: 20, right: 20),
width: double.maxFinite,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 15),
height: 25,
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
width: 25,
height: 25,
child: ClipOval(
child: CachedNetworkImage(
imageUrl: cards.diary.user.portrait,
fit: BoxFit.cover,
),
),
),
Container(
margin: EdgeInsets.only(left: 8),
child: baseText(
cards.diary.user.userName, 13, Color(0xff999999)),
),
cards.diary.userLevel != null &&
cards.diary.userLevel.levelIcon != null
? Container(
width: 31,
height: 12,
child: CachedNetworkImage(
imageUrl: cards.diary.userLevel.levelIcon,
),
)
: Container(
width: 0,
)
],
),
),
(cards.diary.images == null ||
cards.diary.images.isEmpty ||
cards.diary.images.length < 2)
? Container(
height: 0,
)
: Container(
margin: EdgeInsets.only(top: 12),
height: 163,
child: Row(
children: <Widget>[
Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
cards.diary.images[0].imageHalf))),
alignment: Alignment.bottomLeft,
child: Container(
width: 65.5,
height: 18,
decoration: BoxDecoration(
color: Color(0x4c000000),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(4),
topRight: Radius.circular(2),
)),
alignment: Alignment.center,
child: baseText(
cards.diary.images[0].desc, 11, Colors.white),
),
),
),
Container(
width: 9,
),
Expanded(
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
cards.diary.images[1].imageHalf))),
alignment: Alignment.bottomLeft,
child: Container(
width: 65.5,
height: 18,
decoration: BoxDecoration(
color: Color(0x4c000000),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(4),
topRight: Radius.circular(2),
)),
alignment: Alignment.center,
child: baseText(
cards.diary.images[1].desc, 11, Colors.white),
),
),
)
],
)),
Container(
margin: EdgeInsets.only(top: 12.5, bottom: 12),
child: Text(
cards.diary.content,
textScaleFactor: 1.0,
maxLines: 5,
strutStyle: StrutStyle(forceStrutHeight: true, height: 2),
overflow: TextOverflow.ellipsis,
style: TextStyle(
decoration: TextDecoration.none,
fontSize: 15,
color: Color(0xff464646),
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w400),
),
),
baseText("项目${cards.diary.title}", 13, Color(0xff999999)),
Container(
margin: EdgeInsets.only(top: 12, bottom: 16),
height: 14,
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
width: 14,
height: 14,
child: Image.asset("assets/eye.png"),
),
Container(
margin: EdgeInsets.only(left: 5, right: 20),
child:
baseText("${cards.diary.viewNum}", 12, Color(0xff999999)),
),
Container(
width: 14,
height: 14,
child: Image.asset("assets/arguement.png"),
),
Container(
margin: EdgeInsets.only(left: 5, right: 20),
child: baseText(
"${cards.diary.replyNum}", 12, Color(0xff999999)),
),
Container(
width: 14,
height: 14,
child: Image.asset("assets/heart.png"),
),
Container(
margin: EdgeInsets.only(left: 5, right: 20),
child:
baseText("${cards.diary.voteNum}", 12, Color(0xff999999)),
)
],
),
),
Container(
width: double.maxFinite,
height: 0.5,
color: Color(0xffE5E5E5),
)
],
),
);
}
}
/*
* @author lsy
* @date 2020/7/4
**/
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:gm_flutter/ClueModel/page/levelOne/LevelOneListModel.dart';
import 'package:gm_flutter/ClueModel/server/entity/LevelOneFeedList.dart';
import 'package:gm_flutter/commonModel/base/BaseComponent.dart';
import 'package:gm_flutter/commonModel/bean/Pair.dart';
import 'package:gm_flutter/commonModel/eventbus/SimpleEventBus.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'LevelOneItem.dart';
class LevelOneList extends StatefulWidget {
double topHeight;
int planId;
String tabName;
LevelOneList(this.planId, this.tabName, this.topHeight);
@override
State<StatefulWidget> createState() => LevelOneListState();
}
class LevelOneListState extends State<LevelOneList>
with AutomaticKeepAliveClientMixin{
LevelOneListModel _model = LevelOneListModel();
RefreshController refreshController = RefreshController();
Function(String str) refresh;
@override
void initState() {
super.initState();
_model.plan_id = widget.planId;
_model.tab_type = widget.tabName;
_model.refreshView(true);
refresh = (str) {
_model.stateLive.notifyView(LOADING);
_model.refreshView(true);
};
SimpleEventBus.instance().resignEvent("LevelOneList", refresh);
}
@override
void dispose() {
refreshController.dispose();
_model.dispose();
SimpleEventBus.instance().unResignEvent("LevelOneList", refresh);
super.dispose();
}
@override
Widget build(BuildContext context) {
super.build(context);
return baseStateView(MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height, _model.stateLive, pages(), () {
_model.refreshView(true);
}, paddingTop: widget.topHeight);
}
Widget pages() {
return baseRefreshView(refreshController, () {}, null, null,
customScrollView: CustomScrollView(
// physics: NeverScrollableScrollPhysics(),
physics: ClampingScrollPhysics(),
// shrinkWrap: true,
slivers: <Widget>[
// SliverOverlapInjector(
// handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
// ),
StreamBuilder<List<Cards>>(
stream: _model.cardsLive.stream,
initialData: _model.cardsLive.data ?? [],
builder: (c, data) {
if (data.data.isEmpty && _model.page > 1) {
refreshController.loadNoData();
} else {
refreshController.loadComplete();
}
return SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
if(index==_model.data.length){
return Container(
height: 40,
color: Color(0xffF7F6FA),
);
}
return LevelOneItem(_model.data[index]);
},
childCount: _model.data.length+1,
),
);
},
),
StreamBuilder<List<Cards>>(
stream: _model.cardsLive.stream,
initialData: _model.data ?? [],
builder: (c, data) {
double totalHeight = 0;
_model.data.forEach((element) {
totalHeight += LevelOneItem.getCardTypeHeight(element);
});
double height = MediaQuery.of(context).size.height -
40 -
widget.topHeight -
totalHeight;
return SliverToBoxAdapter(
child: Container(
height: height < 0 ? 0 : height,
),
);
},
),
],
), onLoading: () {
_model.loadMore();
}, pullDown: false, pullUp: true);
}
@override
bool get wantKeepAlive => true;
}
/*
* @author lsy
* @date 2020/7/4
**/
import 'package:flutter_common/commonModel/live/BaseModel.dart';
import 'package:flutter_common/commonModel/live/LiveData.dart';
import 'package:flutter_common/commonModel/toast/NativeToast.dart';
import 'package:gm_flutter/ClueModel/server/api/ClueApi.serv.dart';
import 'package:gm_flutter/ClueModel/server/entity/LevelOneFeedList.dart';
import 'package:gm_flutter/commonModel/bean/Pair.dart';
import 'package:gm_flutter/commonModel/net/DioUtil.dart';
import 'package:gm_flutter/commonModel/rx/RxDispose.dart';
import 'package:gm_flutter/commonModel/util/PrintUtil.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
class LevelOneListModel extends BaseModel {
int plan_id;
String tab_type;
int page = 1;
LiveData<int> stateLive = LiveData();
LiveData<List<Cards>> cardsLive = LiveData();
List<Cards> data = [];
RxDispose rxDispose = RxDispose();
void refreshView(bool clear, {RefreshController refreshListener}) {
if (clear) {
data.clear();
page = 1;
}
ClueApiImpl.getInstance()
.getLevelOneList(
DioUtil.getInstance().getDio(), plan_id, tab_type, page)
.listen((event) {
if (event.error == 0) {
if ((event.data.cards == null || event.data.cards.isEmpty) &&
page == 1) {
stateLive.notifyView(EMPTY);
} else {
data.addAll(event.data.cards);
cardsLive.notifyView(data);
stateLive.notifyView(ENDLOADING);
}
} else {
NativeToast.showNativeToast(event.message);
stateLive.notifyView(FAIL);
}
})
.addToDispose(rxDispose)
.onError((err) {
PrintUtil.printBug(err);
stateLive.notifyView(FAIL);
});
}
@override
void dispose() {
rxDispose.dispose();
stateLive.dispost();
cardsLive.dispost();
}
void loadMore() {
page++;
refreshView(false);
}
}
......@@ -5,8 +5,10 @@
import 'package:flutter/material.dart';
import 'package:flutter_common/commonModel/live/BaseModel.dart';
import 'package:flutter_common/commonModel/live/LiveData.dart';
import 'package:flutter_common/commonModel/toast/NativeToast.dart';
import 'package:gm_flutter/ClueModel/server/api/ClueApi.serv.dart';
import 'package:gm_flutter/ClueModel/server/entity/LevelOneFeedList.dart';
import 'package:gm_flutter/ClueModel/server/entity/PlanOverViewBean.dart';
import 'package:gm_flutter/commonModel/GMBase.dart';
import 'package:gm_flutter/commonModel/bean/Pair.dart';
import 'package:gm_flutter/commonModel/rx/RxDispose.dart';
......@@ -14,92 +16,43 @@ import 'package:gm_flutter/commonModel/util/PrintUtil.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
class LevelOneModel extends BaseModel {
LiveData<double> appBarLive = LiveData();
LiveData<List<String>> rectLive = LiveData();
LiveData<List<String>> explainLive = LiveData();
LiveData<bool> showTab = LiveData();
LiveData<int> topIndexLive = new LiveData();
LiveData<double> topScrollLive = new LiveData();
LiveData<bool> loadingLive = LiveData();
RxDispose rxDispose = RxDispose();
LiveData<Pair<int, List<Cards>>> cardsLive = LiveData();
LiveData<int> stateLive = LiveData();
LiveData<double> textLive = LiveData();
Map<int, List<Cards>> data = new Map();
List list = ["plan", "hospital", "doctor", "diary"];
List pageList = [1, 1, 1, 1];
List pageHeightList = [118, 149, 118, 118];
RxDispose rxDispose = RxDispose();
void refreshView(bool clear, {RefreshController refreshListener}) {
Future.delayed(Duration(seconds: 1), () {
Cards cards = Cards(
cardType: "plan",
plan: Plan(planName: "ww", minPrice: "50", maxPrice: "500"));
List<Cards> a = [
cards,
cards,
cards,
cards,
cards,
cards,
cards,
cards,
cards,
cards,
cards,
cards,
];
List<Cards> b = [cards];
print("INDEX ${currentIndex}");
if (currentIndex == 1) {
data[currentIndex] = b;
} else {
data[currentIndex] = a;
}
List<Tabs> tabsList = [];
int plan_id = 0;
PlanOverData planoverItem;
cardsLive.notifyView(Pair(ENDLOADING, data));
});
int index = currentIndex;
if (clear) {
data.clear();
pageList = [1, 1, 1, 1];
}
void init(VoidCallback callback) {
ClueApiImpl.getInstance()
.getLevelOneList(
DioUtil.getInstance().getDio(), 123, list[index], pageList[index])
.listen((event) {})
.getPlanOverView(DioUtil.getInstance().getDio(), plan_id)
.listen((event) {
if (event.error == 0) {
planoverItem = event.data;
tabsList = event.data.tabs;
stateLive.notifyView(ENDLOADING);
callback();
} else {
NativeToast.showNativeToast(event.message);
stateLive.notifyView(FAIL);
}
})
.addToDispose(rxDispose)
.onError((err) {
PrintUtil.printBug(err);
});
stateLive.notifyView(FAIL);
});
}
int currentIndex = 0;
@override
void dispose() {
showTab.dispost();
cardsLive.dispost();
appBarLive.dispost();
rectLive.dispost();
topIndexLive.dispost();
topScrollLive.dispost();
loadingLive.dispost();
}
void selectPage(int index) {
if (currentIndex == index) {
return;
}
currentIndex = index;
if (data[currentIndex] == null) {
cardsLive.notifyView(Pair(LOADING, null));
refreshView(true);
} else {
cardsLive.notifyView(Pair(ENDLOADING, data[currentIndex]));
}
stateLive.dispost();
textLive.dispost();
}
void loadMore() {
}
void selectTab(int index) {}
}
......@@ -2,27 +2,27 @@
* @author lsy
* @date 2020/6/29
**/
import 'dart:async';
import 'dart:math';
import 'dart:ui';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'
as extend;
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'package:flutter_common/commonModel/live/LiveData.dart';
import 'package:gm_flutter/ClueModel/page/levelOne/LevelOneItem.dart';
import 'package:flutter/services.dart';
import 'package:gm_flutter/ClueModel/page/levelOne/LevelOneList.dart';
import 'package:gm_flutter/ClueModel/page/levelOne/LevelOneModel.dart';
import 'package:gm_flutter/ClueModel/server/entity/LevelOneFeedList.dart';
import 'package:gm_flutter/ClueModel/util/PosUtil.dart';
import 'package:gm_flutter/ClueModel/view/FiveStarView.dart';
import 'package:gm_flutter/ClueModel/page/levelTwo/LevelTwoPage.dart';
import 'package:gm_flutter/commonModel/base/BaseComponent.dart';
import 'package:gm_flutter/commonModel/base/BaseState.dart';
import 'package:gm_flutter/commonModel/bean/Pair.dart';
import 'package:gm_flutter/commonModel/base/BaseUtil.dart';
import 'package:gm_flutter/commonModel/eventbus/SimpleEventBus.dart';
import 'package:gm_flutter/commonModel/util/DartUtil.dart';
import 'package:gm_flutter/commonModel/view/baseRefreshIndicator.dart';
import 'package:gm_flutter/main.mark.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'LevelOneBar.dart';
class LevelOnePage extends StatefulWidget {
@override
State<StatefulWidget> createState() => LevelOneState();
......@@ -30,271 +30,192 @@ class LevelOnePage extends StatefulWidget {
class LevelOneState extends BaseState<LevelOnePage>
with TickerProviderStateMixin {
int planId = 10;
LevelOneModel _model = new LevelOneModel();
RefreshController _refreshController = RefreshController();
PageController pageController = new PageController();
GlobalKey keyTop = new GlobalKey();
GlobalKey key1 = new GlobalKey();
double screenWidth;
List<Widget> oneList = new List();
int lastPageIndex = 0;
TabController tabController;
int index = 0;
@override
void initState() {
super.initState();
// Future.delayed(Duration(seconds: 3), () {
// _model.loadingLive.notifyView(false);
// _model.refreshView(true);
// });
pageController.addListener(() {
if (screenWidth != null) {
_model.topScrollLive.notifyView(
pageController.offset / screenWidth * (screenWidth / 4));
}
_model.init(() {
_initTabBar();
});
oneList.add(head());
oneList.add(good());
oneList.add(rect());
oneList.add(explain());
oneList.add(Container(
height: 5,
color: Color(0xffF7F6FA),
));
oneList.add(feed());
oneList.add(pages());
}
void _onScroll(double offset) {
if (offset < 10) {
_model.appBarLive.notifyView(0.0);
} else if (offset < 500) {
var topPos = PosUtil.findPos(keyTop);
if (topPos != null && topPos.dy < 0) {
double dy = -topPos.dy;
if (dy < 20) {
dy = 0;
}
double alpha = dy / 112;
if (alpha < 0) {
alpha = 0;
} else if (alpha > 1) {
alpha = 1;
}
_model.appBarLive.notifyView(alpha);
}
}
if (offset > 100) {
var key1Pos = PosUtil.findPos(key1);
if (key1Pos != null) {
if (key1Pos.dy < 86) {
_model.showTab.notifyView(true);
} else {
_model.showTab.notifyView(false);
}
}
void _initTabBar() {
if (tabController != null) {
tabController.removeListener(tabControlerListener);
}
tabController = TabController(
initialIndex: index, length: _model.tabsList.length, vsync: this);
tabController.addListener(tabControlerListener);
setState(() {});
}
@override
void dispose() {
tabController?.removeListener(tabControlerListener);
tabController?.dispose();
_model.dispose();
pageController.dispose();
_refreshController.dispose();
super.dispose();
}
void tabControlerListener() {
if (index != tabController.index) {
index = tabController.index;
}
}
@override
Widget buildItem(BuildContext context) {
oneList.clear();
oneList.add(good());
oneList.add(rect());
oneList.add(explain());
oneList.add(Container(
height: 5,
color: Color(0xffF7F6FA),
));
screenWidth = MediaQuery.of(context).size.width;
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
top: false,
child: StreamBuilder<bool>(
stream: _model.loadingLive.stream,
initialData: true,
builder: (c, data) {
if (data.data) {
return loadingItem();
} else {
return home();
}
},
)));
child: baseStateView(
MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height,
_model.stateLive,
Container(
child: BaseNestedScrollViewRefreshIndicator(
onRefresh: () async {
Completer completer = new Completer();
_model.init(() {
_initTabBar();
SimpleEventBus.instance()
.notifyListener("LevelOneList", "");
completer.complete();
});
return completer.future;
},
child: newHomeWarp()),
), () {
_model.init(() {
_initTabBar();
});
})));
}
Widget home() {
return Column(
Widget newHomeWarp() {
return Stack(
children: <Widget>[
Expanded(
child: Stack(
children: <Widget>[
MediaQuery.removePadding(
removeTop: true,
context: context,
child: NotificationListener(
onNotification: (scrollNotification) {
if (scrollNotification is ScrollUpdateNotification &&
scrollNotification.metrics.axisDirection.index == 2) {
_onScroll(scrollNotification.metrics.pixels);
}
return false;
},
child: baseRefreshView(
_refreshController,
() {
//TODO
// _refreshController.refreshCompleted();
},
null,
null,
customScrollView: CustomScrollView(
slivers: <Widget>[
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return oneList[index];
},
childCount: oneList.length,
),
),
],
),
pullUp: true,
onLoading: () {
_model.loadMore();
},
),
)),
StreamBuilder<double>(
stream: _model.appBarLive.stream,
initialData: _model.appBarLive.data ?? 0.0,
builder: (c, data) {
return Opacity(
opacity: data.data,
child: Container(
height: 86,
decoration: BoxDecoration(color: Colors.white),
child: Center(
child: Padding(
padding: EdgeInsets.only(top: 20),
child: Text('文案'),
),
),
),
);
},
),
Positioned(
top: 86,
child: StreamBuilder<bool>(
stream: _model.showTab.stream,
initialData: _model.showTab.data ?? false,
builder: (c, data) {
return Visibility(
visible: data.data,
child: Container(
width: MediaQuery.of(context).size.width,
color: Colors.white,
child: MessageBarView(
topIndexLive: _model.topIndexLive,
topScrollLive: _model.topScrollLive,
pageController: pageController,
),
));
},
),
),
Positioned(
top: 49,
left: 15,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
//TODO
print("TAP");
Navigator.pop(context);
},
child: Container(
width: 30,
height: 30,
decoration: BoxDecoration(
color: Color(0x99FFFFFF), shape: BoxShape.circle),
alignment: Alignment.center,
child: Container(
child: Image.asset("assets/left_arrow.png"),
),
)),
)
],
),
),
Container(
height: 0.5,
width: double.maxFinite,
color: Color(0xffE5E5E5),
),
Container(
width: double.maxFinite,
height: 55,
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 18),
width: 30,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
width: 22,
height: 22,
child: Image.asset("assets/vs_black.png"),
),
Container(
margin: EdgeInsets.only(top: 3),
child: baseText("去比较", 10, Color(0xff282828)),
)
],
),
),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {},
child: Container(
margin: EdgeInsets.only(left: 15),
width: 135,
height: 40,
decoration: BoxDecoration(
color: Color(0xff51CDC7),
borderRadius: BorderRadius.circular(20)),
alignment: Alignment.center,
child: baseText("咨询", 14, Colors.white, bold: true),
)),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {},
child: Container(
margin: EdgeInsets.only(left: 15),
width: 135,
height: 40,
decoration: BoxDecoration(
color: Color(0xffF96079),
borderRadius: BorderRadius.circular(20)),
alignment: Alignment.center,
child: baseText("获取底价", 14, Colors.white, bold: true),
)),
Expanded(
child: Container(),
)
],
),
)
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: NotificationListener(
onNotification: (scrollNotification) {
if (scrollNotification is KeepAliveNotification ||
scrollNotification is OverscrollIndicatorNotification) {
return false;
}
if (scrollNotification is ScrollUpdateNotification &&
scrollNotification.metrics.axisDirection.index == 2) {}
if (scrollNotification.depth == 0) {
if (scrollNotification.metrics.pixels > 80) {
_model.textLive.notifyView(
(scrollNotification.metrics.pixels - 80) / 40 > 1.0
? 1.0
: (scrollNotification.metrics.pixels - 80) / 40);
} else {
_model.textLive.notifyView(0.0);
}
} else if (scrollNotification.depth == 2) {
if (scrollNotification.metrics.pixels < 50 &&
scrollNotification.metrics.pixels > 0) {
_model.textLive.notifyView(1.0);
}
}
return false;
},
child: newHome(),
)),
baseSliverTitle(
"title", MediaQuery.of(context).size.width, _model.textLive),
baseSliverBack(() {
Navigator.of(context).pop();
}),
],
);
}
Widget newHome() {
List<Widget> list = [];
var d = MediaQueryData.fromWindow(window).padding.top;
for (int i = 0; i < _model.tabsList.length; i++) {
list.add(extend.NestedScrollViewInnerScrollPositionKeyWidget(
Key("Tab${i}"),
LevelOneList(
planId, _model.tabsList[i].tabType, kToolbarHeight + d)));
}
final double statusBarHeight = MediaQuery.of(context).padding.top;
final double pinnedHeaderHeight = statusBarHeight + kToolbarHeight;
return extend.NestedScrollView(
innerScrollPositionKeyBuilder: () {
String index = 'Tab${tabController.index.toString()}';
return Key(index);
},
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverOverlapAbsorber(
handle: extend.NestedScrollView.sliverOverlapAbsorberHandleFor(
context),
sliver: baseSliverAppBar(
// _model.imageUrl
"https://pic.igengmei.com/2018/09/11/1513/b7e825a4e4c1-w")),
SliverList(
delegate: SliverChildBuilderDelegate((BuildContext c, int i) {
if (i == 0) {
return Container(
height: pinnedHeaderHeight,
);
}
return oneList[i - 1];
}, childCount: oneList.length + 1)),
SliverPersistentHeader(
pinned: true,
delegate: StickyTabBarDelegate(
child: Container(
height: 40,
color: Colors.white,
child: baseTabBar(tabController, getTabs(), (index) {
_model.selectTab(index);
}, scroll: false),
)),
),
];
},
body: TabBarView(controller: tabController, children: list));
}
List<Widget> getTabs() {
List<Widget> list = [];
for (int i = 0; i < _model.tabsList.length; i++) {
list.add(baseTabBarItem(_model.tabsList[i].name,
leftPadding: i == 0 ? 24 : 28,
rightPadding: i == _model.tabsList.length - 1 ? 24 : 28));
}
return list;
}
Widget head() {
return Container(
key: keyTop,
......@@ -311,8 +232,13 @@ class LevelOneState extends BaseState<LevelOnePage>
}
Widget good() {
if (_model.planoverItem == null) {
return Container(
height: 54,
);
}
return Container(
width: double.maxFinite,
width: MediaQuery.of(context).size.width,
height: 54,
margin: EdgeInsets.only(top: 18, bottom: 12),
child: Stack(
......@@ -320,12 +246,14 @@ class LevelOneState extends BaseState<LevelOnePage>
Positioned(
top: 6,
left: 15,
child: baseText("TODO", 18, Color(0xff282828), bold: true),
child: baseText(_model.planoverItem.name, 18, Color(0xff282828),
bold: true),
),
Positioned(
bottom: 8,
left: 15,
child: baseText("TODO", 12, Color(0xff999999)),
child: baseText(
_model.planoverItem.planDescription, 12, Color(0xff999999)),
),
Positioned(
right: 0,
......@@ -350,16 +278,18 @@ class LevelOneState extends BaseState<LevelOnePage>
baseText("好评率", 11, Color(0xff282828)),
Container(
margin: EdgeInsets.only(left: 4),
child: baseText("99", 20, Color(0xffFF5963)),
child: baseText("${_model.planoverItem.positiveRate}", 20,
Color(0xffFF5963)),
),
baseText("%", 11, Color(0xffFF5963)),
// baseText("%", 11, Color(0xffFF5963)),
],
),
),
Positioned(
right: 15,
bottom: 8,
child: baseText("销量110", 11, Color(0xff666666)),
child: baseText(
"销量${_model.planoverItem.salesCount}", 11, Color(0xff666666)),
)
],
),
......@@ -367,168 +297,256 @@ class LevelOneState extends BaseState<LevelOnePage>
}
Widget rect() {
return StreamBuilder<List<String>>(
stream: _model.rectLive.stream,
initialData: ["w", "w", "q", "w"],
builder: (c, data) {
List<Widget> list = List();
for (int i = 0; i < data.data.length; i += 2) {
list.add(Expanded(
child: Container(
alignment: Alignment.center,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
baseText(data.data[i], 14, Color(0xff282828), bold: true),
baseText(data.data[i + 1], 11, Color(0xff999999)),
],
),
),
));
if (i < data.data.length - 2) {
list.add(Container(
width: 0.5,
height: 18,
color: Color(0xFFE5E5E5),
));
}
}
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: Color(0xffF9F8FB),
),
width: double.maxFinite,
height: 62,
margin: EdgeInsets.only(left: 15, right: 15, bottom: 20),
child: Row(
if (_model.planoverItem == null) {
return Container(
height: 62,
);
}
List<Widget> list = List();
int i = 0;
_model.planoverItem.overviewAttrs.forEach((element) {
list.add(Expanded(
child: Container(
alignment: Alignment.center,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: list,
children: <Widget>[
baseText(element.attrName, 14, Color(0xff282828), bold: true),
baseText(element.attrValue, 11, Color(0xff999999)),
],
),
);
},
),
));
if (i != _model.planoverItem?.overviewAttrs.length - 1) {
list.add(Container(
width: 0.5,
height: 18,
color: Color(0xFFE5E5E5),
));
}
i++;
});
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: Color(0xffF9F8FB),
),
width: double.maxFinite,
height: 62,
margin: EdgeInsets.only(left: 15, right: 15, bottom: 20),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: list,
),
);
}
Widget explain() {
return StreamBuilder<List<String>>(
stream: _model.explainLive.stream,
initialData: ["www", "www", "www", "??"],
builder: (c, data) {
List<Widget> list = [];
List<Widget> list = [];
list.add(Container(
height: 31,
child: Row(
children: <Widget>[
baseText("项目说明", 15, Color(0xff282828)),
Expanded(
child: Container(),
),
GestureDetector(
onTap: () {
//TODO
Map<String, dynamic> map = {};
map["page_name"] = "plan_home";
map["referrer_link"] = [];
RouterCenterImpl()
.findMainRouter()
.buriedEvent("on_click_navbar_search", map);
},
behavior: HitTestBehavior.opaque,
child: baseText("了解更多", 12, Color(0xff3FB5AF)),
)
],
),
));
if (_model.planoverItem != null) {
_model.planoverItem.explanationAttrs.forEach((element) {
list.add(Container(
height: 31,
height: 28,
child: Row(
children: <Widget>[
baseText("项目说明", 15, Color(0xff282828)),
Expanded(
child: Container(),
),
GestureDetector(
onTap: () {
//TODO
},
behavior: HitTestBehavior.opaque,
child: baseText("了解更多", 12, Color(0xff3FB5AF)),
baseText(element.attrName, 13, Color(0xff999999)),
Container(
margin: EdgeInsets.only(left: 12),
child: baseText(element.attrValue, 13, Color(0xff666666)),
)
],
),
));
for (int i = 0; i < data.data.length; i += 2) {
list.add(Container(
height: 28,
child: Row(
children: <Widget>[
baseText(data.data[i], 13, Color(0xff999999)),
Container(
margin: EdgeInsets.only(left: 12),
child: baseText(data.data[i + 1], 13, Color(0xff666666)),
)
],
),
));
}
return Container(
margin: EdgeInsets.only(left: 15, right: 15, bottom: 20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: list,
),
);
},
);
}
Widget feed() {
});
}
return Container(
key: key1,
child: MessageBarView(
topIndexLive: _model.topIndexLive,
topScrollLive: _model.topScrollLive,
pageController: pageController,
));
}
Widget pages() {
return StreamBuilder<Pair<int, List<Cards>>>(
stream: _model.cardsLive.stream,
initialData: _model.cardsLive.data ?? Pair(LOADING, null),
builder: (c, data) {
double height = MediaQuery.of(context).size.height - 86 - 55.5 - 49;
if (_model.data[_model.currentIndex] != null &&
_model.data[_model.currentIndex].length > 0) {
height = max(
_model.data[_model.currentIndex].length *
_model.pageHeightList[_model.currentIndex] *
1.0,
height);
}
return Container(
width: double.maxFinite,
height: height,
child: PageView.builder(
itemBuilder: (c, pageIndex) {
if (data.data.first == FAIL) {
return errorItem(MediaQuery.of(context).size.width, height, () {
_model.refreshView(true);
});
}
if (data.data.first == LOADING ||
_model.data[pageIndex] == null) {
return loadingItem();
}
if (data.data.second.length == 0) {
if (_model.pageList[pageIndex] == 1) {
return emptyItem(MediaQuery.of(context).size.width, height);
} else {
_refreshController.loadNoData();
}
} else {
_refreshController.loadComplete();
}
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
itemBuilder: (c, index) {
return LevelOneItem(_model.data[pageIndex][index]);
},
itemCount: _model.data[pageIndex].length,
);
},
allowImplicitScrolling: false,
dragStartBehavior: DragStartBehavior.down,
controller: pageController,
itemCount: 4,
onPageChanged: (index) {
setState(() {
_refreshController.resetNoData();
});
_model.topIndexLive.notifyView(index);
_model.selectPage(index);
},
),
);
},
margin: EdgeInsets.only(left: 15, right: 15, bottom: 20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: list,
),
);
}
// Widget home() {
// return Column(
// children: <Widget>[
// Expanded(
// child: Stack(
// children: <Widget>[
// MediaQuery.removePadding(
// removeTop: true,
// context: context,
// child: NotificationListener(
// onNotification: (scrollNotification) {
// if (scrollNotification is ScrollUpdateNotification &&
// scrollNotification.metrics.axisDirection.index == 2) {
// _onScroll(scrollNotification.metrics.pixels);
// }
// return false;
// },
// child: baseRefreshView(
// _refreshController,
// () {
// //TODO
//// _refreshController.refreshCompleted();
// },
// null,
// null,
// customScrollView: CustomScrollView(
// slivers: <Widget>[
// SliverList(
// delegate: SliverChildBuilderDelegate(
// (BuildContext context, int index) {
// return oneList[index];
// },
// childCount: oneList.length,
// ),
// ),
// ],
// ),
// pullUp: true,
// onLoading: () {
// _model.loadMore();
// },
// ),
// )),
// StreamBuilder<double>(
// stream: _model.appBarLive.stream,
// initialData: _model.appBarLive.data ?? 0.0,
// builder: (c, data) {
// return Opacity(
// opacity: data.data,
// child: Container(
// height: 86,
// decoration: BoxDecoration(color: Colors.white),
// child: Center(
// child: Padding(
// padding: EdgeInsets.only(top: 20),
// child: Text('文案'),
// ),
// ),
// ),
// );
// },
// ),
// baseSliverTitle("title", MediaQuery.of(context).size.width,
// _model.appBarLive),
// Positioned(
// top: 86,
// child: StreamBuilder<bool>(
// stream: _model.showTab.stream,
// initialData: _model.showTab.data ?? false,
// builder: (c, data) {
// return Visibility(
// visible: data.data,
// child: Container(
// width: MediaQuery.of(context).size.width,
// color: Colors.white,
// child: MessageBarView(
// topIndexLive: _model.topIndexLive,
// topScrollLive: _model.topScrollLive,
// pageController: pageController,
// ),
// ));
// },
// ),
// ),
// baseSliverBack(() {
// Navigator.of(context).pop();
// })
// ],
// ),
// ),
// Container(
// height: 0.5,
// width: double.maxFinite,
// color: Color(0xffE5E5E5),
// ),
// Container(
// width: double.maxFinite,
// height: 55,
// child: Row(
// mainAxisSize: MainAxisSize.min,
// crossAxisAlignment: CrossAxisAlignment.center,
// children: <Widget>[
// Container(
// margin: EdgeInsets.only(left: 18),
// width: 30,
// child: Column(
// mainAxisSize: MainAxisSize.min,
// children: <Widget>[
// Container(
// width: 22,
// height: 22,
// child: Image.asset("assets/vs_black.png"),
// ),
// Container(
// margin: EdgeInsets.only(top: 3),
// child: baseText("去比较", 10, Color(0xff282828)),
// )
// ],
// ),
// ),
// GestureDetector(
// behavior: HitTestBehavior.opaque,
// onTap: () {},
// child: Container(
// margin: EdgeInsets.only(left: 15),
// width: 135,
// height: 40,
// decoration: BoxDecoration(
// color: Color(0xff51CDC7),
// borderRadius: BorderRadius.circular(20)),
// alignment: Alignment.center,
// child: baseText("咨询", 14, Colors.white, bold: true),
// )),
// GestureDetector(
// behavior: HitTestBehavior.opaque,
// onTap: () {},
// child: Container(
// margin: EdgeInsets.only(left: 15),
// width: 135,
// height: 40,
// decoration: BoxDecoration(
// color: Color(0xffF96079),
// borderRadius: BorderRadius.circular(20)),
// alignment: Alignment.center,
// child: baseText("获取底价", 14, Colors.white, bold: true),
// )),
// Expanded(
// child: Container(),
// )
// ],
// ),
// )
// ],
// );
// }
}
/*
* @author lsy
* @date 2020/7/2
**/
import 'package:flutter/material.dart';
import 'package:gm_flutter/ClueModel/page/plan/PlanItem.dart';
import 'package:gm_flutter/ClueModel/server/entity/PlanFeedBean.dart';
import 'package:gm_flutter/commonModel/base/BaseComponent.dart';
import 'package:gm_flutter/commonModel/bean/Pair.dart';
import 'package:gm_flutter/commonModel/eventbus/SimpleEventBus.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'TopListModel.dart';
class TopList extends StatefulWidget {
final int id;
double topHeight;
TopList(this.id, this.topHeight);
@override
State<StatefulWidget> createState() => TopListState();
}
class TopListState extends State<TopList>
with AutomaticKeepAliveClientMixin{
RefreshController refreshController = RefreshController();
TopListModel _model = TopListModel();
Function(String str) refresh;
@override
void initState() {
super.initState();
_model.getData(true);
refresh = (str) {
_model.stateLive.notifyView(LOADING);
_model.getData(true);
};
SimpleEventBus.instance().resignEvent("TopList", refresh);
print("${this} INIT");
}
@override
void dispose() {
refreshController.dispose();
_model.dispose();
SimpleEventBus.instance().unResignEvent("TopList", refresh);
print("${this} QUIT");
super.dispose();
}
@override
Widget build(BuildContext context) {
super.build(context);
return baseStateView(MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height, _model.stateLive, home(), () {
_model.getData(true);
});
}
Widget home() {
return baseRefreshView(refreshController, () {}, null, null,
customScrollView: CustomScrollView(
// physics: NeverScrollableScrollPhysics(),
physics: ClampingScrollPhysics(),
// shrinkWrap: true,
slivers: <Widget>[
SliverToBoxAdapter(
child: Container(
height: widget.topHeight,
),
),
// extend.SliverOverlapInjector(
// handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
// ),
StreamBuilder<List<Plans>>(
stream: _model.datasLive.stream,
initialData: _model.datas ?? [],
builder: (c, data) {
if (data.data.isEmpty && _model.page > 1) {
refreshController.loadNoData();
} else {
refreshController.loadComplete();
}
return SliverFixedExtentList(
itemExtent: 100,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return PlanItem(_model.datas[index]);
},
childCount: _model.datas.length,
),
);
},
),
StreamBuilder<List<Plans>>(
stream: _model.datasLive.stream,
initialData: _model.datas ?? [],
builder: (c, data) {
double height = MediaQuery.of(context).size.height -
40 -
100 * _model.datas.length
;
return SliverToBoxAdapter(
child: Container(
height: height < 0 ? 0 : height,
),
);
},
),
],
), onLoading: () {
_model.loadMore();
}, pullDown: false, pullUp: true);
}
@override
bool get wantKeepAlive => true;
}
/*
* @author lsy
* @date 2020/7/3
**/
import 'package:flutter_common/commonModel/live/BaseModel.dart';
import 'package:flutter_common/commonModel/live/LiveData.dart';
import 'package:flutter_common/commonModel/toast/NativeToast.dart';
import 'package:gm_flutter/ClueModel/server/api/ClueApi.serv.dart';
import 'package:gm_flutter/ClueModel/server/entity/PlanFeedBean.dart';
import 'package:gm_flutter/commonModel/GMBase.dart';
import 'package:gm_flutter/commonModel/bean/Pair.dart';
import 'package:gm_flutter/commonModel/rx/RxDispose.dart';
import 'package:gm_flutter/commonModel/util/PrintUtil.dart';
class TopListModel extends BaseModel {
LiveData<int> stateLive = LiveData();
String tagId;
int page = 1;
RxDispose rxDispose = RxDispose();
LiveData<List<Plans>> datasLive = LiveData();
List<Plans> datas = [];
@override
void dispose() {
rxDispose.dispose();
stateLive.dispost();
}
void getData(bool clear) {
if (clear) {
page = 1;
datas.clear();
}
ClueApiImpl.getInstance()
.getPlanFeed(
DioUtil.getInstance().getDio(), tagId, "", "", "", "", "", page)
.listen((event) {
if (event.error == 0) {
if (event.data.plans == null || event.data.plans.isEmpty) {
if (page == 1) {
stateLive.notifyView(EMPTY);
} else {
datasLive.notifyView([]);
}
} else {
datas.addAll(event.data.plans);
datasLive.notifyView(datas);
}
if (page == 1) {
stateLive.notifyView(ENDLOADING);
}
} else {
NativeToast.showNativeToast(event.message);
stateLive.notifyView(FAIL);
}
})
.addToDispose(rxDispose)
.onError((err) {
PrintUtil.printBug(err);
stateLive.notifyView(FAIL);
});
}
void loadMore() {
page++;
getData(false);
}
}
......@@ -2,15 +2,38 @@
* @author lsy
* @date 2020/7/2
**/
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_common/commonModel/live/BaseModel.dart';
import 'package:flutter_common/commonModel/live/LiveData.dart';
import 'package:flutter_common/commonModel/toast/NativeToast.dart';
import 'package:gm_flutter/ClueModel/server/api/ClueApi.serv.dart';
import 'package:gm_flutter/ClueModel/server/entity/PlanBarBean.dart';
import 'package:gm_flutter/commonModel/GMBase.dart';
import 'package:gm_flutter/commonModel/bean/Pair.dart';
import 'package:gm_flutter/commonModel/rx/RxDispose.dart';
import 'package:gm_flutter/commonModel/util/PrintUtil.dart';
class TopModel extends BaseModel {
int tabIndex = 0;
List<String> tabs = ["全部", "眼部", "自体脂肪", "轮廓骨骼"];
List<Tabs> tabs = [];
LiveData<String> titleLive = new LiveData();
LiveData<String> imageLive = LiveData();
LiveData<double> textLive = LiveData();
LiveData<int> stateLive = LiveData();
RxDispose rxDispose = RxDispose();
String rank_type;
String imageUrl;
@override
void dispose() {}
void dispose() {
rxDispose.dispose();
titleLive.dispost();
imageLive.dispost();
stateLive.dispost();
textLive.dispost();
}
void selectTab(int index) {
if (tabIndex == index) {
......@@ -18,4 +41,25 @@ class TopModel extends BaseModel {
}
tabIndex = index;
}
void init(VoidCallback back) {
ClueApiImpl.getInstance()
.getPlanBar(DioUtil.getInstance().getDio(), rank_type)
.listen((event) {
if (event.error == 0) {
stateLive.notifyView(ENDLOADING);
imageUrl = event.data.bannerImage;
tabs = event.data.tabs;
back();
} else {
NativeToast.showNativeToast(event.message);
stateLive.notifyView(FAIL);
}
})
.addToDispose(rxDispose)
.onError((error) {
PrintUtil.printBug(error);
stateLive.notifyView(FAIL);
});
}
}
......@@ -2,15 +2,26 @@
* @author lsy
* @date 2020/7/2
**/
import 'dart:async';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'
as extend;
import 'package:flutter/material.dart';
import 'package:gm_flutter/ClueModel/page/levelTwo/LevelTwoPage.dart';
import 'package:gm_flutter/ClueModel/page/top/TopModel.dart';
import 'package:gm_flutter/ClueModel/util/PosUtil.dart';
import 'package:gm_flutter/commonModel/base/BaseComponent.dart';
import 'package:gm_flutter/commonModel/base/BaseState.dart';
import 'package:gm_flutter/commonModel/view/baseTabIndicator.dart';
import 'package:gm_flutter/commonModel/eventbus/SimpleEventBus.dart';
import 'package:gm_flutter/commonModel/util/DartUtil.dart';
import 'package:gm_flutter/commonModel/view/baseRefreshIndicator.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'TopList.dart';
class TopPage extends StatefulWidget {
@override
State<StatefulWidget> createState() => TopPageState();
......@@ -18,84 +29,156 @@ class TopPage extends StatefulWidget {
class TopPageState extends BaseState<TopPage> with TickerProviderStateMixin {
RefreshController refreshController = RefreshController();
TabController tabController;
TopModel _model = TopModel();
int index = 0;
TabController tabController;
@override
void initState() {
tabController = TabController(length: 4, vsync: this);
init();
super.initState();
}
void init() {
_model.init(() {
_initTabBar();
});
}
@override
void dispose() {
_model.dispose();
tabController?.removeListener(tabControlerListener);
tabController?.dispose();
refreshController.dispose();
super.dispose();
}
void _initTabBar() {
if (tabController != null) {
tabController.removeListener(tabControlerListener);
}
tabController = TabController(
initialIndex: index, length: _model.tabs.length, vsync: this);
tabController.addListener(tabControlerListener);
setState(() {});
}
@override
Widget buildItem(BuildContext context) {
return Scaffold(
body: home(),
body: baseStateView(
MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height,
_model.stateLive,
Container(
child: BaseNestedScrollViewRefreshIndicator(
onRefresh: () async {
Completer completer = new Completer();
_model.init(() {
_initTabBar();
SimpleEventBus.instance().notifyListener("TopList", "");
completer.complete();
});
return completer.future;
},
child: homeWarp()),
), () {
init();
}),
);
}
Widget home() {
return baseRefreshView(refreshController, () {}, null, null,
customScrollView: CustomScrollView(slivers: <Widget>[
SliverAppBar(
pinned: true,
elevation: 0,
expandedHeight: 0,
flexibleSpace: FlexibleSpaceBar(title: Text('二级方案')),
),
SliverToBoxAdapter(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Container(
height: 50,
color: Colors.pink,
)
],
)),
SliverPersistentHeader(
pinned: true,
delegate: StickyTabBarDelegate(
child: Container(
height: 40,
color: Colors.white,
child: baseTabBar(tabController, getTabs(), (index) {
_model.selectTab(index);
}),
)),
Widget homeWarp() {
return Stack(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: NotificationListener(
onNotification: (scrollNotification) {
if (scrollNotification is KeepAliveNotification ||
scrollNotification is OverscrollIndicatorNotification) {
return false;
}
if (scrollNotification is ScrollUpdateNotification &&
scrollNotification.metrics.axisDirection.index == 2) {}
if (scrollNotification.depth == 0) {
if (scrollNotification.metrics.pixels > 80) {
_model.textLive.notifyView(
(scrollNotification.metrics.pixels - 80) / 40 > 1.0
? 1.0
: (scrollNotification.metrics.pixels - 80) / 40);
} else {
_model.textLive.notifyView(0.0);
}
} else if (scrollNotification.depth == 2) {
if (scrollNotification.metrics.pixels < 50 &&
scrollNotification.metrics.pixels > 0) {
_model.textLive.notifyView(1.0);
}
}
return false;
},
child: home(),
),
SliverFillRemaining(
child: TabBarView(controller: tabController, children: <Widget>[
Container(
color: Colors.red,
),
Container(
color: Colors.red,
),
Container(
color: Colors.red,
),
baseSliverBack(() {
Navigator.of(context).pop();
}),
baseSliverTitle(
"title", MediaQuery.of(context).size.width, _model.textLive)
],
);
}
Widget home() {
List<Widget> list = [];
var d = MediaQueryData.fromWindow(window).padding.top;
for (int i = 0; i < _model.tabs.length; i++) {
list.add(extend.NestedScrollViewInnerScrollPositionKeyWidget(Key("Tab${i}"),
TopList(_model.tabs[i].id, kToolbarHeight + d)));
}
return extend.NestedScrollView(
innerScrollPositionKeyBuilder: () {
String index = 'Tab${tabController.index.toString()}';
return Key(index);
},
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverOverlapAbsorber(
handle: extend.NestedScrollView.sliverOverlapAbsorberHandleFor(
context),
sliver: baseSliverAppBar(_model.imageUrl)),
SliverPersistentHeader(
pinned: true,
delegate: StickyTabBarDelegate(
child: Container(
height: 40,
color: Colors.white,
child: baseTabBar(tabController, getTabs(), (index) {
_model.selectTab(index);
}),
)),
),
Container(
color: Colors.blue,
)
]))
]));
];
},
body: TabBarView(controller: tabController, children: list));
}
List<Widget> getTabs() {
List<Widget> list = [];
for (int i = 0; i < _model.tabs.length; i++) {
list.add(baseTabBarItem(_model.tabs[i],
list.add(baseTabBarItem(_model.tabs[i].name,
leftPadding: i == 0 ? 24 : 28,
rightPadding: i == _model.tabs.length - 1 ? 24 : 28));
}
return list;
}
void tabControlerListener() {
if (index != tabController.index) {
index = tabController.index;
}
}
}
......@@ -8,29 +8,32 @@ import 'package:flutter_common/Annotations/anno/Query.dart';
import 'package:flutter_common/Annotations/anno/ServiceCenter.dart';
import 'package:gm_flutter/ClueModel/server/entity/ConsultQuestionsBean.dart';
import 'package:gm_flutter/ClueModel/server/entity/LevelOneFeedList.dart';
import 'package:gm_flutter/ClueModel/server/entity/PlanBarBean.dart';
import 'package:gm_flutter/ClueModel/server/entity/PlanCompareDetail.dart';
import 'package:gm_flutter/ClueModel/server/entity/PlanConsultBean.dart';
import 'package:gm_flutter/ClueModel/server/entity/PlanOverviewBean.dart';
import 'package:gm_flutter/ClueModel/server/entity/PlanOverViewBean.dart';
import 'package:gm_flutter/ClueModel/server/entity/PlansCompareFeed.dart';
import 'package:gm_flutter/ClueModel/server/entity/ProjectDetailsItem.dart';
import 'package:gm_flutter/ClueModel/server/entity/PlanBean.dart';
import 'package:gm_flutter/ClueModel/server/entity/PlanFeedBean.dart';
import 'package:gm_flutter/ClueModel/server/entity/DiscussLowPriceModelBean.dart';
import 'package:gm_flutter/ClueModel/server/entity/DiscussLowPriceAuthBean.dart';
@ServiceCenter()
abstract class ClueApi {
@Get("/api/janus/plans/plan_detail")
ProjectDetailsItem getProjectDetails(@Query("plan_id") double plan_id);
ProjectDetailsItem getProjectDetails(@Query("plan_id") int plan_id);
@Get("/api/janus/plans/compare_feed")
PlansCompareFeed getPlansCompareFeed(
@Query("plan_id") double plan_id, @Query("plan_type") double plan_type);
@Query("plan_id") int plan_id, @Query("plan_type") int plan_type);
@Get("api/janus/plans/plan_feed")
LevelOneFeedList getLevelOneList(@Query("plan_id") int plan_id,
@Query("tab_type") String tab_type, @Query("page") int page);
@Get("api/janus/plans/compare")
PlanCompareDetail getPlanCompareDetail(@Query("plan_ids") List<int> plan_ids);
PlanCompareDetail getPlanCompareDetail(@Query("plan_ids") List<int> planIds);
@Get("api/janus/plans/options")
PlanBean getPlan();
......@@ -46,13 +49,27 @@ abstract class ClueApi {
@Query("page") int page,
);
@Get("/api/janus/plans/plan_overview")
PlanOverviewBean getPlanOverviewBean(@Query("plan_id") int plan_id);
@Get("/api/janus/plans/consult_questions")
ConsultQuestionsBean getConsultQuestions(@Query("plan_id") int plan_id);
@Post("/api/janus/plans/plan_consult")
@Post("api/janus/plans/plan_consult")
PlanConsultBean sendMessage(@Query("doctor_ids") List<int> doctor_ids,
@Query("question") String question, @Query("plan_id") int plan_id);
@Get("api/janus/plans/rank")
PlanBarBean getPlanBar(@Query("rank_type") String rank_type);
@Get("api/janus/plans/plan_phone_authorize")
DiscussLowPriceModelBean getPopviewData(
@Query("plan_id") String planId,
@Query("hospital_id") String hospitalId,
@Query("doctor_id") String doctorId,
);
@Get("api/janus/plans/agree_phone_authorize")
DiscussLowPriceAuthBean givePhoneAuth(
@Query("lead_phone_request_id") int leadPhoneRequestId);
@Get("api/janus/plans/plan_overview")
PlanOverViewBean getPlanOverView(@Query("plan_id") int plan_id);
}
......@@ -20,9 +20,12 @@ import 'package:gm_flutter/ClueModel/server/entity/LevelOneFeedList.dart';
import 'package:gm_flutter/ClueModel/server/entity/PlanCompareDetail.dart';
import 'package:gm_flutter/ClueModel/server/entity/PlanBean.dart';
import 'package:gm_flutter/ClueModel/server/entity/PlanFeedBean.dart';
import 'package:gm_flutter/ClueModel/server/entity/PlanOverviewBean.dart';
import 'package:gm_flutter/ClueModel/server/entity/ConsultQuestionsBean.dart';
import 'package:gm_flutter/ClueModel/server/entity/PlanConsultBean.dart';
import 'package:gm_flutter/ClueModel/server/entity/PlanBarBean.dart';
import 'package:gm_flutter/ClueModel/server/entity/DiscussLowPriceModelBean.dart';
import 'package:gm_flutter/ClueModel/server/entity/DiscussLowPriceAuthBean.dart';
import 'package:gm_flutter/ClueModel/server/entity/PlanOverViewBean.dart';
const bool inProduction = const bool.fromEnvironment("dart.vm.product");
......@@ -40,7 +43,7 @@ class ClueApiImpl {
return _instance;
}
Stream<ProjectDetailsItem> getProjectDetails(Dio _dio, double plan_id) {
Stream<ProjectDetailsItem> getProjectDetails(Dio _dio, int plan_id) {
return Stream.fromFuture(get(_dio, '/api/janus/plans/plan_detail', data: {
'plan_id': plan_id,
})).flatMap((value) {
......@@ -55,7 +58,7 @@ class ClueApiImpl {
}
Stream<PlansCompareFeed> getPlansCompareFeed(
Dio _dio, double plan_id, double plan_type) {
Dio _dio, int plan_id, int plan_type) {
return Stream.fromFuture(get(_dio, '/api/janus/plans/compare_feed', data: {
'plan_id': plan_id,
'plan_type': plan_type,
......@@ -87,9 +90,9 @@ class ClueApiImpl {
});
}
Stream<PlanCompareDetail> getPlanCompareDetail(Dio _dio, List<int> plan_ids) {
Stream<PlanCompareDetail> getPlanCompareDetail(Dio _dio, List<int> planIds) {
return Stream.fromFuture(get(_dio, 'api/janus/plans/compare', data: {
'plan_ids': plan_ids,
'plan_ids': planIds,
})).flatMap((value) {
if (value != null &&
(value.statusCode >= 200 && value.statusCode < 300)) {
......@@ -142,46 +145,93 @@ class ClueApiImpl {
});
}
Stream<PlanOverviewBean> getPlanOverviewBean(Dio _dio, int plan_id) {
return Stream.fromFuture(get(_dio, '/api/janus/plans/plan_overview', data: {
Stream<ConsultQuestionsBean> getConsultQuestions(Dio _dio, int plan_id) {
return Stream.fromFuture(
get(_dio, '/api/janus/plans/consult_questions', data: {
'plan_id': plan_id,
})).flatMap((value) {
if (value != null &&
(value.statusCode >= 200 && value.statusCode < 300)) {
return Stream.fromFuture(
compute(parsePlanOverviewBean, value.toString()));
compute(parseConsultQuestionsBean, value.toString()));
} else {
throw Exception("--未知网络错误--");
}
});
}
Stream<ConsultQuestionsBean> getConsultQuestions(Dio _dio, int plan_id) {
return Stream.fromFuture(
get(_dio, '/api/janus/plans/consult_questions', data: {
Stream<PlanConsultBean> sendMessage(
Dio _dio, List<int> doctor_ids, String question, int plan_id) {
return Stream.fromFuture(post(_dio, 'api/janus/plans/plan_consult', data: {
'doctor_ids': doctor_ids,
'question': question,
'plan_id': plan_id,
})).flatMap((value) {
if (value != null &&
(value.statusCode >= 200 && value.statusCode < 300)) {
return Stream.fromFuture(
compute(parseConsultQuestionsBean, value.toString()));
compute(parsePlanConsultBean, value.toString()));
} else {
throw Exception("--未知网络错误--");
}
});
}
Stream<PlanConsultBean> sendMessage(
Dio _dio, List<int> doctor_ids, String question, int plan_id) {
return Stream.fromFuture(post(_dio, '/api/janus/plans/plan_consult', data: {
'doctor_ids': doctor_ids,
'question': question,
Stream<PlanBarBean> getPlanBar(Dio _dio, String rank_type) {
return Stream.fromFuture(get(_dio, 'api/janus/plans/rank', data: {
'rank_type': rank_type,
})).flatMap((value) {
if (value != null &&
(value.statusCode >= 200 && value.statusCode < 300)) {
return Stream.fromFuture(compute(parsePlanBarBean, value.toString()));
} else {
throw Exception("--未知网络错误--");
}
});
}
Stream<DiscussLowPriceModelBean> getPopviewData(
Dio _dio, String planId, String hospitalId, String doctorId) {
return Stream.fromFuture(
get(_dio, 'api/janus/plans/plan_phone_authorize', data: {
'plan_id': planId,
'hospital_id': hospitalId,
'doctor_id': doctorId,
})).flatMap((value) {
if (value != null &&
(value.statusCode >= 200 && value.statusCode < 300)) {
return Stream.fromFuture(
compute(parseDiscussLowPriceModelBean, value.toString()));
} else {
throw Exception("--未知网络错误--");
}
});
}
Stream<DiscussLowPriceAuthBean> givePhoneAuth(
Dio _dio, int leadPhoneRequestId) {
return Stream.fromFuture(
get(_dio, 'api/janus/plans/agree_phone_authorize', data: {
'lead_phone_request_id': leadPhoneRequestId,
})).flatMap((value) {
if (value != null &&
(value.statusCode >= 200 && value.statusCode < 300)) {
return Stream.fromFuture(
compute(parseDiscussLowPriceAuthBean, value.toString()));
} else {
throw Exception("--未知网络错误--");
}
});
}
Stream<PlanOverViewBean> getPlanOverView(Dio _dio, int plan_id) {
return Stream.fromFuture(get(_dio, 'api/janus/plans/plan_overview', data: {
'plan_id': plan_id,
})).flatMap((value) {
if (value != null &&
(value.statusCode >= 200 && value.statusCode < 300)) {
return Stream.fromFuture(
compute(parsePlanConsultBean, value.toString()));
compute(parsePlanOverViewBean, value.toString()));
} else {
throw Exception("--未知网络错误--");
}
......@@ -349,10 +399,6 @@ PlanFeedBean parsePlanFeedBean(String value) {
return PlanFeedBean.fromJson(json.decode(value));
}
PlanOverviewBean parsePlanOverviewBean(String value) {
return PlanOverviewBean.fromJson(json.decode(value));
}
ConsultQuestionsBean parseConsultQuestionsBean(String value) {
return ConsultQuestionsBean.fromJson(json.decode(value));
}
......@@ -360,3 +406,19 @@ ConsultQuestionsBean parseConsultQuestionsBean(String value) {
PlanConsultBean parsePlanConsultBean(String value) {
return PlanConsultBean.fromJson(json.decode(value));
}
PlanBarBean parsePlanBarBean(String value) {
return PlanBarBean.fromJson(json.decode(value));
}
DiscussLowPriceModelBean parseDiscussLowPriceModelBean(String value) {
return DiscussLowPriceModelBean.fromJson(json.decode(value));
}
DiscussLowPriceAuthBean parseDiscussLowPriceAuthBean(String value) {
return DiscussLowPriceAuthBean.fromJson(json.decode(value));
}
PlanOverViewBean parsePlanOverViewBean(String value) {
return PlanOverViewBean.fromJson(json.decode(value));
}
/*
* @Author: zx
* @Date: 2020-07-04 11:51:02
* @Last Modified by: zx
* @Last Modified time: 2020-07-04 12:52:22
*/
class DiscussLowPriceAuthBean {
int error;
String message;
Null extra;
Null errorExtra;
UserType userType;
Data data;
DiscussLowPriceAuthBean(
{this.error,
this.message,
this.extra,
this.errorExtra,
this.userType,
this.data});
DiscussLowPriceAuthBean.fromJson(Map<String, dynamic> json) {
error = json['error'];
message = json['message'];
extra = json['extra'];
errorExtra = json['error_extra'];
userType = json['user_type'] != null
? new UserType.fromJson(json['user_type'])
: null;
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;
data['extra'] = this.extra;
data['error_extra'] = this.errorExtra;
if (this.userType != null) {
data['user_type'] = this.userType.toJson();
}
if (this.data != null) {
data['data'] = this.data.toJson();
}
return data;
}
}
class UserType {
UserType();
UserType.fromJson(Map<String, dynamic> json) {}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
return data;
}
}
class Data {
bool success;
Data({this.success});
Data.fromJson(Map<String, dynamic> json) {
success = json['success'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['success'] = this.success;
return data;
}
}
/*
* @Author: zx
* @Date: 2020-07-03 20:56:15
* @Last Modified by: zx
* @Last Modified time: 2020-07-03 20:57:44
*/
class DiscussLowPriceModelBean {
int error;
String message;
Null extra;
Null errorExtra;
UserType userType;
Data data;
DiscussLowPriceModelBean(
{this.error,
this.message,
this.extra,
this.errorExtra,
this.userType,
this.data});
DiscussLowPriceModelBean.fromJson(Map<String, dynamic> json) {
error = json['error'];
message = json['message'];
extra = json['extra'];
errorExtra = json['error_extra'];
userType = json['user_type'] != null
? new UserType.fromJson(json['user_type'])
: null;
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;
data['extra'] = this.extra;
data['error_extra'] = this.errorExtra;
if (this.userType != null) {
data['user_type'] = this.userType.toJson();
}
if (this.data != null) {
data['data'] = this.data.toJson();
}
return data;
}
}
class UserType {
UserType();
UserType.fromJson(Map<String, dynamic> json) {}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
return data;
}
}
class Data {
int leadPhoneRequestId;
int authorizeType;
String icon;
String tip;
String title;
String authorizePhone;
Data(
{this.leadPhoneRequestId,
this.authorizeType,
this.icon,
this.tip,
this.title,
this.authorizePhone});
Data.fromJson(Map<String, dynamic> json) {
leadPhoneRequestId = json['lead_phone_request_id'];
authorizeType = json['authorize_type'];
icon = json['icon'];
tip = json['tip'];
title = json['title'];
authorizePhone = json['authorize_phone'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['lead_phone_request_id'] = this.leadPhoneRequestId;
data['authorize_type'] = this.authorizeType;
data['icon'] = this.icon;
data['tip'] = this.tip;
data['title'] = this.title;
data['authorize_phone'] = this.authorizePhone;
return data;
}
}
/*
* @author lsy
* @date 2020/7/3
**/
class PlanBarBean {
int error;
String message;
Null extra;
Null errorExtra;
UserType userType;
Data data;
PlanBarBean({this.error, this.message, this.extra, this.errorExtra, this.userType, this.data});
PlanBarBean.fromJson(Map<String, dynamic> json) {
error = json['error'];
message = json['message'];
extra = json['extra'];
errorExtra = json['error_extra'];
userType = json['user_type'] != null ? new UserType.fromJson(json['user_type']) : null;
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;
data['extra'] = this.extra;
data['error_extra'] = this.errorExtra;
if (this.userType != null) {
data['user_type'] = this.userType.toJson();
}
if (this.data != null) {
data['data'] = this.data.toJson();
}
return data;
}
}
class UserType {
UserType();
UserType.fromJson(Map<String, dynamic> json) {
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
return data;
}
}
class Data {
String bannerImage;
List<Tabs> tabs;
Data({this.bannerImage, this.tabs});
Data.fromJson(Map<String, dynamic> json) {
bannerImage = json['banner_image'];
if (json['tabs'] != null) {
tabs = new List<Tabs>();
json['tabs'].forEach((v) { tabs.add(new Tabs.fromJson(v)); });
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['banner_image'] = this.bannerImage;
if (this.tabs != null) {
data['tabs'] = this.tabs.map((v) => v.toJson()).toList();
}
return data;
}
}
class Tabs {
bool selected;
int id;
String name;
Tabs({this.selected, this.id, this.name});
Tabs.fromJson(Map<String, dynamic> json) {
selected = json['selected'];
id = json['id'];
name = json['name'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['selected'] = this.selected;
data['id'] = this.id;
data['name'] = this.name;
return data;
}
}
/*
* @author lsy
* @date 2020/7/3
**/
class PlanOverViewBean {
int error;
String message;
Null extra;
Null errorExtra;
UserType userType;
PlanOverData data;
PlanOverViewBean(
{this.error,
this.message,
this.extra,
this.errorExtra,
this.userType,
this.data});
PlanOverViewBean.fromJson(Map<String, dynamic> json) {
error = json['error'];
message = json['message'];
extra = json['extra'];
errorExtra = json['error_extra'];
userType = json['user_type'] != null
? new UserType.fromJson(json['user_type'])
: null;
data = json['data'] != null ? new PlanOverData.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;
data['extra'] = this.extra;
data['error_extra'] = this.errorExtra;
if (this.userType != null) {
data['user_type'] = this.userType.toJson();
}
if (this.data != null) {
data['data'] = this.data.toJson();
}
return data;
}
}
class UserType {
UserType();
UserType.fromJson(Map<String, dynamic> json) {}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
return data;
}
}
class PlanOverData {
Banner banner;
String name;
String positiveRate;
String salesCount;
String planDescription;
String guidePrice;
List<OverviewAttrs> overviewAttrs;
List<ExplanationAttrs> explanationAttrs;
List<Tabs> tabs;
PlanOverData(
{this.banner,
this.name,
this.positiveRate,
this.salesCount,
this.planDescription,
this.guidePrice,
this.overviewAttrs,
this.explanationAttrs,
this.tabs});
PlanOverData.fromJson(Map<String, dynamic> json) {
banner =
json['banner'] != null ? new Banner.fromJson(json['banner']) : null;
name = json['name'];
positiveRate = json['positive_rate'];
salesCount = json['sales_count'];
planDescription = json['plan_description'];
guidePrice = json['guide_price'];
if (json['overview_attrs'] != null) {
overviewAttrs = new List<OverviewAttrs>();
json['overview_attrs'].forEach((v) {
overviewAttrs.add(new OverviewAttrs.fromJson(v));
});
}
if (json['explanation_attrs'] != null) {
explanationAttrs = new List<ExplanationAttrs>();
json['explanation_attrs'].forEach((v) {
explanationAttrs.add(new ExplanationAttrs.fromJson(v));
});
}
if (json['tabs'] != null) {
tabs = new List<Tabs>();
json['tabs'].forEach((v) {
tabs.add(new Tabs.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.banner != null) {
data['banner'] = this.banner.toJson();
}
data['name'] = this.name;
data['positive_rate'] = this.positiveRate;
data['sales_count'] = this.salesCount;
data['plan_description'] = this.planDescription;
data['guide_price'] = this.guidePrice;
if (this.overviewAttrs != null) {
data['overview_attrs'] =
this.overviewAttrs.map((v) => v.toJson()).toList();
}
if (this.explanationAttrs != null) {
data['explanation_attrs'] =
this.explanationAttrs.map((v) => v.toJson()).toList();
}
if (this.tabs != null) {
data['tabs'] = this.tabs.map((v) => v.toJson()).toList();
}
return data;
}
}
class Banner {
String type;
String imageUrl;
String videoUrl;
Banner({this.type, this.imageUrl, this.videoUrl});
Banner.fromJson(Map<String, dynamic> json) {
type = json['type'];
imageUrl = json['image_url'];
videoUrl = json['video_url'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['type'] = this.type;
data['image_url'] = this.imageUrl;
data['video_url'] = this.videoUrl;
return data;
}
}
class OverviewAttrs {
int attrId;
String attrName;
String attrValue;
OverviewAttrs({this.attrId, this.attrName, this.attrValue});
OverviewAttrs.fromJson(Map<String, dynamic> json) {
attrId = json['attr_id'];
attrName = json['attr_name'];
attrValue = json['attr_value'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['attr_id'] = this.attrId;
data['attr_name'] = this.attrName;
data['attr_value'] = this.attrValue;
return data;
}
}
class Tabs {
String tabType;
String name;
Tabs({this.tabType, this.name});
Tabs.fromJson(Map<String, dynamic> json) {
tabType = json['tab_type'];
name = json['name'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['tab_type'] = this.tabType;
data['name'] = this.name;
return data;
}
}
class ExplanationAttrs {
int attrId;
String attrName;
String attrValue;
ExplanationAttrs({this.attrId, this.attrName, this.attrValue});
ExplanationAttrs.fromJson(Map<String, dynamic> json) {
attrId = json['attr_id'];
attrName = json['attr_name'];
attrValue = json['attr_value'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['attr_id'] = this.attrId;
data['attr_name'] = this.attrName;
data['attr_value'] = this.attrValue;
return data;
}
}
class PlanOverviewBean {
/*
* @author lsy
* @date 2020/7/3
**/
class PlanOverViewBean {
int error;
String message;
Null extra;
Null errorExtra;
UserType userType;
Data data;
PlanOverData data;
PlanOverviewBean(
PlanOverViewBean(
{this.error,
this.message,
this.extra,
......@@ -14,7 +18,7 @@ class PlanOverviewBean {
this.userType,
this.data});
PlanOverviewBean.fromJson(Map<String, dynamic> json) {
PlanOverViewBean.fromJson(Map<String, dynamic> json) {
error = json['error'];
message = json['message'];
extra = json['extra'];
......@@ -22,7 +26,7 @@ class PlanOverviewBean {
userType = json['user_type'] != null
? new UserType.fromJson(json['user_type'])
: null;
data = json['data'] != null ? new Data.fromJson(json['data']) : null;
data = json['data'] != null ? new PlanOverData.fromJson(json['data']) : null;
}
Map<String, dynamic> toJson() {
......@@ -52,18 +56,18 @@ class UserType {
}
}
class Data {
class PlanOverData {
Banner banner;
String name;
String positiveRate;
String salesCount;
String planDescription;
String guidePrice;
List<OverviewAttrsEntity> overviewAttrs;
List<ExplanationAttrsEntity> explanationAttrs;
List<OverviewAttrs> overviewAttrs;
List<ExplanationAttrs> explanationAttrs;
List<Tabs> tabs;
Data(
PlanOverData(
{this.banner,
this.name,
this.positiveRate,
......@@ -74,7 +78,7 @@ class Data {
this.explanationAttrs,
this.tabs});
Data.fromJson(Map<String, dynamic> json) {
PlanOverData.fromJson(Map<String, dynamic> json) {
banner =
json['banner'] != null ? new Banner.fromJson(json['banner']) : null;
name = json['name'];
......@@ -83,15 +87,15 @@ class Data {
planDescription = json['plan_description'];
guidePrice = json['guide_price'];
if (json['overview_attrs'] != null) {
overviewAttrs = new List<OverviewAttrsEntity>();
overviewAttrs = new List<OverviewAttrs>();
json['overview_attrs'].forEach((v) {
overviewAttrs.add(new OverviewAttrsEntity.fromJson(v));
overviewAttrs.add(new OverviewAttrs.fromJson(v));
});
}
if (json['explanation_attrs'] != null) {
explanationAttrs = new List<ExplanationAttrsEntity>();
explanationAttrs = new List<ExplanationAttrs>();
json['explanation_attrs'].forEach((v) {
explanationAttrs.add(new ExplanationAttrsEntity.fromJson(v));
explanationAttrs.add(new ExplanationAttrs.fromJson(v));
});
}
if (json['tabs'] != null) {
......@@ -149,14 +153,14 @@ class Banner {
}
}
class OverviewAttrsEntity {
class OverviewAttrs {
int attrId;
String attrName;
String attrValue;
OverviewAttrsEntity({this.attrId, this.attrName, this.attrValue});
OverviewAttrs({this.attrId, this.attrName, this.attrValue});
OverviewAttrsEntity.fromJson(Map<String, dynamic> json) {
OverviewAttrs.fromJson(Map<String, dynamic> json) {
attrId = json['attr_id'];
attrName = json['attr_name'];
attrValue = json['attr_value'];
......@@ -171,43 +175,43 @@ class OverviewAttrsEntity {
}
}
class ExplanationAttrsEntity {
int attrId;
String attrName;
String attrValue;
class Tabs {
String tabType;
String name;
ExplanationAttrsEntity({this.attrId, this.attrName, this.attrValue});
Tabs({this.tabType, this.name});
ExplanationAttrsEntity.fromJson(Map<String, dynamic> json) {
attrId = json['attr_id'];
attrName = json['attr_name'];
attrValue = json['attr_value'];
Tabs.fromJson(Map<String, dynamic> json) {
tabType = json['tab_type'];
name = json['name'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['attr_id'] = this.attrId;
data['attr_name'] = this.attrName;
data['attr_value'] = this.attrValue;
data['tab_type'] = this.tabType;
data['name'] = this.name;
return data;
}
}
class Tabs {
String tabType;
String name;
class ExplanationAttrs {
int attrId;
String attrName;
String attrValue;
Tabs({this.tabType, this.name});
ExplanationAttrs({this.attrId, this.attrName, this.attrValue});
Tabs.fromJson(Map<String, dynamic> json) {
tabType = json['tab_type'];
name = json['name'];
ExplanationAttrs.fromJson(Map<String, dynamic> json) {
attrId = json['attr_id'];
attrName = json['attr_name'];
attrValue = json['attr_value'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['tab_type'] = this.tabType;
data['name'] = this.name;
data['attr_id'] = this.attrId;
data['attr_name'] = this.attrName;
data['attr_value'] = this.attrValue;
return data;
}
}
......@@ -19,7 +19,6 @@ class DemoState extends State<DemoPage> {
@override
void initState() {
super.initState();
flutterChannel.invokeMethod("method!!!", "wwww");
}
@override
......
......@@ -12,4 +12,6 @@ abstract class MainRouter extends RouterBaser {
void init();
Widget getTestPage();
void buriedEvent(String name, Map<String, dynamic> map);
}
......@@ -18,4 +18,9 @@ class MainRouterImpl implements MainRouter {
Widget getTestPage() {
return TestPage();
}
@override
void buriedEvent(String name, Map<String, dynamic> map) {
MainManager.getInstance().buriedEvent(name, map);
}
}
......@@ -4,9 +4,12 @@
**/
import 'package:flutter/services.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'package:gm_flutter/commonModel/base/BaseUtil.dart';
class MainManager {
static const EventChannel _channel = EventChannel("flutter_plugin_event");
MethodChannel flutterChannel;
EventChannel flutterEvent;
bool isInit = false;
MainManager._() {}
......@@ -20,8 +23,24 @@ class MainManager {
}
startInit() {
_channel.receiveBroadcastStream().listen((data) {
if (isInit) {
return;
}
isInit = true;
print("LSY STATEINIT METHOD AND CHANNEL ");
flutterChannel = MethodChannel('gm_method_channel');
flutterEvent = EventChannel('gm_method_event');
flutterEvent.receiveBroadcastStream().listen((data) {
print("LSY FLUTTER EVENT ${data}");
});
}
void buriedEvent(String name, Map<String, dynamic> map) {
if (!isInit) {
return;
}
if (flutterChannel != null) {
flutterChannel.invokeMethod("buried-${name}", map);
}
}
}
......@@ -9,7 +9,7 @@ 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 'package:gm_flutter/main.mark.dart';
import 'package:gm_flutter/ClueModel/page/DiscussLowPrice/DiscussLowPricePopView.dart';
import 'TP.dart';
class TestPage extends StatefulWidget {
......@@ -46,12 +46,15 @@ class TestState extends BaseState<TestPage> {
context, RouterCenterImpl().findClueRouter().getProjectDetailsPage());
}));
list.add(listItem("方案对比", () {
JumpUtil.jumpToPageRight(context, RouterCenterImpl().findClueRouter().getPlansCompareFeed());
JumpUtil.jumpToPageRight(
context, RouterCenterImpl().findClueRouter().getPlansCompareFeed());
}));
list.add(listItem("榜单", () {
JumpUtil.jumpToPageRight(context, RouterCenterImpl().findClueRouter().getTopPage());
JumpUtil.jumpToPageRight(
context, RouterCenterImpl().findClueRouter().getTopPage());
}));
list.add(listItem("弹窗", () {
DiscussLowPricePopView.showPopView(context);
}));
return list;
}
......
......@@ -8,11 +8,14 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_common/commonModel/eventbus/GlobalEventBus.dart';
import 'package:flutter_common/commonModel/live/LiveData.dart';
import 'package:flutter_common/commonModel/view/iOSLoading.dart';
import 'package:flutter_svg/svg.dart';
import 'package:gm_flutter/commonModel/GMBase.dart';
import 'package:gm_flutter/commonModel/bean/Pair.dart';
import 'package:gm_flutter/commonModel/util/DartUtil.dart';
import 'package:gm_flutter/commonModel/view/ImagesAnimation.dart';
import 'package:gm_flutter/commonModel/view/baseRefreshIndicator.dart';
import 'package:gm_flutter/commonModel/view/baseTabIndicator.dart';
import 'package:lottie/lottie.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
......@@ -141,42 +144,49 @@ Widget loadingItem({bool needBackground = false}) {
Widget netErrorItem() {}
Widget errorItem(double width, double height, VoidCallback retry,
{String errorText, String retryText}) {
{String errorText, String retryText,double paddingTop}) {
return Container(
width: width,
height: height,
color: Colors.white,
padding: EdgeInsets.only(top: paddingTop),
alignment: Alignment.topCenter,
child: Container(
width: 180,
height: 315.5,
margin: EdgeInsets.only(top: 62),
child: Stack(
alignment: AlignmentDirectional.center,
children: <Widget>[
Positioned(
left: 0,
top: 0,
child: Container(
width: 180,
height: 315.5,
margin: EdgeInsets.only(top: 62),
child: Column(
children: <Widget>[
Container(
width: 180,
height: 186.5,
child: Image.asset("assets/error.png"),
),
),
Positioned(
top: 166,
child: Container(
width: 180,
alignment: Alignment.center,
child: baseText(
errorText ?? "原谅我一看到美人就不淡定", 15, Color(0xff666666)),
child: Stack(
alignment: AlignmentDirectional.center,
children: <Widget>[
Positioned(
left: 0,
top: 0,
child: Container(
width: 180,
height: 186.5,
child: Image.asset("assets/error.png"),
),
),
Positioned(
top: 166,
child: Container(
width: 180,
alignment: Alignment.center,
child: baseText(
errorText ?? "原谅我一看到美人就不淡定", 15, Color(0xff666666)),
),
),
],
),
),
),
Positioned(
bottom: 60,
child: Container(
Container(
width: 150,
height: 40,
margin: EdgeInsets.only(top: 17),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
gradient: LinearGradient(
......@@ -188,12 +198,10 @@ Widget errorItem(double width, double height, VoidCallback retry,
).gestureDetector(() {
retry();
}),
),
Positioned(
bottom: 0,
child: Container(
Container(
width: 150,
height: 40,
margin: EdgeInsets.only(top: 20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
border: Border.all(color: Color(0xffFF5963), width: 0.5)),
......@@ -202,10 +210,8 @@ Widget errorItem(double width, double height, VoidCallback retry,
).gestureDetector(() {
AppSettings.openWIFISettings();
}),
)
],
),
));
],
)));
}
//TODO
......@@ -289,7 +295,7 @@ Widget baseRefreshView(RefreshController refreshController,
bool pullDown = true,
VoidCallback onLoading,
ScrollController scrollController,
CustomScrollView customScrollView}) {
Widget customScrollView}) {
return SmartRefresher(
enablePullDown: pullDown,
enablePullUp: pullUp,
......@@ -307,9 +313,10 @@ Widget baseRefreshView(RefreshController refreshController,
// body = baseText("加载中", 12, Color(0xff545454));
// } else if (mode == LoadStatus.failed) {
// body = baseText("加载失败", 12, Color(0xff545454));
// } else if (mode == LoadStatus.noMore) {
// body = baseText("没有更多数据了", 12, Color(0xff545454));
// }
// } else
if (mode == LoadStatus.noMore) {
body = baseText("没有更多数据了", 12, Color(0xff545454));
}
// else {
// body = Container();
// }
......@@ -370,8 +377,9 @@ Widget normalRefreshHeader() {
);
}
Widget emptyItem(double width, double height, {String detail}) {
Widget emptyItem(double width, double height, {String detail,double paddingTop}) {
return Container(
padding: EdgeInsets.only(top: paddingTop??0),
width: width,
height: height,
color: Colors.white,
......@@ -380,20 +388,34 @@ Widget emptyItem(double width, double height, {String detail}) {
width: 175,
height: 188,
margin: EdgeInsets.only(top: 62.5),
child: Stack(
alignment: AlignmentDirectional.bottomCenter,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
width: 175,
height: 188,
child: Image.asset("assets/empty.png"),
child: Stack(
alignment: AlignmentDirectional.bottomCenter,
children: <Widget>[
Container(
width: 175,
height: 188,
child: Image.asset("assets/empty.png"),
),
Positioned(
bottom: 17,
child: baseText(detail ?? "此处太寂寥,转转别处吧", 15, Color(0xff666666)),
)
],
),
),
Positioned(
bottom: 17,
child: baseText(detail ?? "此处太寂寥,转转别处吧", 15, Color(0xff666666)),
Container(
height:0,
color: Colors.white,
)
],
),
)
),
);
}
......@@ -422,7 +444,7 @@ Widget baseTabBarItem(String text,
rightPadding = rightPadding ?? 28.0;
double width = leftPadding + text.length * 16.0 + rightPadding;
return Container(
height: 40,
height: 40.0,
width: wantWidth ?? width,
alignment: Alignment.center,
child: Tab(
......@@ -430,3 +452,77 @@ Widget baseTabBarItem(String text,
),
);
}
Widget baseSliverAppBar(String url, {double height, double elevation}) {
return SliverAppBar(
pinned: true,
centerTitle: true,
elevation: elevation ?? 0,
expandedHeight: height ?? 200,
automaticallyImplyLeading: false,
flexibleSpace: FlexibleSpaceBar(
background: Image.network(
url ?? '',
fit: BoxFit.cover,
),
),
);
}
Widget baseSliverBack(VoidCallback tap) {
return Positioned(
top: 49,
left: 15,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: tap,
child: Container(
width: 30,
height: 30,
decoration:
BoxDecoration(color: Color(0x99FFFFFF), shape: BoxShape.circle),
alignment: Alignment.center,
child: Container(
child: Image.asset("assets/left_arrow.png"),
),
)),
);
}
Widget baseSliverTitle(String text, double width, LiveData liveData) {
return Positioned(
top: 57,
child: StreamBuilder(
stream: liveData.stream,
initialData: liveData.data ?? 0.0,
builder: (c, data) {
return Opacity(
opacity: data.data,
child: Container(
alignment: Alignment.center,
width: width,
child: baseText(text, 18, Color(0xff333333)),
),
);
},
),
);
}
Widget baseStateView(double width, double height, LiveData<int> stateLive,
Widget home, VoidCallback retry,{double paddingTop}) {
return StreamBuilder(
stream: stateLive.stream,
initialData: stateLive.data ?? LOADING,
builder: (c, data) {
if (data.data == LOADING) {
return loadingItem();
} else if (data.data == FAIL) {
return errorItem(width, height, retry,paddingTop: paddingTop);
} else if (data.data == EMPTY) {
return emptyItem(width, height,paddingTop: paddingTop);
}
return home;
},
);
}
......@@ -8,5 +8,4 @@ import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
const flutterChannel = const MethodChannel('gm_method_channel');
const bool isDebug = ! const bool.fromEnvironment("dart.vm.product");
const bool isDebug = !const bool.fromEnvironment("dart.vm.product");
......@@ -5,6 +5,7 @@
const LOADING = 0;
const ENDLOADING = 1;
const FAIL = 2;
const EMPTY = 3;
class Pair<T, S> {
dynamic first;
......
/*
* @author lsy
* @date 2020/7/5
**/
import 'package:dio/dio.dart';
class SimpleEventBus{
static SimpleEventBus _eventBus;
SimpleEventBus._();
static SimpleEventBus instance(){
if(_eventBus==null){
_eventBus=SimpleEventBus._();
}
return _eventBus;
}
Map<String,List<Function(String str)>> map=Map();
void resignEvent(String name,Function(String str) func){
if(map[name]==null){
map[name]=[];
}
map[name].add(func);
}
void unResignEvent(String name,Function(String str) func){
if(map[name]==null){
return;
}
map[name].remove(func);
}
void notifyListener(String name,String str){
if(map[name]==null){
return;
}
map[name].forEach((element) {
element(str);
});
}
}
\ No newline at end of file
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:gm_flutter/commonModel/GMBase.dart';
import 'package:lottie/lottie.dart';
// The over-scroll distance that moves the indicator to its maximum
// displacement, as a percentage of the scrollable's container extent.
const double _kDragContainerExtentPercentage = 0.25;
// How much the scroll's drag gesture can overshoot the RefreshIndicator's
// displacement; max displacement = _kDragSizeFactorLimit * displacement.
const double _kDragSizeFactorLimit = 1.5;
// When the scroll ends, the duration of the refresh indicator's animation
// to the RefreshIndicator's displacement.
const Duration _kIndicatorSnapDuration = Duration(milliseconds: 150);
// The duration of the ScaleTransition that starts when the refresh action
// has completed.
const Duration _kIndicatorScaleDuration = Duration(milliseconds: 200);
/// The signature for a function that's called when the user has dragged a
/// [NestedScrollViewRefreshIndicator] far enough to demonstrate that they want the app to
/// refresh. The returned [Future] must complete when the refresh operation is
/// finished.
///
/// Used by [NestedScrollViewRefreshIndicator.onRefresh].
typedef NestedScrollViewRefreshCallback = Future<void> Function();
// The state machine moves through these modes only when the scrollable
// identified by scrollableKey has been scrolled to its min or max limit.
enum _RefreshIndicatorMode {
drag, // Pointer is down.
armed, // Dragged far enough that an up event will run the onRefresh callback.
snap, // Animating to the indicator's final "displacement".
refresh, // Running the refresh callback.
done, // Animating the indicator's fade-out after refreshing.
canceled, // Animating the indicator's fade-out after not arming.
}
/// A widget that supports the Material "swipe to refresh" idiom.
///
/// When the child's [Scrollable] descendant overscrolls, an animated circular
/// progress indicator is faded into view. When the scroll ends, if the
/// indicator has been dragged far enough for it to become completely opaque,
/// the [onRefresh] callback is called. The callback is expected to update the
/// scrollable's contents and then complete the [Future] it returns. The refresh
/// indicator disappears after the callback's [Future] has completed.
///
/// If the [Scrollable] might not have enough content to overscroll, consider
/// settings its `physics` property to [AlwaysScrollableScrollPhysics]:
///
/// ```dart
/// ListView(
/// physics: const AlwaysScrollableScrollPhysics(),
/// children: ...
// )
/// ```
///
/// Using [AlwaysScrollableScrollPhysics] will ensure that the scroll view is
/// always scrollable and, therefore, can trigger the [NestedScrollViewRefreshIndicator].
///
/// A [NestedScrollViewRefreshIndicator] can only be used with a vertical scroll view.
///
/// See also:
///
/// * <https://material.google.com/patterns/swipe-to-refresh.html>
/// * [NestedScrollViewRefreshIndicatorState], can be used to programmatically show the refresh indicator.
/// * [RefreshProgressIndicator], widget used by [NestedScrollViewRefreshIndicator] to show
/// the inner circular progress spinner during refreshes.
/// * [CupertinoSliverRefreshControl], an iOS equivalent of the pull-to-refresh pattern.
/// Must be used as a sliver inside a [CustomScrollView] instead of wrapping
/// around a [ScrollView] because it's a part of the scrollable instead of
/// being overlaid on top of it.
class BaseNestedScrollViewRefreshIndicator extends StatefulWidget {
/// Creates a refresh indicator.
///
/// The [onRefresh], [child], and [notificationPredicate] arguments must be
/// non-null. The default
/// [displacement] is 40.0 logical pixels.
const BaseNestedScrollViewRefreshIndicator({
Key key,
@required this.child,
this.displacement = 55.0,
@required this.onRefresh,
this.color,
this.backgroundColor,
this.notificationPredicate = nestedScrollViewScrollNotificationPredicate,
this.semanticsLabel,
this.semanticsValue,
}) : assert(child != null),
assert(onRefresh != null),
assert(notificationPredicate != null),
super(key: key);
/// The widget below this widget in the tree.
///
/// The refresh indicator will be stacked on top of this child. The indicator
/// will appear when child's Scrollable descendant is over-scrolled.
///
/// Typically a [ListView] or [CustomScrollView].
final Widget child;
/// The distance from the child's top or bottom edge to where the refresh
/// indicator will settle. During the drag that exposes the refresh indicator,
/// its actual displacement may significantly exceed this value.
final double displacement;
/// A function that's called when the user has dragged the refresh indicator
/// far enough to demonstrate that they want the app to refresh. The returned
/// [Future] must complete when the refresh operation is finished.
final NestedScrollViewRefreshCallback onRefresh;
/// The progress indicator's foreground color. The current theme's
/// [ThemeData.accentColor] by default.
final Color color;
/// The progress indicator's background color. The current theme's
/// [ThemeData.canvasColor] by default.
final Color backgroundColor;
/// A check that specifies whether a [ScrollNotification] should be
/// handled by this widget.
///
/// By default, checks whether `notification.depth == 0`. Set it to something
/// else for more complicated layouts.
final ScrollNotificationPredicate notificationPredicate;
/// {@macro flutter.material.progressIndicator.semanticsLabel}
///
/// This will be defaulted to [MaterialLocalizations.refreshIndicatorSemanticLabel]
/// if it is null.
final String semanticsLabel;
/// {@macro flutter.material.progressIndicator.semanticsValue}
final String semanticsValue;
@override
NestedScrollViewRefreshIndicatorState createState() =>
NestedScrollViewRefreshIndicatorState();
}
/// Contains the state for a [NestedScrollViewRefreshIndicator]. This class can be used to
/// programmatically show the refresh indicator, see the [show] method.
class NestedScrollViewRefreshIndicatorState
extends State<BaseNestedScrollViewRefreshIndicator>
with TickerProviderStateMixin<BaseNestedScrollViewRefreshIndicator> {
AnimationController _positionController;
Animation<double> _positionFactor;
Animation<double> _value;
Animation<Color> _valueColor;
_RefreshIndicatorMode _mode;
Future<void> _pendingRefreshFuture;
bool _isIndicatorAtTop;
double _dragOffset;
static final Animatable<double> _threeQuarterTween =
Tween<double>(begin: 0.0, end: 0.75);
static final Animatable<double> _kDragSizeFactorLimitTween =
Tween<double>(begin: 0.0, end: _kDragSizeFactorLimit);
@override
void initState() {
super.initState();
_positionController = AnimationController(vsync: this);
_positionFactor = _positionController.drive(_kDragSizeFactorLimitTween);
_value = _positionController.drive(
_threeQuarterTween); // The "value" of the circular progress indicator during a drag.
}
@override
void didChangeDependencies() {
final ThemeData theme = Theme.of(context);
_valueColor = _positionController.drive(
ColorTween(
begin: (widget.color ?? theme.accentColor).withOpacity(0.0),
end: (widget.color ?? theme.accentColor).withOpacity(1.0))
.chain(CurveTween(
curve: const Interval(0.0, 1.0 / _kDragSizeFactorLimit))),
);
super.didChangeDependencies();
}
@override
void dispose() {
_positionController.dispose();
super.dispose();
}
double maxContainerExtent = 0.0;
bool _handleScrollNotification(ScrollNotification notification) {
if (!widget.notificationPredicate(notification)) {
return false;
}
maxContainerExtent =
math.max(notification.metrics.viewportDimension, maxContainerExtent);
if (notification is ScrollStartNotification &&
notification.metrics.extentBefore == 0.0 &&
_mode == null &&
_start(notification.metrics.axisDirection)) {
setState(() {
_mode = _RefreshIndicatorMode.drag;
});
return false;
}
bool indicatorAtTopNow;
switch (notification.metrics.axisDirection) {
case AxisDirection.down:
indicatorAtTopNow = true;
break;
case AxisDirection.up:
indicatorAtTopNow = false;
break;
case AxisDirection.left:
case AxisDirection.right:
indicatorAtTopNow = null;
break;
}
if (indicatorAtTopNow != _isIndicatorAtTop) {
if (_mode == _RefreshIndicatorMode.drag ||
_mode == _RefreshIndicatorMode.armed)
_dismiss(_RefreshIndicatorMode.canceled);
} else if (notification is ScrollUpdateNotification) {
if (_mode == _RefreshIndicatorMode.drag ||
_mode == _RefreshIndicatorMode.armed) {
if (notification.metrics.extentBefore > 0.0) {
_dismiss(_RefreshIndicatorMode.canceled);
} else {
_dragOffset -= notification.scrollDelta;
_checkDragOffset(maxContainerExtent);
}
}
if (_mode == _RefreshIndicatorMode.armed &&
notification.dragDetails == null) {
// On iOS start the refresh when the Scrollable bounces back from the
// overscroll (ScrollNotification indicating this don't have dragDetails
// because the scroll activity is not directly triggered by a drag).
_show();
}
} else if (notification is OverscrollNotification) {
if (_mode == _RefreshIndicatorMode.drag ||
_mode == _RefreshIndicatorMode.armed) {
_dragOffset -= notification.overscroll / 2.0;
_checkDragOffset(maxContainerExtent);
}
} else if (notification is ScrollEndNotification) {
switch (_mode) {
case _RefreshIndicatorMode.armed:
_show();
break;
case _RefreshIndicatorMode.drag:
_dismiss(_RefreshIndicatorMode.canceled);
break;
default:
// do nothing
break;
}
}
return false;
}
bool _handleGlowNotification(OverscrollIndicatorNotification notification) {
if (notification.depth != 0 || !notification.leading) {
return false;
}
if (_mode == _RefreshIndicatorMode.drag) {
notification.disallowGlow();
return true;
}
return false;
}
bool _start(AxisDirection direction) {
assert(_mode == null);
assert(_isIndicatorAtTop == null);
assert(_dragOffset == null);
switch (direction) {
case AxisDirection.down:
_isIndicatorAtTop = true;
break;
case AxisDirection.up:
_isIndicatorAtTop = false;
break;
case AxisDirection.left:
case AxisDirection.right:
_isIndicatorAtTop = null;
// we do not support horizontal scroll views.
return false;
}
_dragOffset = 0.0;
_positionController.value = 0.0;
return true;
}
void _checkDragOffset(double containerExtent) {
assert(_mode == _RefreshIndicatorMode.drag ||
_mode == _RefreshIndicatorMode.armed);
double newValue =
_dragOffset / (containerExtent * _kDragContainerExtentPercentage);
if (_mode == _RefreshIndicatorMode.armed)
newValue = math.max(newValue, 1.0 / _kDragSizeFactorLimit);
_positionController.value =
newValue.clamp(0.0, 1.0) as double; // this triggers various rebuilds
if (_mode == _RefreshIndicatorMode.drag && _valueColor.value.alpha == 0xFF)
_mode = _RefreshIndicatorMode.armed;
}
// Stop showing the refresh indicator.
Future<void> _dismiss(_RefreshIndicatorMode newMode) async {
await Future<void>.value();
// This can only be called from _show() when refreshing and
// _handleScrollNotification in response to a ScrollEndNotification or
// direction change.
assert(newMode == _RefreshIndicatorMode.canceled ||
newMode == _RefreshIndicatorMode.done);
setState(() {
_mode = newMode;
});
switch (_mode) {
case _RefreshIndicatorMode.done:
break;
case _RefreshIndicatorMode.canceled:
await _positionController.animateTo(0.0,
duration: _kIndicatorScaleDuration);
break;
default:
assert(false);
}
if (mounted && _mode == newMode) {
_dragOffset = null;
_isIndicatorAtTop = null;
setState(() {
_mode = null;
});
}
}
void _show() {
assert(_mode != _RefreshIndicatorMode.refresh);
assert(_mode != _RefreshIndicatorMode.snap);
final Completer<void> completer = Completer<void>();
_pendingRefreshFuture = completer.future;
_mode = _RefreshIndicatorMode.snap;
_positionController
.animateTo(1.0 / _kDragSizeFactorLimit,
duration: _kIndicatorSnapDuration)
.then<void>((void value) {
if (mounted && _mode == _RefreshIndicatorMode.snap) {
assert(widget.onRefresh != null);
setState(() {
// Show the indeterminate progress indicator.
_mode = _RefreshIndicatorMode.refresh;
});
final Future<void> refreshResult = widget.onRefresh();
assert(() {
if (refreshResult == null)
FlutterError.reportError(FlutterErrorDetails(
exception: FlutterError('The onRefresh callback returned null.\n'
'The RefreshIndicator onRefresh callback must return a Future.'),
context: ErrorDescription('when calling onRefresh'),
library: 'material library',
));
return true;
}());
if (refreshResult == null) {
return;
}
refreshResult.whenComplete(() {
if (mounted && _mode == _RefreshIndicatorMode.refresh) {
completer.complete();
_dismiss(_RefreshIndicatorMode.done);
}
});
}
});
}
/// Show the refresh indicator and run the refresh callback as if it had
/// been started interactively. If this method is called while the refresh
/// callback is running, it quietly does nothing.
///
/// Creating the [NestedScrollViewRefreshIndicator] with a [GlobalKey<RefreshIndicatorState>]
/// makes it possible to refer to the [NestedScrollViewRefreshIndicatorState].
///
/// The future returned from this method completes when the
/// [NestedScrollViewRefreshIndicator.onRefresh] callback's future completes.
///
/// If you await the future returned by this function from a [State], you
/// should check that the state is still [mounted] before calling [setState].
///
/// When initiated in this manner, the refresh indicator is independent of any
/// actual scroll view. It defaults to showing the indicator at the top. To
/// show it at the bottom, set `atTop` to false.
Future<void> show({bool atTop = true}) {
if (_mode != _RefreshIndicatorMode.refresh &&
_mode != _RefreshIndicatorMode.snap) {
if (_mode == null) {
_start(atTop ? AxisDirection.down : AxisDirection.up);
}
_show();
}
return _pendingRefreshFuture;
}
final GlobalKey _key = GlobalKey();
@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
final Widget child = NotificationListener<ScrollNotification>(
key: _key,
onNotification: _handleScrollNotification,
child: NotificationListener<OverscrollIndicatorNotification>(
onNotification: _handleGlowNotification,
child: widget.child,
),
);
if (_mode == null) {
assert(_dragOffset == null);
assert(_isIndicatorAtTop == null);
return child;
}
assert(_dragOffset != null);
assert(_isIndicatorAtTop != null);
final bool showIndeterminateIndicator =
_mode == _RefreshIndicatorMode.refresh ||
_mode == _RefreshIndicatorMode.done;
return Column(
children: <Widget>[
Container(
child: SizeTransition(
axisAlignment: _isIndicatorAtTop ? 1.0 : -1.0,
sizeFactor: _positionFactor, // this is what brings it down
child: Container(
padding: _isIndicatorAtTop
? EdgeInsets.only(top: widget.displacement)
: EdgeInsets.only(bottom: widget.displacement),
alignment: _isIndicatorAtTop
? Alignment.topCenter
: Alignment.bottomCenter,
child: AnimatedBuilder(
animation: _positionController,
builder: (BuildContext context, Widget child) {
return Container(
height: 55,
child: Lottie.asset("assets/smart_refresh_header.json",
repeat: true, reverse: false),
);
},
),
),
),
),
Expanded(
child: child,
)
],
);
// return Stack(
// children: <Widget>[
// child,
// Positioned(
// top: _isIndicatorAtTop ? 0.0 : null,
// bottom: !_isIndicatorAtTop ? 0.0 : null,
// left: 0.0,
// right: 0.0,
// child: SizeTransition(
// axisAlignment: _isIndicatorAtTop ? 1.0 : -1.0,
// sizeFactor: _positionFactor, // this is what brings it down
// child: Container(
// padding: _isIndicatorAtTop
// ? EdgeInsets.only(top: widget.displacement)
// : EdgeInsets.only(bottom: widget.displacement),
// alignment: _isIndicatorAtTop
// ? Alignment.topCenter
// : Alignment.bottomCenter,
// child: ScaleTransition(
// scale: _scaleFactor,
// child: AnimatedBuilder(
// animation: _positionController,
// builder: (BuildContext context, Widget child) {
// return RefreshProgressIndicator(
// semanticsLabel: widget.semanticsLabel ?? MaterialLocalizations.of(context).refreshIndicatorSemanticLabel,
// semanticsValue: widget.semanticsValue,
// value: showIndeterminateIndicator ? null : _value.value,
// valueColor: _valueColor,
// backgroundColor: widget.backgroundColor,
// );
// },
// ),
// ),
// ),
// ),
// ),
// ],
// );
}
}
//return true so that we can handle inner scroll notification
bool nestedScrollViewScrollNotificationPredicate(
ScrollNotification notification) {
return true;
}
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'package:flutter_common/Annotations/anno/RouterCenter.dart';
import 'package:flutter_common/commonModel/util/WindowUtil.dart';
import 'package:flutter_screenutil/screenutil.dart';
import 'package:gm_flutter/main.mark.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:gm_flutter/ClueModel/page/PlanCompareDetail/PlanCompareDetailPage.dart';
......@@ -37,7 +39,6 @@ void main() {
SharedPreferences.getInstance().then((value) {
sharedPreferences = value;
});
RouterCenterImpl().findMainRouter().init();
}, (Object error, StackTrace stack) {
//TODO
print("lsy EEEEEE ${error.toString()} ${stack.toString()}");
......@@ -58,7 +59,7 @@ class MyApp extends State<MyAppWidget> {
super.initState();
FlutterBoost.singleton.registerPageBuilders({
'demoPage': (pageName, params, _) {
return DemoPage();
return RouterCenterImpl().findClueRouter().getLevelOnePage();
},
'demoPage1': (pageName, params, _) {
return DemoPage1();
......@@ -67,30 +68,25 @@ class MyApp extends State<MyAppWidget> {
FlutterBoost.singleton
.addBoostContainerLifeCycleObserver((state, settings) {
print("LSY ${state}");
if (state == ContainerLifeCycle.Init) {
RouterCenterImpl().findMainRouter().init();
}
});
// FlutterBoost.singleton.addBoostNavigatorObserver(TestBoostNavigatorObserver());
}
@override
Widget build(BuildContext context) {
WindowUtil.setBarStatus(true);
WindowUtil.setBarStatus(false);
return MaterialApp(
theme: ThemeData(
primaryColor: Colors.white,
cursorColor: Color(0xff20BDBB),
highlightColor:Colors.transparent,
splashColor:Colors.transparent,
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
canvasColor: Colors.white),
builder: FlutterBoost.init(postPush: _onRoutePushed),
home: isDebug
? RouterCenterImpl().findMainRouter().getTestPage()
: Container(
color: Colors.white,
child: Center(
child: loadingItem(),
),
));
home: Home());
}
void _onRoutePushed(
......@@ -101,3 +97,23 @@ class MyApp extends State<MyAppWidget> {
Future<dynamic> _,
) {}
}
class Home extends StatefulWidget {
@override
State<StatefulWidget> createState() => HomeState();
}
class HomeState extends State<Home> {
@override
Widget build(BuildContext context) {
ScreenUtil.init(context, width: 375, height: 810);
return isDebug
? RouterCenterImpl().findMainRouter().getTestPage()
: Container(
color: Colors.white,
child: Center(
child: loadingItem(),
),
);
}
}
......@@ -211,6 +211,13 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.1"
extended_nested_scroll_view:
dependency: "direct main"
description:
name: extended_nested_scroll_view
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.0"
file:
dependency: transitive
description:
......@@ -255,6 +262,13 @@ packages:
url: "https://github.com/asd451398533/flutter_common.git"
source: git
version: "0.0.1"
flutter_screenutil:
dependency: "direct main"
description:
name: flutter_screenutil
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.0"
flutter_svg:
dependency: transitive
description:
......@@ -738,7 +752,7 @@ packages:
name: uuid
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
version: "2.2.0"
vector_math:
dependency: transitive
description:
......@@ -783,4 +797,4 @@ packages:
version: "2.2.1"
sdks:
dart: ">=2.8.0 <3.0.0"
flutter: ">=1.12.13+hotfix.5 <2.0.0"
flutter: ">=1.17.0 <2.0.0"
......@@ -33,6 +33,9 @@ dependencies:
shared_preferences: ^0.5.7+1
lottie: ^0.4.0+1
app_settings: ^4.0.1+1
extended_nested_scroll_view: ^1.0.0
#适配库
flutter_screenutil: ^1.1.0
dev_dependencies:
......
#!/usr/bin/env bash
echo START
projectDir=`pwd`
rootFlutter=`which flutter`
rootDir=${rootFlutter%/*}
#====clean
cd ${projectDir}
${rootFlutter} clean
export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
${rootFlutter} packages get
rm -rf ${projectDir}/build
flutter build aar --release --target-platform android-arm
#rm -rf /Users/apple/lsy/gengmei_android/gm-flutter/libs/flutterApp.aar
cp -r ${projectDir}/build/host/outputs/repo/com/example/gm_flutter/flutter_release/1.0/flutter_release-1.0.aar /Users/apple/lsy/gengmei_android/gm-flutter/libs/flutterApp.aar
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment