Commit 88fe4c9a authored by 汪林玲's avatar 汪林玲

升级到Flutter2.5.1

parents
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
build/
# Android related
**/android/**/gradle-wrapper.jar
**/android/.gradle
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Flutter.podspec
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/Flutter/flutter_export_environment.sh
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
.vscode/
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 1aafb3a8b9b0c36241c5f5b34ee914770f015818
channel: stable
project_type: package
## [0.0.1] - TODO: Add release date.
* TODO: Describe initial release.
TODO: Add your license here.
lifeChannel.shopOther recommend_list 差团购购买链接
全部评价接口
店铺详情需要 经纬度
秒杀板块显示单个配置项
宣传口-吃喝玩乐
宣传口-生活秒杀
### 引入说明
```
life_module:
git:
url: 'git@git.xiaomanxiong.com:flutter-plugin/life_module.git'
ref: 'null-safety'
```
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_repository/life_repository.dart';
import 'package:common_module/utils/date_time_helper.dart';
class AppraiseItem extends StatelessWidget {
final ShopCommentItemEntity item;
AppraiseItem(this.item);
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(
top: 16.rpx, bottom: 20.rpx, left: 12.rpx, right: 12.rpx),
color: Colors.white,
child: Column(
children: [
Container(
child: Text(
item.comment,
style: TextStyle(color: rgba(48, 38, 0, 1), fontSize: 13.rpx),
),
),
SizedBox(
height: 6.rpx,
),
_ScoreInfo(item),
],
),
);
}
}
/// 评分和距离信息
class _ScoreInfo extends StatelessWidget {
final ShopCommentItemEntity item;
_ScoreInfo(this.item);
@override
Widget build(BuildContext context) {
List<InlineSpan> widgets = [];
double score = double.parse(item.star);
for (int star = 1, len = score.ceil(); star <= len; star++) {
widgets.add(WidgetSpan(
child: Image.asset(
star <= score
? "assets/ic_star_small_full.png"
: "assets/ic_star_small_half.png",
width: 12.rpx,
height: 12.rpx,
)));
}
for (int star = widgets.length; star < 5; star++) {
widgets.add(WidgetSpan(
child: Image.asset(
"assets/ic_star_small_blank.png",
width: 12.rpx,
height: 12.rpx,
)));
}
widgets.add(WidgetSpan(
child: SizedBox(
width: 2.rpx,
)));
return Container(
height: 18.rpx,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
alignment: Alignment.centerLeft,
height: 14.rpx,
child: Text.rich(TextSpan(children: widgets)),
),
Container(
child: Text(double.parse(item.star).toStringAsFixed(1),
style: TextStyle(
color: rgba(255, 128, 0, 1),
fontSize: 14.rpx,
fontWeight: FontWeight.bold,
)),
),
Spacer(),
Text(
DateUtil.formatDateStr(item.createTime,format: "yyyy-MM-dd HH:mm:ss"),
style: TextStyle(color: rgba(153, 153, 153, 1), fontSize: 11.rpx),
)
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_module/utils/filter_panel_utils.dart';
import 'package:mvp/mvp.dart';
import 'package:life_module/models/filter_model.dart';
class FilterArea extends StatefulWidget {
final FilterModel model;
final Function(bool checked) onTap;
final Function() onChange;
final double topOffset;
FilterArea({this.model, this.onTap, this.topOffset, this.onChange});
@override
State<StatefulWidget> createState() => _FilterAreaState();
}
class _FilterAreaState extends State<FilterArea> {
Function() _onChange;
@override
void initState() {
this._onChange = widget.onChange;
super.initState();
}
@override
Widget build(BuildContext context) {
return QMProvider<FilterModel>.value(
model: widget.model,
builderWidget: (context, model, child) {
if (model.isEmpty()) {
return Container(
height: 48.rpx,
);
}
String regionName = '';
if (model.getCheckedTwoRegion() != null &&
model.getCheckedTwoRegion().name != null) {
regionName = model.getCheckedTwoRegion().name;
} else if (model.getCheckedOneRegion() != null) {
regionName = model.getCheckedOneRegion().name;
}
return Container(
height: 48.rpx,
padding: EdgeInsets.symmetric(horizontal: 1.rpx),
color: rgba(245, 245, 245, 1),
transform: Matrix4.translationValues(0, -1, 0),
child: Row(
children: [
_FilterItem(
text: model.getCheckedOneCategory().name,
checked: model.getFilterAreaIndex() == 1,
onTap: (checked) {
if (widget.onTap != null) {
widget.onTap(checked);
}
if (checked) {
return;
}
model.stFilterAreaIndex(1);
FilterPanelUtils.show(
topOffset: widget.topOffset,
items: model.getCategorys(),
oneCheckedId: model.getCheckedOneCategory().id,
twoCheckedId: model.getCheckedTwoCategory().id,
context: context,
model: model,
onClose: () {
model.stFilterAreaIndex(0);
},
onChange:this._onChange,
onConfirm: (onwItem, twoItem) {
model.stFilterAreaIndex(0);
model.setCheckedCategory(onwItem, twoItem);
if(_onChange != null){
_onChange();
}
});
},
),
_FilterItem(
text: regionName,
checked: model.getFilterAreaIndex() == 2,
onTap: (checked) {
if (widget.onTap != null) {
widget.onTap(checked);
}
if (checked) {
return;
}
model.stFilterAreaIndex(2);
FilterPanelUtils.show(
topOffset: widget.topOffset,
items: model.getRegions(),
oneCheckedId: model.getCheckedOneRegion().id,
twoCheckedId: model.getCheckedTwoRegion().id,
context: context,
model: model,
onClose: () {
model.stFilterAreaIndex(0);
},
onChange:this._onChange,
onConfirm: (onwItem, twoItem) {
model.stFilterAreaIndex(0);
model.setCheckedRegion(onwItem, twoItem);
if(_onChange != null){
_onChange();
}
});
},
),
_FilterItem(
text: model.getCheckedOneSort().name,
checked: model.getFilterAreaIndex() == 3,
onTap: (checked) {
if (widget.onTap != null) {
widget.onTap(checked);
}
if (checked) {
return;
}
model.stFilterAreaIndex(3);
FilterPanelUtils.show(
topOffset: widget.topOffset,
items: model.getSorts(),
oneCheckedId: model.getCheckedOneSort().id,
twoCheckedId: model.getCheckedTwoSort().id,
context: context,
model: model,
onClose: () {
model.stFilterAreaIndex(0);
},
onChange:this._onChange,
onConfirm: (onwItem, twoItem) {
model.stFilterAreaIndex(0);
model.setCheckedSort(onwItem, twoItem);
if(_onChange != null){
_onChange();
}
});
},
),
],
),
);
},
);
}
}
class _FilterItem extends StatelessWidget {
final bool checked;
final String text;
final Function(bool checked) onTap;
_FilterItem({this.text, this.onTap, this.checked = false});
@override
Widget build(BuildContext context) {
Color textColor = rgba(48, 38, 0, 1);
Color iconColor = rgba(151, 151, 151, 1);
if (checked) {
textColor = rgba(255, 128, 0, 1);
iconColor = textColor;
}
String clipText = text;
if (clipText.length > 5) {
clipText = "${text.substring(0, 5)}...";
}
return Expanded(
child: GestureDetector(
onTap: () {
onTap(checked);
},
child: Container(
height: 48.rpx,
padding: EdgeInsets.only(
left: 6.rpx,
right: 6.rpx,
bottom: checked ? 0 : 8.rpx,
top: 12.rpx),
child: Container(
padding: EdgeInsets.only(
bottom: checked ? 8.rpx : 0,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(4.rpx),
topRight: Radius.circular(4.rpx),
bottomLeft: Radius.circular(checked ? 0 : 4.rpx),
bottomRight: Radius.circular(checked ? 0 : 4.rpx))),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
child: Text(
clipText,
overflow: TextOverflow.clip,
style: TextStyle(
color: textColor, fontSize: 14.rpx, height: 1.rpx),
),
),
Icon(checked ? Icons.arrow_drop_up : Icons.arrow_drop_down,
size: 13.rpx, color: iconColor)
],
),
),
),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_module/components/filter_area.dart';
import 'package:life_repository/life_repository.dart';
import 'package:life_module/models/filter_model.dart';
class FilterPanel extends StatelessWidget {
final FilterModel model;
final List<FilterItemEntity> items;
final String oneCheckedId;
final String twoCheckedId;
final Function() onClose;
final double topOffset;
final Function() onChange;
final Function(FilterItemEntity onwItem, FilterItemEntity twoItem) onConfirm;
FilterPanel(
{this.items,
this.model,
this.oneCheckedId,
this.twoCheckedId,
this.onClose,
this.topOffset,
this.onConfirm,
this.onChange,
});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.transparent,
body: SafeArea(
bottom: false,
child: Column(
children: [
GestureDetector(
onTap: onClose,
child: Container(
color: Colors.transparent,
height: 44.rpx,
),
),
FilterArea(
model: model,
onTap: (checked) {
onClose();
},
onChange:onChange,
),
Expanded(
child: Container(
color: rgba(0, 0, 0, 0.4),
child: Column(
children: [
_Body(
list: items,
oneCheckedId: oneCheckedId,
twoCheckedId: twoCheckedId,
onConfirm: onConfirm,
),
Expanded(
child: GestureDetector(
onTap: onClose,
child: Container(
color: Colors.transparent,
)),
)
],
),
),
),
],
),
),
);
}
}
class _Body extends StatefulWidget {
final String oneCheckedId;
final String twoCheckedId;
final List<FilterItemEntity> list;
final Function(FilterItemEntity onwItem, FilterItemEntity twoItem) onConfirm;
_Body({this.list, this.oneCheckedId, this.twoCheckedId, this.onConfirm});
@override
State<StatefulWidget> createState() => _BodyState();
}
class _BodyState extends State<_Body> {
String oneCheckedId = '';
String twoCheckedId = '';
FilterItemEntity onwItem;
FilterItemEntity twoItem;
List<FilterItemEntity> get list => widget.list;
List<FilterItemEntity> get twoList {
FilterItemEntity item = list.firstWhere((item) => item.id == oneCheckedId);
if (item != null && item.subs.length > 0) {
return item.subs;
}
return [];
}
// 是否需要二级
bool get isTwoLevel {
for (int i = 0, len = list.length; i < len; i++) {
FilterItemEntity item = list[i];
if (item.subs != null && item.subs.length > 0) {
return true;
}
}
return false;
}
@override
void initState() {
super.initState();
oneCheckedId = widget.oneCheckedId;
twoCheckedId = widget.twoCheckedId;
onwItem = list.firstWhere((item) => item.id == oneCheckedId);
if (twoList != null && twoList.length > 0) {
twoItem = twoList.firstWhere((item) => item.id == twoCheckedId);
}
}
@override
Widget build(BuildContext context) {
return Expanded(
child: Container(
child: Container(
height: 342.rpx,
transform: Matrix4.translationValues(0, -1.rpx, 0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius:
BorderRadius.vertical(bottom: Radius.circular(12.rpx))),
child: Container(
child: Column(
children: [
Builder(
builder: (context) {
if (!isTwoLevel) {
return _SortLevel(
list: list,
checkedId: oneCheckedId,
onTap: (item) {
if (oneCheckedId == item.id) {
return;
}
onwItem = item;
setState(() {
oneCheckedId = item.id;
twoCheckedId = '';
});
},
);
}
return Expanded(
child: Row(
children: [
_OneLevel(
list: list,
checkedId: oneCheckedId,
onTap: (item) {
if (oneCheckedId == item.id) {
return;
}
onwItem = item;
if (onwItem.subs != null &&
onwItem.subs.length > 0) {
twoItem = onwItem.subs[0];
} else {
twoItem = FilterItemEntity(null, null, []);
}
setState(() {
oneCheckedId = item.id;
twoCheckedId = twoItem.id;
});
},
),
_TwoLevel(
list: twoList,
checkedId: twoCheckedId,
onTap: (item) {
if (twoCheckedId == item.id) {
return;
}
twoItem = item;
setState(() {
twoCheckedId = item.id;
});
},
)
],
));
},
),
_Fotter(
onReset: () {
FilterItemEntity onwItem = list[0];
FilterItemEntity twoItem;
if (onwItem.subs != null && onwItem.subs.length > 0) {
twoItem = onwItem.subs[0];
} else {
twoItem = FilterItemEntity(null, null, []);
}
widget.onConfirm(onwItem, twoItem);
},
onConfirm: () {
widget.onConfirm(onwItem, twoItem);
},
),
],
),
),
),
),
);
}
}
/// 一级选择列表
class _OneLevel extends StatelessWidget {
final String checkedId;
final List<FilterItemEntity> list;
final Function(FilterItemEntity item) onTap;
_OneLevel({this.list, this.checkedId, this.onTap});
@override
Widget build(BuildContext context) {
int len = list.length;
return Container(
width: 94.rpx,
child: ListView.builder(
itemCount: len,
physics: ClampingScrollPhysics(),
itemBuilder: (c, i) {
// 上一个索引
int preIndex = i - 1;
// 下一个索引
int nextIndex = i + 1;
FilterItemEntity item = list[i];
bool checked = item.id == checkedId;
bool preChecked = preIndex > -1 && list[preIndex].id == checkedId;
bool nextChecked =
nextIndex < len - 1 && list[nextIndex].id == checkedId;
return _OneLevelTextItem(
text: item.name,
checked: checked,
nextChecked: nextChecked,
preChecked: preChecked,
onTap: () {
onTap(item);
},
);
}),
);
}
}
/// 二级选择列表
class _TwoLevel extends StatelessWidget {
final String checkedId;
final List<FilterItemEntity> list;
final Function(FilterItemEntity item) onTap;
_TwoLevel({this.list, this.checkedId, this.onTap});
@override
Widget build(BuildContext context) {
if (list == null || list.length == 0) {
return Container();
}
return Expanded(
child: GridView.builder(
physics: ClampingScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, childAspectRatio: 3.1818181818181817),
itemBuilder: (context, i) {
FilterItemEntity item = list[i];
bool checked = item.id == checkedId;
return _TextItem(
text: item.name,
checked: checked,
onTap: () {
onTap(item);
},
);
},
itemCount: list.length,
),
);
}
}
/// 排序选择列表
class _SortLevel extends StatelessWidget {
final String checkedId;
final List<FilterItemEntity> list;
final Function(FilterItemEntity item) onTap;
_SortLevel({this.list, this.checkedId, this.onTap});
@override
Widget build(BuildContext context) {
List<Widget> childs = [];
List<Widget> rows = [];
for (int i = 0, len = list.length; i < len; i++) {
FilterItemEntity item = list[i];
bool checked = item.id == checkedId;
Widget widget = _TextItem(
text: item.name,
checked: checked,
onTap: () {
onTap(item);
},
);
rows.add(Container(
width: 95.rpx,
child: widget,
));
if (rows.length == 2 || i == len - 1) {
childs.add(Row(
children: rows,
));
rows = [];
}
}
return Expanded(
child: SingleChildScrollView(
child: Column(
children: childs,
),
),
);
}
}
/// 一级选择文字
class _OneLevelTextItem extends StatelessWidget {
final String text;
final bool checked;
final bool preChecked;
final bool nextChecked;
final Function() onTap;
_OneLevelTextItem(
{this.text,
this.onTap,
this.checked = false,
this.preChecked = false,
this.nextChecked = false});
@override
Widget build(BuildContext context) {
Radius radius = Radius.circular(8.rpx);
Color color = rgba(245, 245, 245, 1);
if (checked) {
color = Colors.transparent;
}
return Container(
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.only(
topRight: preChecked ? radius : Radius.zero,
bottomRight: nextChecked ? radius : Radius.zero,
)),
child: _TextItem(
text: text,
checked: checked,
onTap: onTap,
),
);
}
}
/// 二级选择文字
class _TextItem extends StatelessWidget {
final String text;
final bool checked;
final Function() onTap;
_TextItem({this.text, this.onTap, this.checked = false});
@override
Widget build(BuildContext context) {
Color color = rgba(102, 102, 102, 1);
if (checked) {
color = rgba(255, 128, 0, 1);
}
return GestureDetector(
onTap: onTap,
behavior: HitTestBehavior.translucent,
child: Container(
height: 44.rpx,
alignment: Alignment.center,
child: Text(
text,
style: TextStyle(color: color, fontSize: 13.rpx),
),
));
}
}
/// 底部操作按钮
class _Fotter extends StatelessWidget {
final Function() onReset;
final Function() onConfirm;
_Fotter({this.onReset, this.onConfirm});
@override
Widget build(BuildContext context) {
return Container(
height: 56.rpx,
padding: EdgeInsets.symmetric(horizontal: 13.rpx),
child: Row(
children: [
GestureDetector(
onTap: onReset,
child: Container(
width: 82.rpx,
height: 40.rpx,
alignment: Alignment.center,
decoration: BoxDecoration(
color: rgba(245, 245, 245, 1),
borderRadius: BorderRadius.circular(8.rpx)),
child: Text(
"重置",
style: TextStyle(
color: rgba(51, 51, 51, 1),
fontSize: 15.rpx,
fontWeight: FontWeight.bold,
height: 1.rpx),
),
)),
SizedBox(
width: 12.rpx,
),
Expanded(
child: GestureDetector(
onTap: onConfirm,
child: Container(
height: 40.rpx,
alignment: Alignment.center,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [rgba(255, 187, 0, 1), rgba(255, 187, 0, 1)]),
borderRadius: BorderRadius.circular(8.rpx)),
child: Text(
"确认",
style: TextStyle(
color: rgba(51, 51, 51, 1),
fontSize: 15.rpx,
fontWeight: FontWeight.bold,
height: 1.rpx),
),
),
),
)
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_module/components/type_icon.dart';
import 'package:life_repository/life_repository.dart';
import 'package:common_module/widget/xiaoxiong_base_image_widget/widget.dart';
class GoodsItem extends StatelessWidget {
final ComboItem item;
final Function() onTap;
GoodsItem({this.item, this.onTap});
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Container(
height: 98.rpx,
margin: EdgeInsets.only(left: 13.rpx, right: 13.rpx, bottom: 8.rpx),
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(8.rpx)),
child: Row(
children: [
SizedBox(
width: 12.rpx,
),
ClipRRect(
borderRadius: BorderRadius.circular(8.rpx),
child: Container(
height: 74.rpx,
width: 74.rpx,
child: XiaoxiongBaseImageWidget(
imageUrl: item.shopImg,
height: 74.rpx,
width: 74.rpx,
),
)),
SizedBox(
width: 10.rpx,
),
Expanded(
child: Container(
height: 74.rpx,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_Title(item),
_PriceInfo(item),
_LocaionInfo(item)
],
),
),
),
SizedBox(
width: 12.rpx,
),
],
),
),
);
}
}
//、 标题
class _Title extends StatelessWidget {
final ComboItem item;
_Title(this.item);
@override
Widget build(BuildContext context) {
return Container(
height: 22.rpx,
child: Row(
children: [
TypeIcon(
type: int.parse(item.dealType),
),
SizedBox(
width: 5.rpx,
),
Expanded(
child: Container(
child: Text(
item.itemTitle,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(
fontSize: 15.rpx, fontWeight: FontWeight.bold, height: 1.rpx),
),
))
],
),
);
}
}
/// 价格信息
class _PriceInfo extends StatelessWidget {
final ComboItem item;
_PriceInfo(this.item);
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(top: 6.rpx),
height: 19.rpx,
child: Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
children: [
Text(
"¥",
style: TextStyle(
color: rgba(255, 4, 0, 1),
fontSize: 11.rpx,
),
),
Text(
item.endPrice,
style: TextStyle(
color: rgba(255, 4, 0, 1),
fontSize: 16.rpx,
),
),
SizedBox(
width: 4.rpx,
),
Text(
${item.originalPrice}",
style: TextStyle(
color: rgba(153, 153, 153, 1),
fontSize: 12.rpx,
decoration: TextDecoration.lineThrough),
),
SizedBox(
width: 12.rpx,
),
Container(
padding: EdgeInsets.symmetric(horizontal: 4.rpx, vertical: 2.rpx),
decoration: BoxDecoration(
border: Border.all(color: rgba(255, 128, 0, 1), width: 0.5),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(9.rpx),
topRight: Radius.circular(9.rpx),
bottomRight: Radius.circular(9.rpx))),
child: Text(
item.userCommissionText,
style: TextStyle(
color: rgba(255, 128, 0, 1), fontSize: 11.rpx, height: 1.2),
),
),
],
),
);
}
}
/// 定位信息
class _LocaionInfo extends StatelessWidget {
final ComboItem item;
_LocaionInfo(this.item);
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(top: 9.rpx),
height: 18.rpx,
width: double.infinity,
child: Row(
children: [
Container(
constraints: BoxConstraints(maxWidth: 190.rpx),
child: Text(
item.shopTitle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: rgba(102, 102, 102, 1), fontSize: 11.rpx, height: 1.2),
),
),
SizedBox(
width: 2.rpx,
),
/*LifeImage(
name: "assets/images/life/ic_miaosha_arrow_right.png",
width: 8.rpx,
height: 8.rpx,
),*/
Spacer(),
Text(
item.distance,
style: TextStyle(
color: rgba(102, 102, 102, 1), fontSize: 11.rpx, height: 1.2),
),
],
),
);
}
}
import 'package:flutter/material.dart';
class LifeImage extends StatelessWidget {
final String name;
final double width;
final double height;
final Color color;
final BoxFit fit;
LifeImage({
Key key,
this.name,
this.width,
this.height,
this.color,
this.fit,
});
@override
Widget build(BuildContext context) {
return Image.asset(
name,
key: key,
width: width,
height: height,
color: color,
fit: fit,
package: "life_module",
);
}
}
export 'package:common_module/delegates/persistent_header_delegate.dart';
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
class SearchBar extends StatefulWidget {
final Color inputColor;
final bool enabled;
final FocusNode focusNode;
final Function(String) onSubmitted;
final Function(String) onChanged;
final TextEditingController controller;
SearchBar(
{this.enabled = false,
this.inputColor,
this.focusNode,
this.onChanged,
this.onSubmitted,
this.controller});
@override
State<StatefulWidget> createState() => _SearchBarState();
}
class _SearchBarState extends State<SearchBar> {
@override
Widget build(BuildContext context) {
return Container(
height: 34.rpx,
alignment: Alignment.centerLeft,
decoration: BoxDecoration(
color: widget.inputColor,
borderRadius: BorderRadius.circular(17.rpx),
border: Border.all(color: rgba(255, 128, 0, 1), width: 1)),
child: Row(
children: [
_TextField(
enabled: widget.enabled,
onChanged: widget.onChanged,
onSubmitted: widget.onSubmitted,
focusNode: widget.focusNode,
controller: widget.controller),
!widget.enabled
? _Button()
: InkWell(
child: _Button(),
onTap: () {
widget.onSubmitted(widget.controller.text);
},
),
SizedBox(
width: 3.rpx,
)
],
),
);
}
}
class _TextField extends StatefulWidget {
final bool enabled;
final FocusNode focusNode;
final Function(String) onSubmitted;
final Function(String) onChanged;
final TextEditingController controller;
_TextField(
{this.enabled,
this.focusNode,
this.onChanged,
this.onSubmitted,
this.controller});
@override
State<StatefulWidget> createState() => _TextFieldState();
}
/// 输入框
class _TextFieldState extends State<_TextField> {
bool showCloseBtn = false;
@override
void initState() {
super.initState();
if (widget.controller != null) {
widget.controller.addListener(() {
setState(() {
showCloseBtn = widget.controller.text.isNotEmpty;
});
});
}
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Expanded(
child: Container(
height: 34.rpx,
alignment: Alignment.centerLeft,
child: Stack(
children: [
Container(
height: 34.rpx,
padding: EdgeInsets.only(right: 29.rpx),
child: TextField(
enabled: widget.enabled,
focusNode: widget.focusNode,
controller: widget.controller,
onSubmitted: widget.onSubmitted,
onChanged: widget.onChanged,
textAlignVertical: TextAlignVertical.center,
textInputAction: TextInputAction.search,
keyboardType: TextInputType.text,
style: TextStyle(color: rgba(48, 38, 0, 1), fontSize: 12.rpx),
decoration: InputDecoration(
fillColor: Colors.transparent,
prefixIconConstraints: BoxConstraints(maxWidth: 26.rpx),
prefixIcon: Container(
margin:
EdgeInsets.only(left: 8.rpx, right: 4.rpx, bottom: 2.rpx),
child: Image.asset("assets/ic_home_search.png",
width: 20.rpx, height: 20.rpx, fit: BoxFit.contain),
),
hintText: '搜索店铺,领隐藏优惠券省钱',
isDense: false,
hintStyle:
TextStyle(color: rgba(170, 170, 170, 1), fontSize: 12.rpx),
border: OutlineInputBorder(
borderSide: BorderSide(style: BorderStyle.none),
gapPadding: 0.0,
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(style: BorderStyle.none),
gapPadding: 0.0,
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide(style: BorderStyle.none),
gapPadding: 0.0,
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(style: BorderStyle.none),
gapPadding: 0.0,
),
disabledBorder: OutlineInputBorder(
borderSide: BorderSide(style: BorderStyle.none),
gapPadding: 0.0,
),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(style: BorderStyle.none),
gapPadding: 0.0,
),
contentPadding: EdgeInsets.all(0),
),
),
),
Builder(
builder: (context) {
if (!showCloseBtn) {
return SizedBox();
}
return Positioned(
right: 0,
top: 0,
child: InkWell(
onTap: () {
widget.onChanged("");
widget.onSubmitted("");
widget.controller.text = '';
widget.focusNode.requestFocus();
},
child: Container(
width: 34.rpx,
height: 34.rpx,
alignment: Alignment.center,
child: Image.asset(
"assets/close_btn_black.png",
width: 14.rpx,
height: 14.rpx,
),
),
),
);
},
)
],
),
));
}
}
/// 搜索按钮
class _Button extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 46.rpx,
height: 28.rpx,
alignment: Alignment.center,
decoration: BoxDecoration(
color: rgba(255, 128, 0, 1),
borderRadius: BorderRadius.circular(17.rpx)),
child: Text(
'搜索',
style: TextStyle(color: rgba(255, 255, 255, 1), fontSize: 13.rpx),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:navigation_bar/navigation_bar.dart';
import 'package:mvp/mvp.dart';
import 'package:life_module/models/filter_model.dart';
import 'package:life_repository/life_repository.dart';
class ShopClassNav extends StatefulWidget {
final FilterModel model;
final Function() onChange;
ShopClassNav({this.model, this.onChange});
@override
State<StatefulWidget> createState() => _ShopClassNavState();
}
class _ShopClassNavState extends State<ShopClassNav>
with TickerProviderStateMixin {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return QMProvider<FilterModel>.value(
model: widget.model,
builderWidget: (context, model, child) {
if (model.isEmpty()) {
return Container(
height: 40.rpx,
);
}
List<FilterItemEntity> subs = model.getCheckedOneCategory().subs;
return Container(
key: ValueKey(subs.map((e) => e.id).join('_')),
height: 40.rpx,
color: rgba(245, 245, 245, 1),
transform: Matrix4.translationValues(0, -1.5, 0),
child: NotificationListener(
child: NavigationBar(
controller: model.getShopClassNavTabController(),
items: subs.map((e) => e.name).toList(),
selectColor: rgba(255, 128, 0, 1),
normalColor: rgba(102, 102, 102, 1),
selectStyle: TextStyle(
fontSize: 13.rpx, height: 1, fontWeight: FontWeight.bold),
normalStyle: TextStyle(
fontSize: 13.rpx,
height: 1,
),
isScrollable: true,
onChange: (index) {
if (subs[index] == model.getCheckedTwoCategory()) {
return;
}
model.setCheckedCategory(
model.getCheckedOneCategory(), subs[index]);
widget.onChange();
},
indicatorSize: TabBarIndicatorSize.label,
indicator: RoundRectIndicator(
color: Colors.transparent, height: 0, marginBottom: 0),
),
onNotification: (e) {
return true;
},
),
);
});
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_module/components/type_icon.dart';
import 'package:life_repository/life_repository.dart';
import 'package:common_module/widget/xiaoxiong_base_image_widget/widget.dart';
class ShopItem extends StatelessWidget {
final RestaurantItem item;
final Function() onTap;
ShopItem({this.item, this.onTap});
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Container(
margin: EdgeInsets.only(left: 13.rpx, right: 13.rpx, bottom: 8.rpx),
padding: EdgeInsets.symmetric(vertical: 12.rpx),
alignment: Alignment.topLeft,
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(8.rpx)),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 12.rpx,
),
ClipRRect(
borderRadius: BorderRadius.circular(8.rpx),
child: Container(
height: 74.rpx,
width: 74.rpx,
child: XiaoxiongBaseImageWidget(
imageUrl: item.shopImg,
height: 74.rpx,
width: 74.rpx,
),
)),
SizedBox(
width: 10.rpx,
),
Expanded(
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_Title(item),
_ScoreInfo(item),
_LocaionInfo(item),
Container(
height: 0.5.rpx,
color: rgba(151, 151, 151, 0.1),
),
_GoodList(item)
],
),
),
),
SizedBox(
width: 12.rpx,
),
],
),
),
);
}
}
/// 标题
class _Title extends StatelessWidget {
final RestaurantItem item;
_Title(this.item);
@override
Widget build(BuildContext context) {
return Container(
height: 22.rpx,
child: Row(
children: [
Expanded(
child: Container(
child: Text(
item.shopTitle,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(
fontSize: 15.rpx, fontWeight: FontWeight.bold, height: 1.rpx),
),
))
],
),
);
}
}
/// 评分和销量信息
class _ScoreInfo extends StatelessWidget {
final RestaurantItem item;
_ScoreInfo(this.item);
@override
Widget build(BuildContext context) {
var showStarScore = item.showStarScore;
List<InlineSpan> widgets = [];
double score = double.parse(showStarScore);
for (int star = 1, len = score.ceil(); star <= len; star++) {
widgets.add(WidgetSpan(
child: Image.asset(
star <= score
? "assets/ic_star_small_full.png"
: "assets/ic_star_small_half.png",
width: 12.rpx,
height: 16.rpx,
)));
}
for (int star = widgets.length; star < 5; star++) {
widgets.add(WidgetSpan(
child: Image.asset(
"assets/ic_star_small_blank.png",
width: 12.rpx,
height: 16.rpx,
)));
}
widgets.add(WidgetSpan(
child: SizedBox(
width: 2.rpx,
)));
widgets.add(TextSpan(
text: double.parse(item.commentScore).toStringAsFixed(1),
style: TextStyle(
color: rgba(255, 128, 0, 1),
fontSize: 14.rpx,
fontWeight: FontWeight.bold)));
return Container(
height: 18.rpx,
margin: EdgeInsets.only(top: 4.rpx),
child: Row(
children: [
Container(
alignment: Alignment.centerLeft,
height: 14.rpx,
child: Text.rich(TextSpan(children: widgets)),
),
SizedBox(
width: 8.rpx,
),
Text(
" ",
style: TextStyle(color: rgba(102, 102, 102, 1), fontSize: 12.rpx),
),
Spacer(),
Text(
${item.avgPrice}/人",
style: TextStyle(color: rgba(102, 102, 102, 1), fontSize: 11.rpx),
)
],
),
);
}
}
/// 地址信息
class _LocaionInfo extends StatelessWidget {
final RestaurantItem item;
_LocaionInfo(this.item);
@override
Widget build(BuildContext context) {
return Container(
height: 18.rpx,
margin: EdgeInsets.only(top: 7.rpx, bottom: 8.rpx),
child: Row(
children: [
Text(
item.categoryName,
style: TextStyle(color: rgba(102, 102, 102, 1), fontSize: 11.rpx),
),
Spacer(),
Text(
"${item.regionName}${item.distance}",
style: TextStyle(color: rgba(102, 102, 102, 1), fontSize: 11.rpx),
)
],
),
);
}
}
class _GoodList extends StatelessWidget {
final RestaurantItem item;
_GoodList(this.item);
@override
Widget build(BuildContext context) {
List<Widget> items = item.itemList.map((e) {
return _GoodItem(e);
}).toList();
return Container(
child: Column(
children: items,
),
);
}
}
/// 代金券和团购单个信息
class _GoodItem extends StatelessWidget {
final ShopSubItemList item;
_GoodItem(this.item);
@override
Widget build(BuildContext context) {
return Container(
height: 16.rpx,
margin: EdgeInsets.only(top: 9.rpx),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
TypeIcon(type: int.parse(item.dealType)),
SizedBox(
width: 6.rpx,
),
_PriceInfo(item),
SizedBox(
width: 8.rpx,
),
Expanded(
child: Container(
child: Text(
item.itemTitle,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(color: rgba(51, 51, 51, 1), fontSize: 12.rpx),
),
),
)
],
),
);
}
}
/// 价格信息
class _PriceInfo extends StatelessWidget {
final ShopSubItemList item;
_PriceInfo(this.item);
@override
Widget build(BuildContext context) {
return Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
children: [
Text(
"¥",
style: TextStyle(
color: rgba(255, 4, 0, 1),
fontSize: 11.rpx,
),
),
Text(
item.endPrice,
style: TextStyle(
color: rgba(255, 4, 0, 1),
fontSize: 14.rpx,
),
),
SizedBox(
width: 8.rpx,
),
Text(
${item.originalPrice}",
style: TextStyle(
color: rgba(153, 153, 153, 1),
fontSize: 12.rpx,
decoration: TextDecoration.lineThrough),
),
],
),
);
}
}
import 'package:flutter/material.dart';
import './shop_item.dart';
import './goods_item.dart';
import '../models/shop_list_model.dart';
import 'package:mvp/mvp.dart';
import 'package:life_repository/life_repository.dart';
import 'package:common_module/utils/list_model_status_utils.dart';
import 'package:common_module/utils/xapp_utils.dart';
class ShopList extends StatelessWidget {
final ShopListModel model;
final Function(ShopItemEntity) onTap;
final Function() onReset;
ShopList({this.model, this.onTap, this.onReset});
@override
Widget build(BuildContext context) {
return SliverPadding(
padding: EdgeInsets.zero,
sliver: QMProvider<ShopListModel>.value(
model: model,
builderWidget: (context, model, child) {
Widget statusWidget = ListModelStatusUtils.getInstance().form(
failText: "没有搜到结果~",
btnText: "恢复默认",
paddingTop: 30.rpx,
iconSize: 200.rpx,
btnRadius: 40.rpx,
btnColor: Colors.black,
icon: "assets/life_img_no_location.png",
model: model,
onTap: onReset);
if (statusWidget != null) {
return SliverFillViewport(
delegate: SliverChildBuilderDelegate((context, index) {
return Container(
alignment: Alignment.topCenter,
child: statusWidget,
);
}, childCount: 1),
viewportFraction: 1.0,
);
}
return SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
var item = model.getList()[index];
if (item.type == 0) {
return ShopItem(
item: item.restaurant,
onTap: () {
onTap(item);
},
);
}
return GoodsItem(
item: item.combo,
onTap: () {
onTap(item);
},
);
},
childCount: model.getList().length,
));
},
),
);
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:navigation_bar/navigation_bar.dart';
import 'package:life_module/models/filter_model.dart';
import 'package:mvp/mvp.dart';
class ShopTypeNav extends StatelessWidget {
final FilterModel model;
final Function() onChange;
ShopTypeNav({this.model, this.onChange});
@override
Widget build(BuildContext context) {
return QMProvider<FilterModel>.value(
model: model,
builderWidget: (context, model, child) {
if (model.isEmpty()) {
return Container(
height: 37.rpx,
);
}
return Container(
height: 37.rpx,
alignment: Alignment.topLeft,
padding: EdgeInsets.only(top: 12.rpx),
color: rgba(245, 245, 245, 1),
child: DefaultTabController(
length: 2,
initialIndex: model.getShopTypeNavIndex(),
child: NavigationBar(
items: ["优惠店铺", "优惠套餐"],
selectColor: rgba(48, 38, 0, 1),
normalColor: rgba(48, 38, 0, 1),
selectStyle: TextStyle(
fontWeight: FontWeight.bold, fontSize: 18.rpx, height: 1),
normalStyle: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14.rpx,
height: 1,
),
isScrollable: true,
indicatorSize: TabBarIndicatorSize.label,
indicator: RoundRectIndicator(
round: 3.rpx,
color: rgba(255, 61, 0, 1),
height: 3.rpx,
marginBottom: 6.rpx),
onChange: (index) {
model.setShopTypeNavIndex(index);
onChange();
},
),
),
);
},
);
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
/// 券和团icon
class TypeIcon extends StatelessWidget {
final int type;
TypeIcon({this.type = 1});
@override
Widget build(BuildContext context) {
String text = type == 1 ? '团' : '券';
List<Color> colors = type == 1
? [rgba(255, 125, 0, 1), rgba(250, 18, 14, 1)]
: [rgba(255, 201, 0, 1), rgba(255, 143, 0, 1)];
return Container(
height: 16.rpx,
width: 16.rpx,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4.rpx),
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: colors)),
child: Text(
text,
style: TextStyle(color: Colors.white, fontSize: 11.rpx, height: 1.rpx),
),
);
}
}
library life;
export './views/life/view.dart';
export './views/city/view.dart';
export './views/city/city.dart';
export './views/shop/view.dart';
export './views/sec_kill/view.dart';
export './views/search/view.dart';
import 'package:flutter/material.dart';
import 'package:mvp/mvp.dart';
import 'package:life_repository/life_repository.dart';
class FilterModel extends BaseModel {
List<FilterItemEntity> _regions = [];
List<FilterItemEntity> _categorys = [];
List<FilterItemEntity> _sorts = [];
FilterItemEntity _checkedOneRegion;
FilterItemEntity _checkedOneCategory;
FilterItemEntity _checkedOneSort;
FilterItemEntity _checkedTwoRegion;
FilterItemEntity _checkedTwoCategory;
FilterItemEntity _checkedTwoSort;
// 筛选区域选中的索引
int _filterAreaIndex = 0;
// 二级类目控制器
TabController _shopClassNavTabController;
int _shopTypeNavIndex = 0;
// 搜索关键词
String _keywords;
Future<void> getFilterList(String cid) async {
FilterObject object = await FilterArepository.get().getFilterList(cid);
_regions = object.regions;
_categorys = object.categorys;
_sorts = object.sorts;
if (_regions.length > 0) {
_checkedOneRegion = _regions[0];
if (_checkedOneRegion.subs.length > 0) {
_checkedTwoRegion = _checkedOneRegion.subs[0];
}
}
if (_categorys.length > 0) {
_checkedOneCategory = _categorys[0];
if (_checkedOneCategory.subs.length > 0) {
_checkedTwoCategory = _checkedOneCategory.subs[0];
_shopClassNavTabController = TabController(
length: _checkedOneCategory.subs.length, vsync: ScrollableState());
}
}
if (_sorts.length > 0) {
_checkedOneSort = _sorts[0];
if (_checkedOneSort.subs.length > 0) {
_checkedTwoSort = _checkedOneSort.subs[0];
}
}
notifyListeners();
}
bool isEmpty() {
return _regions.length == 0 || _categorys.length == 0 || _sorts.length == 0;
}
int getFilterAreaIndex() {
return _filterAreaIndex;
}
void stFilterAreaIndex(int index) {
_filterAreaIndex = index;
notifyListeners();
}
FilterItemEntity getCheckedOneRegion() {
return _checkedOneRegion;
}
void setCheckedRegion(FilterItemEntity r1, FilterItemEntity r2) {
_checkedOneRegion = r1;
_checkedTwoRegion = r2;
notifyListeners();
}
FilterItemEntity getCheckedOneCategory() {
return _checkedOneCategory;
}
void setCheckedCategory(FilterItemEntity c1, FilterItemEntity c2) {
var oldCheckedOneCategory = _checkedOneCategory;
_checkedOneCategory = c1;
_checkedTwoCategory = c2;
int index = 0;
if (c2 != null && c1.subs.length > 0) {
index = c1.subs.indexWhere((item) => item.id == c2.id);
}
if (c1 != oldCheckedOneCategory) {
int oIndex = 0;
// 解决初始化为0无法自动滚回初始位置问题
if (index == 0) {
oIndex = 1;
}
_shopClassNavTabController = TabController(
length: c1.subs.length,
initialIndex: oIndex,
vsync: ScrollableState());
notifyListeners();
Future.delayed(Duration(milliseconds: 300)).then((value) {
_shopClassNavTabController.animateTo(index);
});
} else {
_shopClassNavTabController.animateTo(index);
}
}
void resetShopClassNavController() {}
FilterItemEntity getCheckedOneSort() {
return _checkedOneSort;
}
void setCheckedSort(FilterItemEntity s1, FilterItemEntity s2) {
_checkedOneSort = s1;
_checkedTwoSort = s2;
notifyListeners();
}
FilterItemEntity getCheckedTwoRegion() {
if (_checkedTwoRegion == null) {
return FilterItemEntity(null, null, []);
}
return _checkedTwoRegion;
}
FilterItemEntity getCheckedTwoCategory() {
return _checkedTwoCategory;
}
FilterItemEntity getCheckedTwoSort() {
if (_checkedTwoSort == null) {
return FilterItemEntity(null, null, []);
}
return _checkedTwoSort;
}
List<FilterItemEntity> getRegions() {
return _regions;
}
List<FilterItemEntity> getCategorys() {
return _categorys;
}
List<FilterItemEntity> getSorts() {
return _sorts;
}
TabController getShopClassNavTabController() {
return _shopClassNavTabController;
}
int getShopTypeNavIndex() {
return _shopTypeNavIndex;
}
void setShopTypeNavIndex(int index) {
_shopTypeNavIndex = index;
}
String getKeywords() {
return _keywords;
}
void setKeywords(String s) {
_keywords = s;
}
void reset() {
if (_regions.length > 0) {
FilterItemEntity onwItem = _regions[0];
FilterItemEntity twoItem;
if (onwItem.subs != null && onwItem.subs.length > 0) {
twoItem = onwItem.subs[0];
} else {
twoItem = FilterItemEntity(null, null, []);
}
setCheckedRegion(onwItem, twoItem);
}
if (_categorys.length > 0) {
FilterItemEntity onwItem = _categorys[0];
FilterItemEntity twoItem;
if (onwItem.subs != null && onwItem.subs.length > 0) {
twoItem = onwItem.subs[0];
} else {
twoItem = FilterItemEntity(null, null, []);
}
setCheckedCategory(onwItem, twoItem);
}
if (_sorts.length > 0) {
FilterItemEntity onwItem = _sorts[0];
FilterItemEntity twoItem;
if (onwItem.subs != null && onwItem.subs.length > 0) {
twoItem = onwItem.subs[0];
} else {
twoItem = FilterItemEntity(null, null, []);
}
setCheckedSort(onwItem, twoItem);
}
}
}
import 'package:mvp/mvp.dart';
import 'package:life_repository/life_repository.dart';
class ShopListModel extends BaseListModel<ShopItemEntity> {
String _cityId;
String _lat;
String _lng;
String _firstCateId;
String _keywords = '';
String _secondCateId;
String _regionId;
String _sortType;
// 0 优惠店铺,1 优惠套餐
int _shopTypeNavIndex = 0;
@override
Future<List<ShopItemEntity>> request() {
return _find();
}
Future<List<ShopItemEntity>> _find() {
if (_shopTypeNavIndex == 0) {
return ShopListRepository.get().findRestaurantList(
_cityId,
_lat,
_lng,
getPage().toString(),
_firstCateId,
_keywords,
_secondCateId,
_regionId,
_sortType);
}
return ShopListRepository.get().findComboList(
_cityId,
_lat,
_lng,
getPage().toString(),
_firstCateId,
_keywords,
_secondCateId,
_regionId,
_sortType);
}
void setCityId(String cid) {
_cityId = cid;
}
void setLat(String lat) {
_lat = lat;
}
void setLng(String lng) {
_lng = lng;
}
void setFirstCateId(String firstCateId) {
_firstCateId = firstCateId;
}
String getKeywords() {
return _keywords;
}
void setKeywords(String keywords) {
_keywords = keywords;
}
void setSecondCateId(String secondCateId) {
_secondCateId = secondCateId;
}
void setRegionId(String regionId) {
_regionId = regionId;
}
void setSortType(String sortType) {
_sortType = sortType;
}
void setShopTypeNavIndex(int index) {
_shopTypeNavIndex = index;
}
}
import 'package:flutter/foundation.dart';
const String LIFE_LOCATION_PAGE = "life_location_page"; // 生活定位选择地址
const String LIFE_SHOP_PAGE = "l_ife_shop_page"; // 生活店铺
///
/// 路由路径枚举管理
///
enum RouteCityPath {
//LOCATION_PAGE, //定位页面
LOCATION_CITY_PAGE, //定位中城市选择页面
LIFE_SEARCH_PAGE, // 搜索页面
LIFE_SEC_KILL_PAGE // 秒杀
}
///
/// 路由管理的扩展
///
extension RoutePathExt on RouteCityPath {
String get name => describeEnum(this);
String path() {
return name.replaceAll("_", "/");
}
}
import 'package:common_module/utils/amap_utils.dart';
import 'package:life_repository/repository/amap_repository.dart';
class Res {
///
/// 历史定位缓存信息key值
///
static String historyLocation = '_history_locations';
///
/// 高德地图Key
///
static String aWebMapKey = '9897a29ccc4b396fc7dc560c50973fae';
static String aAndroidMapKey = '565eee69e48dbbfe1eb408da0fc31256';
static String aIOSMapKey = 'e492fb8617d0e93332fe980a934778b0';
///
/// 城市数据
///
static String chinaCityData = 'packages/life_module/assets/data/china.json';
///
/// 城市的历史定位信息
///
static String cityHistoryLocation = 'city/history/location';
static String icArrowGray =
'packages/price_module/assets/images/ic_arrow_gray.png';
static String icGpsOrg = 'packages/life_module/assets/images/ic_gps_org.png';
///
/// 转换定位数据
/// [entity] 当前获取到的定位数据
///
static Future<LocationEntity> convertLocation({LocationEntity entity,String location}) async{
dynamic idata = await AMapRepository.get().geocodeAddress(key: Res.aWebMapKey, location: location);
if(idata['status'] == '1' && idata['regeocode'] != null){
if(idata['regeocode'] != null){
idata = idata['regeocode'];
entity.address = idata['formatted_address'];
if(idata['addressComponent'] != null){
idata = idata['addressComponent'];
entity.city = idata['city'];
entity.aoiName = entity.address;
entity.poiName = entity.address;
}
}
}
return Future.value(entity);
}
}
import 'package:common_module/utils/store_utils.dart';
class SearchHistoryUtils {
static SearchHistoryUtils _instance;
static SearchHistoryUtils getInstance() {
if (_instance == null) {
_instance = new SearchHistoryUtils();
}
return _instance;
}
String key = "__SearchHistoryUtils__";
void push(String s) {
if (s == null || s.isEmpty) {
return;
}
var words = StoreUtils.getInstance().getForKey(
key: key,
defaultValue:[]
);
words = List<String>.from(words);
words.insert(0, s);
Set<String> uniWordes = words.toSet();
words = uniWordes.toList();
if (words.length > 20) {
words = words.sublist(0, 20);
}
StoreUtils.getInstance().setKV(key: key, value: words);
}
List<String> getWords() {
var words = StoreUtils.getInstance().getForKey(
key: key,
);
if (words == null) {
return [];
}
return List<String>.from(words);
}
void clear() {
StoreUtils.getInstance().setKV(key: key, value: []);
}
}
export 'package:common_module/utils/app_tap_config_utils.dart';
\ No newline at end of file
import 'package:flutter/material.dart';
import 'package:flutter_boost/boost_navigator.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_module/components/life_image.dart';
class BuyLoadingDialogUtils {
static BuyLoadingDialogUtils _instance;
static BuyLoadingDialogUtils getInstance() {
if (_instance == null) {
_instance = new BuyLoadingDialogUtils._();
}
return _instance;
}
BuyLoadingDialogUtils._();
LoadingDialog show() {
return LoadingDialog();
}
}
class LoadingDialog {
Route route;
LoadingDialog() {
route = _LoadingDialogRouter(_LoadingDialog());
BoostNavigator.instance.appState.topContainer.navigator.push(route);
}
void close() {
if (route != null && route.navigator != null) {
route.navigator.pop(route);
}
}
}
// loading widget
class _LoadingDialog extends Dialog {
const _LoadingDialog()
: super(
clipBehavior: Clip.none,
);
@override
Widget build(BuildContext context) {
return WillPopScope(
child: Material(
type: MaterialType.transparency,
child: Center(
child: Container(
width: 120.rpx,
height: 120.rpx,
decoration: BoxDecoration(
color: rgba(0, 0, 0, 0.6),
borderRadius: BorderRadius.circular(8),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 22.rpx, bottom: 10.rpx),
child: Container(
width: 30.rpx,
height: 30.rpx,
child: LifeImage(
name: "assets/images/shop/loading_icon.png",
width: 30.rpx,
height: 30.rpx,
),
),
),
Padding(
padding: EdgeInsets.only(left: 8.rpx, right: 8.rpx),
child: Container(
child: Text(
"吃喝玩乐前先来小熊领优惠",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 13.rpx,
height: 1.2.rpx),
overflow: TextOverflow.clip,
),
),
)
],
),
),
),
),
onWillPop: () async {
return false;
},
);
}
}
// loading路由
class _LoadingDialogRouter extends PageRouteBuilder {
final Widget page;
_LoadingDialogRouter(this.page)
: super(
opaque: false,
barrierColor: Color(0x00000001),
pageBuilder: (context, animation, secondaryAnimation) => page,
transitionsBuilder: (context, animation, secondaryAnimation, child) =>
child,
);
}
import 'package:flutter/material.dart';
import 'package:life_module/components/filter_panel.dart';
import 'package:life_repository/life_repository.dart';
import 'package:life_module/models/filter_model.dart';
class FilterPanelUtils {
static OverlayEntry overlayEntry;
static show({
@required BuildContext context,
@required FilterModel model,
@required List<FilterItemEntity> items,
@required String oneCheckedId,
Function() onChange,
double topOffset,
String twoCheckedId,
Function() onClose,
Function(FilterItemEntity onwItem, FilterItemEntity twoItem) onConfirm,
}) {
close();
OverlayState overlayState = Overlay.of(context);
overlayEntry = new OverlayEntry(builder: (context) {
return FilterPanel(
model: model,
items: items,
topOffset: topOffset,
oneCheckedId: oneCheckedId,
twoCheckedId: twoCheckedId,
onClose: () {
close();
onClose();
},
onConfirm: (onwItem, twoItem) {
close();
onConfirm(onwItem, twoItem);
},
onChange:onChange,
);
});
overlayState.insert(overlayEntry);
}
static void close() {
if (overlayEntry != null) {
overlayEntry.remove();
overlayEntry = null;
}
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_module/components/life_image.dart';
import 'package:life_repository/life_repository.dart';
import 'package:common_module/widget/image/index.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:flutter/cupertino.dart';
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'dart:ui' as ui;
import 'package:path_provider/path_provider.dart';
import 'package:common_module/utils/permission_utils.dart';
import 'package:modal/modal.dart';
import 'package:flutter_album_save/flutter_album_save.dart';
import 'package:flutter_native_toast/flutter_native_toast.dart';
import 'package:common_module/utils/system_share_utils.dart';
import 'package:common_module/utils/clipboard_utils.dart';
class ShareDialogUtils {
static ShareDialogUtils _instance;
static ShareDialogUtils getInstance() {
if (_instance == null) {
_instance = new ShareDialogUtils();
}
return _instance;
}
void show({BuildContext context, ShopInfo shopInfo, String firstCateId}) {
var route = _LoadingDialogRouter(_Dialog(shopInfo, firstCateId));
Navigator.of(context).push(route);
}
}
class _Dialog extends StatefulWidget {
final ShopInfo shopInfo;
final String firstCateId;
_Dialog(this.shopInfo, this.firstCateId);
@override
State<StatefulWidget> createState() => _DialogState();
}
class _DialogState extends State<_Dialog> {
List<_Item> items = [
_Item(0, "assets/download_icon.png", "保存图片"),
_Item(1, "assets/ic_life_copy.png", "复制文案"),
_Item(2, "assets/wechat2.png", "微信好友")
];
GlobalKey globalKey = GlobalKey();
String shareUrl;
ShopInfo get shopInfo => widget.shopInfo;
@override
void initState() {
super.initState();
getShareUrl();
}
void getShareUrl() async {
String url = await ShopInfoRepository.get()
.getShareUrl(widget.shopInfo.shopId, widget.firstCateId);
setState(() {
shareUrl = url;
});
}
@override
Widget build(BuildContext context) {
List<Widget> childs = items.map((e) {
return Expanded(
child: InkWell(
child: Container(
child: Column(
children: [
SizedBox(
height: 17.rpx,
),
Image.asset(e.icon, width: 45.rpx, height: 45.rpx),
SizedBox(
height: 11.rpx,
),
Text(
e.name,
style:
TextStyle(color: rgba(51, 51, 51, 1), fontSize: 12.rpx),
),
SizedBox(
height: 24.rpx,
),
],
),
),
onTap: () async {
if (shareUrl == null) {
FlutterNativeToast.showToast('请稍后');
return;
}
String copyText = '''店铺名称: ${shopInfo.shopTitle}
评分: ${shopInfo.commentScore}
人均: ${shopInfo.avgPrice}
地址: ${shopInfo.address}
——————————————————
$shareUrl
''';
ClipboardUtils.copyText(copyText);
if (e.id == 0 || e.id == 2) {
File file = await widgetToImage();
FlutterNativeToast.showToast('文案已复制');
if (e.id == 0) {
var _permission =
Platform.isIOS ? Permission.photos : Permission.storage;
var permission = await PermissionUtils.getInstance()
.has(permissions: [_permission]);
var status = permission[_permission];
if (status != PermissionUtilsStatus.granted) {
bool confirm = await Modal.showModal(
title: "温馨提示",
msg: "为给您提供更好的服务,使用此功能前需获取您的储存权限",
cancelBtnText: '拒绝',
confirmBtnText: '同意');
if (!confirm) {
return;
}
if (status == PermissionUtilsStatus.permanentlyDenied) {
PermissionUtils.getInstance().openAppSetting();
return;
}
permission = await PermissionUtils.getInstance()
.request(permissions: [_permission]);
status = permission[_permission];
if (status != PermissionUtilsStatus.granted) {
return;
}
}
await FlutterAlbumSave.saveImageToAlbum(file.path, null);
Navigator.of(context).pop();
} else {
SystemShareUtils.getInstance().shareImage(file.path);
}
} else {
Navigator.of(context).pop();
FlutterNativeToast.showToast('文案已复制');
}
},
),
);
}).toList();
return Scaffold(
backgroundColor: rgba(0, 0, 0, 0.3),
body: Column(
children: [
Spacer(),
Container(
alignment: Alignment.center,
child: RepaintBoundary(
key: globalKey,
child: ClipRRect(
borderRadius: BorderRadius.circular(8.rpx),
child: Container(
width: 243.rpx,
padding: EdgeInsets.all(10.rpx),
decoration: BoxDecoration(
color: Colors.white,
// borderRadius: BorderRadius.circular(12.rpx)
),
child: Column(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(8.rpx),
child: Container(
height: 222.rpx,
width: 222.rpx,
child: CachedNetworkImage(
imageUrl: widget.shopInfo.shopImg,
height: 222.rpx,
width: 222.rpx,
fit: BoxFit.cover,
),
)),
SizedBox(
height: 12.rpx,
),
Container(
alignment: Alignment.centerLeft,
child: Text(
widget.shopInfo.shopTitle,
style: TextStyle(
color: rgba(48, 38, 0, 1),
fontSize: 12.rpx,
height: 1.rpx,
fontWeight: FontWeight.bold),
),
),
SizedBox(
height: 20.rpx,
),
Container(
height: 84.rpx,
alignment: Alignment.topLeft,
child: Row(
children: [
Container(
width: 84.rpx,
height: 84.rpx,
alignment: Alignment.center,
decoration: BoxDecoration(
border: Border.all(
color: rgba(151, 151, 151, 0.5),
width: 0.5.rpx)),
child: Builder(
builder: (context) {
if (shareUrl == null) {
return CupertinoActivityIndicator(
radius: 12,
);
}
return QrImage(
data: shareUrl,
version: QrVersions.auto,
size: 84.rpx,
padding: EdgeInsets.zero,
);
},
),
),
SizedBox(
width: 12.rpx,
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 16.rpx,
child: Row(
children: [
LifeImage(
name:
"assets/images/share/ic_copy.png",
width: 16.rpx,
height: 16.rpx,
),
SizedBox(
width: 6.rpx,
),
Text(
"第一步:长按识别二维码",
style: TextStyle(
color: rgba(102, 102, 102, 1),
fontSize: 8.rpx),
)
],
),
),
SizedBox(
height: 8.rpx,
),
Container(
height: 16.rpx,
child: Row(
children: [
LifeImage(
name:
"assets/images/share/ic_copy.png",
width: 16.rpx,
height: 16.rpx,
),
SizedBox(
width: 6.rpx,
),
Text(
"第二步:查看优惠",
style: TextStyle(
color: rgba(102, 102, 102, 1),
fontSize: 8.rpx),
)
],
),
),
SizedBox(
height: 8.rpx,
),
Container(
height: 16.rpx,
child: Row(
children: [
LifeImage(
name:
"assets/images/share/ic_scan.png",
width: 16.rpx,
height: 16.rpx,
),
SizedBox(
width: 6.rpx,
),
Text(
"第三步:领券购买",
style: TextStyle(
color: rgba(102, 102, 102, 1),
fontSize: 8.rpx),
)
],
),
)
],
),
)
],
),
),
],
),
)),
)),
SizedBox(
height: 32.rpx,
),
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius:
BorderRadius.vertical(top: Radius.circular(16.rpx))),
child: Column(
children: [
Container(
child: Row(
children: childs,
),
),
SafeArea(
top: false,
child: InkWell(
onTap: () {
Navigator.of(context).pop();
},
child: Container(
height: 49.rpx,
alignment: Alignment.center,
child: Text(
"取消",
style: TextStyle(
color: rgba(102, 102, 102, 1),
fontSize: 15.rpx,
height: 1.rpx),
),
),
),
)
],
),
)
],
),
);
}
Future<File> widgetToImage() async {
Completer completer = Completer<File>();
RenderRepaintBoundary render = globalKey.currentContext.findRenderObject();
ui.Image image = await render.toImage(pixelRatio: 2.0);
ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
var imgData = byteData.buffer.asUint8List();
String sTempDir = (await getTemporaryDirectory()).path;
bool isDirExist = await Directory(sTempDir).exists();
if (!isDirExist) {
Directory(sTempDir).create();
}
var time = DateTime.now().millisecondsSinceEpoch;
File file = File(sTempDir + "/widget_image_$time.png");
file = await file.writeAsBytes(imgData,mode:FileMode.writeOnly,flush: true);
completer.complete(file);
return completer.future;
}
}
// loading路由
class _LoadingDialogRouter extends PageRouteBuilder {
final Widget page;
_LoadingDialogRouter(this.page)
: super(
opaque: false,
barrierColor: Color(0x00000001),
pageBuilder: (context, animation, secondaryAnimation) => page,
transitionsBuilder: (context, animation, secondaryAnimation, child) =>
child,
);
}
class _Item {
int id;
String icon;
String name;
_Item(this.id, this.icon, this.name);
}
import 'dart:convert';
import 'package:common_module/utils/navigate_utils.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:life_module/util/resource.dart';
import 'package:life_module/views/city/components/index.dart';
import 'package:azlistview/azlistview.dart';
import 'entity/city_model.dart';
class CityPage extends StatefulWidget {
final Map<String, dynamic> param;
const CityPage({Key key, this.param}) : super(key: key);
@override
_CityPageState createState() => _CityPageState();
}
class _CityPageState extends State<CityPage> {
List<CityModel> citysList = List<CityModel>.empty(growable: true);
ValueNotifier<SearchCityInfo> listenable =
ValueNotifier<SearchCityInfo>(null);
String _currentCity = '';
@override
void initState() {
super.initState();
if (widget.param != null && widget.param['currentCity'] != null) {
_currentCity = widget.param['currentCity'] as String;
}
if (_currentCity.isNotEmpty) {
citysList.add(
CityModel(
type: 0,
name: '当前城市',
tagIndex: '0',
citys: [_currentCity],
),
);
}
citysList.add(
CityModel(
type: 1,
name: '热门城市',
tagIndex: '1',
citys: ['北京', '上海', '广州', '深圳', '杭州', '南京', '重庆', '郑州', '西安', '成都'],
),
);
SearchCityInfo cityInfo = SearchCityInfo(citys: citysList, isSearch: false);
listenable.value = cityInfo;
rootBundle.loadString(Res.chinaCityData).then((value) {
List list = json.decode(value);
list.forEach((v) {
citysList.add(CityModel.fromJson(v));
});
}).whenComplete(() {
_handleList(list: citysList);
});
}
void _handleList({@required List<CityModel> list}) {
if (list.isEmpty) return;
for (int i = 0, length = list.length; i < length; i++) {
CityModel model = list[i];
if (model.type == -1) {
String pinyin = model.namePinyin;
String tag = pinyin.substring(0, 1).toUpperCase();
list[i].namePinyin = pinyin;
if (RegExp('[A-Z]').hasMatch(tag)) {
list[i].tagIndex = tag;
} else {
list[i].tagIndex = '#';
}
}
}
// A-Z sort.
SuspensionUtil.sortListBySuspensionTag(list);
// show sus tag.
SuspensionUtil.setShowSuspensionStatus(citysList);
SearchCityInfo cityInfo = SearchCityInfo(citys: citysList, isSearch: false);
listenable.value = cityInfo;
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFFF5F5F5),
appBar: buildAppBar(title: '选择城市',onPressed:(){
NavigateUtils.pop(arguments:{});
}),
body: _buildBody(),
);
}
TextEditingController _controller = TextEditingController();
Widget _buildBody() {
return Column(
children: [
//搜索框
buildSearchWidget(
hintText: '请输入城市名称',
onCityTap: () {},
controller: _controller,
onChanged: onChangedText,
),
Expanded(
child: buildAzListViewWidget(
notifier: listenable,
onCityTap: (String city) {
NavigateUtils.pop(arguments: {'city': city});
},
reEnteTap: () {
_controller.clear();
onChangedText('');
},
),
flex: 1,
),
],
);
}
void onChangedText(String text) {
Iterable<CityModel> citys = citysList
.where((ele) => (ele.type == 0 || ele.name.indexOf(text) > -1));
listenable.value = SearchCityInfo(
isSearch: text.isNotEmpty,
citys: List<CityModel>.from(citys),
keywords: text,
);
}
@override
void dispose() {
listenable.dispose();
_controller.dispose();
super.dispose();
}
}
import 'package:azlistview/azlistview.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:life_module/util/resource.dart';
import 'package:life_module/views/city/entity/city_model.dart';
import 'package:common_module/utils/xapp_utils.dart';
Widget buildAppBar({String title = '',void Function() onPressed}) {
return AppBar(
backgroundColor: Colors.white,
elevation: 0.1,
title: Text(
title,
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w500,
color: Color(0xFF333333),
),
),
leading: IconButton(
padding: EdgeInsets.all(5),
icon: ImageIcon(
AssetImage('assets/web_view_back_btn.png'),
size: 26,
),
onPressed:onPressed
),
);
}
///
/// 搜索布局
/// [city] 当前城市
/// [hintText] 提示信息
/// [onChanged] 搜索框文字发生改变
/// [onSubmitted] 点击提交
/// [onCityTap] 点击提交
///
Widget buildSearchWidget({
String city,
String hintText,
void Function(String) onChanged,
void Function(String) onSubmitted,
void Function() onCityTap,
void Function() onEnterCity,
bool enabled = true,
TextEditingController controller,
}) {
Widget _child = Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(24)),
),
child: TextField(
controller: controller,
maxLines: 1,
autofocus: false,
enabled: enabled,
style: TextStyle(),
onChanged: onChanged,
onSubmitted: onSubmitted,
textAlign: TextAlign.left,
decoration: InputDecoration(
contentPadding: EdgeInsets.only(bottom: 15, left: 0),
hintText: hintText,
border: InputBorder.none,
hintStyle: TextStyle(
fontSize: 13,
color: Color(0xFFAAAAAA),
),
prefixIcon: Icon(Icons.search),
),
),
height: 34,
);
if (city != null) {
_child = Row(
children: [
InkWell(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(24)),
),
height: 34,
width: 110,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
city,
style: TextStyle(
color: Color(0xFF302600),
fontSize: 13,
fontWeight: FontWeight.bold,
height: 1.2,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Container(
margin: EdgeInsets.only(left: 4),
height: 12,
width: 6,
alignment: Alignment.centerLeft,
child: Image.asset(Res.icArrowGray),
)
],
),
),
onTap: onCityTap,
),
SizedBox(
width: 8,
),
Expanded(child: _child, flex: 1),
],
);
}
return InkWell(
child: Container(
height: 35,
margin: EdgeInsets.symmetric(vertical: 8, horizontal: 13),
child: _child,
),
onTap: () {
onEnterCity();
},
);
}
///
/// 历史定位或者附加定位
///
///
Widget buildLocationsWidget({
@required String title,
@required Widget action,
bool isLoading = false,
List<dynamic> list = const [],
Widget bottom,
void Function(Map<String, dynamic>) onAddressTap,
EdgeInsetsGeometry padding = const EdgeInsets.symmetric(horizontal: 13),
}) {
if (list == null || list.isEmpty) {
return Container();
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.only(top: 20),
padding: padding,
height: 32,
child: Row(
children: [
Text(
title,
style: TextStyle(fontSize: 13, color: Color(0xFF666666)),
),
Spacer(),
action,
],
),
),
isLoading
? Container(
margin: EdgeInsets.only(top: 10),
alignment: Alignment.center,
child: CupertinoActivityIndicator(),
)
: ListView.builder(
shrinkWrap: true,
itemCount: list.length,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
Map<String, dynamic> data = list[index];
return InkWell(
child: Container(
child: Column(
children: [
Container(
height: 40,
alignment: Alignment.centerLeft,
margin: EdgeInsets.symmetric(horizontal: 13),
child: Text(
'${data['name']}',
style: TextStyle(
color: Color(0xFF302600), fontSize: 13),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Color(0xFFF5F5F5),
width: 0.5,
),
),
),
)
],
),
color: Colors.white,
),
onTap: () => onAddressTap(data),
);
},
),
if (bottom != null) bottom
],
);
}
Widget buildCurrentLocationsWidget({String title = '', void Function() onTap}) {
return InkWell(
onTap: onTap,
child: Container(
margin: EdgeInsets.symmetric(horizontal: 13),
padding: EdgeInsets.symmetric(horizontal: 12),
height: 40,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(
Radius.circular(20),
),
),
child: Row(
children: [
Text(
title,
style: TextStyle(color: Color(0xFF302600), fontSize: 13),
),
Spacer(),
Image.asset(Res.icGpsOrg, width: 16, height: 16),
],
),
),
);
}
///
/// 当前城市和热门城市
/// [citys] 城市列表
/// [current] 当前城市时显示定位
///
///
Widget buildCityWidget({
@required List<String> citys,
bool current = false,
EdgeInsetsGeometry padding = const EdgeInsets.only(left: 13),
void Function(String) onCityTap,
}) {
return GridView.builder(
padding: EdgeInsets.symmetric(
horizontal: padding.horizontal,
vertical: 7,
),
itemCount: citys.length,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 100,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
childAspectRatio: 3.1,
),
itemBuilder: (BuildContext context, int index) {
String city = citys[index];
Widget _child = Text(
city,
style: TextStyle(color: Color(0xFF302600), fontSize: 13),
);
if (current) {
_child = Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
Res.icGpsOrg,
width: 16,
height: 16,
),
SizedBox(width: 4),
_child,
],
);
}
return InkWell(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(4)),
),
height: 34,
alignment: Alignment.center,
child: _child,
),
onTap: () => onCityTap != null ? onCityTap(city) : null,
);
},
);
}
///
/// AZ排序ListView
/// [citysList] 城市列表数据
///
///
Widget buildAzListViewWidget({
@required ValueNotifier<SearchCityInfo> notifier,
@required Function(String) onCityTap,
@required Function() reEnteTap,
}) {
return ValueListenableBuilder(
valueListenable: notifier,
builder: (BuildContext context, SearchCityInfo cityInfo, Widget child) {
if (cityInfo.isSearch && cityInfo.citys.length == 1) {
return Column(
children: [
Container(
padding: EdgeInsets.only(left: 13.0),
width: MediaQuery.of(context).size.width,
height: 32,
color: Color(0xFFF5F5F5),
alignment: Alignment.centerLeft,
child: Text(
'当前城市',
style: TextStyle(fontSize: 13.0, color: Color(0xFF666666)),
),
),
buildCityWidget(
citys: cityInfo.citys[0].citys,
current: true,
onCityTap: onCityTap,
),
SizedBox(height: 120),
Container(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: '没有找到“',
style: TextStyle(fontSize: 14, color: Color(0xFF666666)),
),
TextSpan(
text: '${cityInfo.keywords}',
style: TextStyle(fontSize: 14, color: Color(0xFFFF8000)),
),
TextSpan(
text: '”这个城市',
style: TextStyle(fontSize: 14, color: Color(0xFF666666)),
)
],
),
),
),
SizedBox(height: 12),
InkWell(
child: Container(
height: 40,
width: 160,
decoration: BoxDecoration(
color: Color(0xFFFFCA00),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
alignment: Alignment.center,
child: Text(
'重新输入',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color(0xFF3D3101),
),
),
),
onTap: () => reEnteTap(),
),
],
);
}
return AzListView(
data: cityInfo.citys,
itemCount: cityInfo.citys.length,
itemBuilder: (BuildContext context, int index) {
CityModel model = cityInfo.citys[index];
if (model.type == 0) {
//0当前城市
return buildCityWidget(
citys: model.citys,
current: true,
onCityTap: onCityTap,
);
} else if (model.type == 1) {
//热门城市
return buildCityWidget(
citys: model.citys,
current: false,
onCityTap: onCityTap,
);
}
Text textWidget;
if (cityInfo.isSearch) {
List<TextSpan> childs = model.name.split("").map((s) {
bool isKeyWordChar = cityInfo.keywords.indexOf(s) > -1;
return TextSpan(
text: s,
style: TextStyle(
color:
isKeyWordChar ? rgba(255, 128, 0, 1) : rgba(48, 38, 0, 1),
),
);
}).toList();
textWidget = Text.rich(
TextSpan(children: childs),
style: TextStyle(fontSize: 13.rpx),
);
} else {
textWidget = Text(
model.name,
style: TextStyle(color: Color(0xFF302600), fontSize: 13),
);
}
return InkWell(
child: Container(
color: Colors.white,
margin: EdgeInsets.only(
top: (cityInfo.isSearch && index == 1) ? 12 : 0,
),
child: Column(
children: [
Container(
child: textWidget,
alignment: Alignment.centerLeft,
height: 40,
margin: EdgeInsets.only(left: 13, right: 12),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(color: Color(0xFF979797), width: .2),
),
),
),
],
),
),
onTap: () => onCityTap(model.name),
);
},
susItemBuilder: (BuildContext context, int index) {
CityModel model = cityInfo.citys[index];
if (model.type == 0) {
return Container(
padding: EdgeInsets.only(left: 13.0),
width: MediaQuery.of(context).size.width,
height: 32,
color: Color(0xFFF5F5F5),
alignment: Alignment.centerLeft,
child: Text(
'当前城市',
style: TextStyle(fontSize: 13.0, color: Color(0xFF666666)),
),
);
} else if (model.type == 1) {
return Container(
padding: EdgeInsets.only(left: 13.0),
width: MediaQuery.of(context).size.width,
height: 32,
alignment: Alignment.centerLeft,
color: Color(0xFFF5F5F5),
child: Text(
'热门城市',
style: TextStyle(fontSize: 13.0, color: Color(0xFF666666)),
),
);
}
String tag = model.getSuspensionTag();
return Container(
height: 32,
padding: EdgeInsets.only(left: 13.0),
color: Color(0xFFF5F5F5),
width: MediaQuery.of(context).size.width,
alignment: Alignment.centerLeft,
child: Text(
'$tag',
softWrap: false,
style: TextStyle(fontSize: 13.0, color: Color(0xFF666666)),
),
);
},
);
},
);
}
import 'package:azlistview/azlistview.dart';
import 'package:flutter/foundation.dart';
class SearchCityInfo {
List<CityModel> citys;
bool isSearch = false;
String keywords;
SearchCityInfo({
this.citys,
this.isSearch,
this.keywords = '',
});
}
///
/// type = 0 当前城市
/// type = 1 热门城市
///
class CityModel extends ISuspensionBean {
String name;
String tagIndex;
String namePinyin;
int type = -1;
List<String> citys;
CityModel({
@required this.name,
this.tagIndex,
this.namePinyin,
this.type = -1,
this.citys,
});
CityModel.fromJson(Map<String, dynamic> json)
: name = json['name'],
namePinyin = json['pinyin'];
@override
String getSuspensionTag() => tagIndex;
}
import 'dart:io';
import 'package:amaps_location/location.dart';
import 'package:common_module/utils/navigate_utils.dart';
import 'package:common_module/utils/store_utils.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:life_module/route/index.dart';
import 'package:life_module/util/resource.dart';
import 'package:life_repository/life_repository.dart';
import 'package:life_repository/repository/amap_repository.dart';
import 'package:common_module/utils/location_permission_utils.dart';
import 'components/index.dart';
import 'package:modal/modal.dart';
import 'package:common_module/utils/loading_dialog_utils.dart';
class LifeLocationPage extends StatefulWidget {
final Map<String, dynamic> param;
const LifeLocationPage({Key key, this.param}) : super(key: key);
@override
_LifeLocationPageState createState() => _LifeLocationPageState();
}
class _LifeLocationPageState extends State<LifeLocationPage> {
///当前城市
String _currentCity;
String _mCurrentCity;
List<dynamic> _historyLocations;
//附近位置
List<dynamic> _nearbyLocations = List<dynamic>.empty(growable: true);
List<MapTipEntity> _searchTips;
LocationEntity _currentLocationEntity;
@override
void initState() {
super.initState();
Future.delayed(Duration.zero,()=>startLocation(isInit: false,first:true));
getHistoryLocations();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: buildAppBar(
title: '选择定位地址',
onPressed:(){
Map<String,dynamic> arguments;
if(_currentLocationEntity != null){
arguments = {
'address': _currentLocationEntity?.address,
'name': _currentLocationEntity?.aoiName ?? _currentLocationEntity?.poiName ?? _currentLocationEntity?.streetNum?? _currentLocationEntity?.address,
'location': '${_currentLocationEntity?.longitude},${_currentLocationEntity?.latitude}',
'city': _currentLocationEntity?.city
};
}
NavigateUtils.pop(arguments:arguments);
}
),
body: _buildBody(),
);
}
Widget _buildBody() {
return Column(
children: [
buildSearchWidget(
enabled: !(_currentCity == null || _currentCity.isEmpty),
city: _currentCity ?? '加载中...',
hintText: (_currentCity == null || _currentCity.isEmpty)
? '请输入城市名称'
: '在$_currentCity内搜索',
onCityTap: () {
pushCity();
},
onEnterCity: () {
if (_currentCity == null || _currentCity.isEmpty) {
pushCity();
}
},
onChanged: (value) {
searchForKeywordsTips(keywords: value);
},
),
Expanded(
child: (_searchTips == null || _searchTips.isEmpty)
? CustomScrollView(
slivers: [
//获取当前定位按钮
SliverToBoxAdapter(
child: buildCurrentLocationsWidget(
onTap: () async {
startLocation(isInit: false, isCurrent: true);
},
title: '获取当前定位',
),
),
//历史定位
SliverToBoxAdapter(
child: buildLocationsWidget(
title: '历史定位',
list: _historyLocations,
action: Text(
(_historyLocations == null ||
(_historyLocations?.length ?? 0) < 5)
? '最多显示5条历史定位'
: '仅显示5条历史定位',
style:
TextStyle(color: Color(0xFF999999), fontSize: 12),
),
onAddressTap: (Map<String, dynamic> address) {
popAddress(arguments: address);
},
),
),
//附近定位
SliverToBoxAdapter(
child: buildLocationsWidget(
title: '附近定位',
isLoading: startLocationLoading,
list: _nearbyLocations,
padding: EdgeInsets.only(left: 13),
action: InkWell(
child: Container(
padding: EdgeInsets.symmetric(
vertical: 8,
horizontal: 13,
),
child: Image.asset(
'assets/images/life/ic_location_refresh.png',
package: 'life_module',
width: 16,
height: 16,
),
),
onTap: () {
startLocation(isInit: false);
},
),
onAddressTap: (Map<String, dynamic> address) {
popAddress(arguments: address);
}),
),
],
)
: ListView.builder(
itemCount: _searchTips.length,
itemBuilder: (BuildContext context, int index) {
MapTipEntity entity = _searchTips[index];
List<TextSpan> childs = entity.name.split("").map((s) {
bool isKeyWordChar = this.searchKeywords.indexOf(s) > -1;
return TextSpan(
text: s,
style: TextStyle(
color: isKeyWordChar
? rgba(255, 128, 0, 1)
: rgba(48, 38, 0, 1),
),
);
}).toList();
Text textWidget = Text.rich(
TextSpan(children: childs),
style: TextStyle(fontSize: 13.rpx),
);
return InkWell(
child: Container(
child: Column(
children: [
Container(
margin: EdgeInsets.symmetric(horizontal: 13),
height: 43,
alignment: Alignment.centerLeft,
child: textWidget,
decoration: BoxDecoration(
color: Colors.white,
border: Border(
bottom: BorderSide(
color: Color(0xFFF5F5F5), width: 0.5),
),
),
),
],
),
color: Colors.white,
),
onTap: () {
Map<String, dynamic> arguments = {
'address': entity.address,
'city': _currentCity, //entity.city, 这里直接用当前的城市
'district': entity.district,
'adcode': entity.adcode,
'location': entity.location,
'name': entity.name
};
popAddress(arguments: arguments);
},
);
}),
flex: 1,
),
],
);
}
void popAddress({Map<String, dynamic> arguments}) {
NavigateUtils.pop(arguments: arguments).then(
(value) {
_historyLocations.removeWhere(
(item) => item['name'] == arguments['name'],
);
_historyLocations.insert(0, arguments);
if (_historyLocations.length > 5) {
_historyLocations.removeAt(5);
}
StoreUtils.getInstance().setKV(
key: Res.historyLocation,
value: _historyLocations,
);
},
);
}
String searchKeywords = '';
void searchForKeywordsTips({String keywords}) async {
AMapRepository.get()
.inputSearchTips(
key: Res.aWebMapKey,
keywords: keywords,
city: _currentCity,
datatype: 'poi',
citylimit: true,
)
.then(
(value) {
this.searchKeywords = keywords;
setState(() {
_searchTips = value;
});
},
).catchError(
(error) {
print('========> error:$error');
},
);
}
void pushCity() {
Map<String, dynamic> arguments = {};
if (_mCurrentCity != null && _mCurrentCity.isNotEmpty) {
arguments['currentCity'] = _mCurrentCity;
}
NavigateUtils.push(
path: RouteCityPath.LOCATION_CITY_PAGE.path(),
arguments: arguments,
).then((value) {
if (value is Map && value['city'] != null) {
String _city = value['city'];
setState(() {
_currentCity = _city;
});
}
});
}
///
/// 使用高德地图SDK定位
///
void startLocation({
bool isInit = false,
bool isCurrent = false,
bool first = false,
}) async {
///
/// 检查是否存在权限
/// 如果有定位权限则读取定位权限
/// 否则使用网络定位显示城市
///
bool enable = await AmapsLocation.create().isOpenGPS();
bool has = await LocationPermissionUtils.getInstance().hasPermission();
if (has && enable) {
//有定位权限
var loading = LoadingDialogUtils.getInstance().show();
AmapsLocation.create()
.getLocation(
apiKey: Platform.isIOS ? Res.aIOSMapKey : Res.aAndroidMapKey)
.then(
(value) async{
LocationEntity entity = value;
loading.close();
if((entity?.address??'').isEmpty){
String location = '${entity.longitude},${entity.latitude}';
entity = await Res.convertLocation(entity:value,location:location);
}
_currentLocationEntity = entity;
if ((entity?.city ?? '').isNotEmpty) {
if (isCurrent) {
popAddress(arguments: {
'address': entity.address,
'name': entity.aoiName ?? entity.poiName ?? entity.streetNum?? entity.address,
'location': '${entity.longitude},${entity.latitude}',
'city': entity.city
});
} else {
_mCurrentCity = entity.city;
setState(() {
_currentCity = entity.city;
});
//查询附近位置信息
queryNearbyInfo(location: '${entity.longitude},${entity.latitude}');
}
} else {
ipConfigAddress();
}
},
).catchError(
(error) {
ipConfigAddress();
},
);
} else {
//没有定位权限
if (isInit) {
ipConfigAddress();
} else {
if (enable) {
//有GPS
bool confirm = await Modal.showModal(
title: "温馨提示",
msg: "为给您提供更好的服务,需获取您的位置权限",
cancelBtnText: '拒绝',
confirmBtnText: '同意');
if (confirm) {
bool has =
await LocationPermissionUtils.getInstance().reqPermission();
if (has) {
startLocation(isInit: false, isCurrent: isCurrent);
}
}else if(first){
ipConfigAddress();
}
} else {
//没有GPS
bool confirm = await Modal.showModal(
title: "温馨提示",
msg: "为给您提供更好的服务,需获取您的位置权限",
cancelBtnText: '拒绝',
confirmBtnText: '同意');
if (confirm) {
AmapsLocation.create().enableGPS().then((value) {});
}
}
}
}
}
bool startLocationLoading = false;
void queryNearbyInfo({@required String location}) {
setState(() {
startLocationLoading = true;
});
AMapRepository.get()
.poi2AroundSearch(key: Res.aWebMapKey, location: location, pageSize: 5)
.then((value) {
_nearbyLocations.clear();
for (var item in value) {
_nearbyLocations.add({
'address': item.address,
'name': item.name,
'id': item.id,
'location': item.location,
'type': item.type,
'typecode': item.typecode,
'pname': item.pname,
'cityname': item.cityname,
'adname': item.adname,
'pcode': item.pcode,
'citycode': item.citycode,
'adcode': item.adcode,
});
}
startLocationLoading = false;
setState(() {});
}).catchError((error) {});
}
///
/// 使用网络定位
///
void ipConfigAddress() {
AMapRepository.get().ipConfigAddress(key: Res.aWebMapKey).then((value) {
if (value.status == '1' && value.city != null) {
_mCurrentCity = _currentCity;
setState(() {
_currentCity = value.city;
});
}
}).catchError((error) {
print('============> 高德地图网络定位异常');
});
}
///
/// 获取缓存的历史定位
///
void getHistoryLocations() {
_historyLocations =
StoreUtils.getInstance().getForKey(key: Res.historyLocation);
if (_historyLocations == null || _historyLocations.isEmpty) {
_historyLocations = List<dynamic>.empty(growable: true);
}
}
}
import 'package:common_module/utils/store_utils.dart';
import 'package:mvp/mvp.dart';
import '../models/model.dart';
import '../models/app_nav_model.dart';
import '../models/sec_kill_model.dart';
import '../models/mini_banner_model.dart';
import 'package:life_module/models/filter_model.dart';
import 'package:life_module/models/shop_list_model.dart';
import 'package:common_module/utils/loading_dialog_utils.dart';
class IAction extends BaseAction<Model> {
AppNavModel _appNavModel = AppNavModel();
SecKillModel _killModel = SecKillModel();
MiniBannerModel _miniBannerModel = MiniBannerModel();
FilterModel _filterModel = FilterModel();
ShopListModel _shopListModel = ShopListModel();
@override
void initState() {
_init();
}
void _init() async {
setModel(Model());
await getModel().findPermission();
initLocation();
await getAppNavModel().getAppNavList();
await getMiniBannerModel().getActivityConfigList();
}
void initLocation() async{
// 没有定位权限,用ip定位
bool isFirstLifeLocation = StoreUtils.getInstance().getForKey(key:'is_first_life_location2');
if(isFirstLifeLocation == null){
StoreUtils.getInstance().setKV(key:'is_first_life_location2', value: true);
await getModel().reqPermission();
initLocation();
}else if (!getModel().getHasPermission()) {
getModel().ipConfigAddress().then((value) => initData());
} else {
getModel().getLocation().then((value){
initData();
});
}
}
Future<void> initData() async {
await getModel().findCityId();
String cityId = getModel().getCityId();
print('===========> cityId:$cityId');
if (cityId != null && cityId.isNotEmpty) {
await getSecKillModel().findDisplay(cityId);
await getFilterModel().getFilterList(cityId);
refreshShopList();
}
}
Future<void> refreshShopList() async {
var loading = LoadingDialogUtils.getInstance().show();
Future.delayed(Duration(seconds: 2)).then((value) {
if (loading != null) {
loading.close();
}
});
try {
getShopListModel()
.setShopTypeNavIndex(getFilterModel().getShopTypeNavIndex());
getShopListModel().setCityId(getModel().getCityId());
getShopListModel().setLat(getModel().getLatitude());
getShopListModel().setLng(getModel().getLongitude());
getShopListModel()
.setFirstCateId(getFilterModel().getCheckedOneCategory().id);
getShopListModel()
.setSecondCateId(getFilterModel().getCheckedTwoCategory().id ?? "");
if (getFilterModel().getCheckedTwoRegion().id == null) {
getShopListModel()
.setRegionId(getFilterModel().getCheckedOneRegion().id);
} else {
getShopListModel()
.setRegionId(getFilterModel().getCheckedTwoRegion().id);
}
getShopListModel().setSortType(getFilterModel().getCheckedOneSort().id);
getShopListModel().clear();
getShopListModel().scrollTop();
await getShopListModel().refresh();
} catch (e) {}
if (loading != null) {
loading.close();
}
}
void refresh() async {
String cityId = getModel().getCityId();
try {
await getAppNavModel().getAppNavList();
await getMiniBannerModel().getActivityConfigList();
} catch (e) {}
if (cityId != null && cityId.isNotEmpty) {
try {
await getSecKillModel().findDisplay(cityId);
} catch (e) {}
await getShopListModel().refresh();
}
}
AppNavModel getAppNavModel() {
return _appNavModel;
}
SecKillModel getSecKillModel() {
return _killModel;
}
MiniBannerModel getMiniBannerModel() {
return _miniBannerModel;
}
FilterModel getFilterModel() {
return _filterModel;
}
ShopListModel getShopListModel() {
return _shopListModel;
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import '../actions/action.dart';
import 'package:mvp/providers/provider.dart';
import '../models/app_nav_model.dart';
import 'package:life_repository/entity/app_nav_entity.dart';
import 'package:common_module/widget/xiaoxiong_base_image_widget/widget.dart';
import 'package:life_module/utils/activity_config_tap_utils.dart';
/// 钻石位
class DiamondBit extends StatelessWidget {
final IAction iAction;
final ValueNotifier<double> scrollIndex = ValueNotifier(0.0);
DiamondBit({Key key, this.iAction}) : super(key: key);
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: QMProvider<AppNavModel>.value(
model: iAction.getAppNavModel(),
builderWidget: (context, model, child) {
if (model.getList().length == 0) {
return SizedBox();
}
return Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
NotificationListener(
onNotification: (ScrollNotification notification) {
double extentBefore = notification.metrics.extentBefore;
double extentAfter = notification.metrics.extentAfter;
double width = extentAfter + extentBefore;
scrollIndex.value = (extentBefore / width) * 10.rpx;
return true;
},
child: _DiamondGrid(model.getList()),
),
ValueListenableBuilder<double>(
valueListenable: scrollIndex,
builder:
(BuildContext context, dynamic value, Widget child) {
return _DiamondBitWidgetPagination(
offset: value, itemCount: model.getList().length);
},
),
]),
);
}),
);
}
}
/// 宫格
class _DiamondGrid extends StatelessWidget {
final List<AppNavEntity> items;
_DiamondGrid(this.items);
@override
Widget build(BuildContext context) {
int itemCount = items.length;
bool isOneRow = itemCount < 10;
bool isFull = itemCount == 10 || itemCount == 5;
return Theme(
data: ThemeData(platform: TargetPlatform.macOS),
child: Container(
height: (isOneRow ? 78.rpx : 156.rpx) + (isFull ? 20.rpx : 0),
padding: EdgeInsets.only(top: 8.rpx, bottom: isFull ? 20.rpx : 0),
child: GridView.builder(
scrollDirection: Axis.horizontal,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: isOneRow ? 1 : 2,
crossAxisSpacing: 0,
mainAxisSpacing: 0,
childAspectRatio: .95,
),
itemBuilder: (context, index) {
var item = items[index];
return InkWell(
onTap: () {
AppTapConfigUtils.getInstance().activityConfigTap(item.toJson());
},
child: Container(
alignment: Alignment.topCenter,
child: Column(
children: <Widget>[
SizedBox(
height: 8.rpx,
),
Container(
width: 40.rpx,
height: 40.rpx,
child: XiaoxiongBaseImageWidget(
imageUrl: item.iconUrl,
width: 40.rpx,
height: 40.rpx,
fit: BoxFit.cover,
),
),
SizedBox(
height: 2.rpx,
),
Text(
item.name,
overflow: TextOverflow.ellipsis,
style: TextStyle(
height: 1.3846153846153,
fontSize: 12.rpx,
color: rgba(48, 38, 0, 1)),
)
],
),
),
);
},
itemCount: items.length,
),
),
);
}
}
/// 指示器
class _DiamondBitWidgetPagination extends StatelessWidget {
final double offset;
final int itemCount;
_DiamondBitWidgetPagination({Key key, this.offset, this.itemCount})
: super(key: key);
@override
Widget build(BuildContext context) {
if (itemCount == 10 || itemCount <= 5) {
return SizedBox();
}
return Container(
width: double.infinity,
alignment: Alignment.center,
padding: EdgeInsets.only(bottom: 9.rpx, top: 4.rpx),
child: Container(
width: 20.rpx,
height: 3.rpx,
decoration: BoxDecoration(
color: Color(0xFFE9E9E9),
borderRadius: BorderRadius.all(Radius.circular(2.rpx))),
child: Stack(
children: [
Positioned(
left: offset,
child: Container(
width: 10.rpx,
height: 3.rpx,
decoration: BoxDecoration(
color: Color(0XFFFF8000),
borderRadius:
BorderRadius.all(Radius.circular(2.rpx)),
)))
],
)));
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import '../models/mini_banner_model.dart';
import 'package:mvp/mvp.dart';
import 'package:common_module/widget/xiaoxiong_base_image_widget/widget.dart';
import 'package:life_module/utils/activity_config_tap_utils.dart';
// assets/close_btn_black.png
class FloatBanner extends StatelessWidget {
final MiniBannerModel model;
FloatBanner({this.model});
@override
Widget build(BuildContext context) {
return QMProvider<MiniBannerModel>.value(
model: model,
builderWidget: (context, model, child) {
if (model.getFloatBannerEntity() == null) {
return SizedBox();
}
return Container(
height: 96.rpx,
width: 96.rpx,
child: Stack(
alignment: Alignment.topRight,
children: [
Positioned(
right: 0,
top: 10.rpx,
child: InkWell(
onTap: () {
AppTapConfigUtils.getInstance().activityConfigTap(model.getFloatBannerEntity().toJson());
model.removeFloatBannerEntity(true);
},
child: Container(
height: 76.rpx,
width: 76.rpx,
//padding: EdgeInsets.symmetric(horizontal: 9.rpx),
margin: EdgeInsets.only(top: 6.rpx, bottom: 6.rpx),
child: XiaoxiongBaseImageWidget(
imageUrl: model.getFloatBannerEntity().imgUrl,
height: 76.rpx,
fit: BoxFit.contain,
placeholder:(BuildContext context, String data){
return Container();
},
),
))),
InkWell(
onTap: () {
model.removeFloatBannerEntity();
},
child: Container(
height: 16.rpx,
alignment: Alignment.topRight,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.rpx),
),
child: Image.asset(
"assets/close_btn_black.png",
width: 16.rpx,
height: 16.rpx,
)),
)
],
),
);
},
);
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import '../models/mini_banner_model.dart';
import 'package:mvp/mvp.dart';
import 'package:common_module/widget/xiaoxiong_base_image_widget/widget.dart';
import 'package:life_module/utils/activity_config_tap_utils.dart';
class MiniBanner extends StatelessWidget {
final MiniBannerModel model;
MiniBanner({this.model});
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: QMProvider<MiniBannerModel>.value(
model: model,
builderWidget: (context, model, child) {
if (model.getList().length == 0) {
return SizedBox();
}
// ;
List<Widget> items = model.getList().map((e) {
return Expanded(
child: InkWell(
onTap: () {
AppTapConfigUtils.getInstance().activityConfigTap(e.toJson());
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 4.rpx),
child: ClipRRect(
borderRadius: BorderRadius.circular(8.rpx),
child: Container(
height: 50.rpx,
child: XiaoxiongBaseImageWidget(
imageUrl: e.imgUrl,
height: 50.rpx,
fit: BoxFit.cover,
),
)),
),
),
);
}).toList();
return Container(
height: 56.rpx,
padding: EdgeInsets.symmetric(horizontal: 9.rpx),
margin: EdgeInsets.only(top: 8.rpx, bottom: 6.rpx),
child: Row(
children: items,
),
);
},
),
);
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_module/components/shop_type_nav.dart';
import './diamond_bit.dart';
import './sec_kill.dart';
import './mini_banner.dart';
import 'package:life_module/components/filter_area.dart';
import 'package:life_module/components/shop_class_nav.dart';
import 'package:life_module/components/shop_list.dart';
import 'package:life_module/components/persistent_header_delegate.dart';
import '../actions/action.dart';
import 'package:flutter/rendering.dart';
import 'package:common_module/widget/pull_widget/widget.dart';
import 'package:common_module/utils/navigate_utils.dart';
import 'package:life_module/route/index.dart';
class ScrollBody extends StatefulWidget {
final IAction iAction;
final String isAuditMode;
final Function(double offset) onScroll;
final ScrollController controller;
ScrollBody({this.onScroll, this.iAction, this.controller,this.isAuditMode});
@override
State<StatefulWidget> createState() => _ScrollBodyState();
}
class _ScrollBodyState extends State<ScrollBody> {
ScrollController get controller => widget.controller;
void initState() {
super.initState();
controller.addListener(() {
widget.onScroll(controller.offset);
});
}
@override
Widget build(BuildContext context) {
return PullWidget(
headerInsertIndex: 1,
controller: widget.iAction.getShopListModel().getRefreshController(),
onLoad: () {
widget.iAction.getShopListModel().getData();
},
onRefresh: () {
widget.iAction.getShopListModel().refresh();
},
child: CustomScrollView(
controller: controller,
slivers: [
SliverAppBar(
backgroundColor: Colors.transparent,
toolbarHeight: 44.rpx,
),
DiamondBit(
iAction: widget.iAction,
),
SecKill(
iAction: widget.iAction,
),
MiniBanner(
model: widget.iAction.getMiniBannerModel(),
),
SliverPersistentHeader(
pinned: false,
delegate: PersistentHeaderDelegate(
minHeight: 37.rpx,
maxHeight: 37.rpx,
child: ShopTypeNav(
model: widget.iAction.getFilterModel(),
onChange: () {
widget.iAction.refreshShopList();
},
))),
SliverPersistentHeader(
pinned: true,
delegate: PersistentHeaderDelegate(
minHeight: 48.rpx,
maxHeight: 48.rpx,
child: FilterArea(
topOffset: 81.rpx - 37.rpx,
model: widget.iAction.getFilterModel(),
onTap: (c) {
var _context =
widget.iAction.getShopListModel().getContext();
RenderSliver renderSliver = _context
.findAncestorRenderObjectOfType<RenderSliver>();
if (renderSliver != null &&
renderSliver.constraints.overlap <= 0) {
Scrollable.ensureVisible(_context);
}
},
onChange: () {
widget.iAction.refreshShopList();
},
))),
SliverPersistentHeader(
pinned: true,
delegate: PersistentHeaderDelegate(
minHeight: 40.rpx,
maxHeight: 40.rpx,
child: ShopClassNav(
model: widget.iAction.getFilterModel(),
onChange: () {
widget.iAction.refreshShopList();
},
))),
ShopList(
model: widget.iAction.getShopListModel(),
onReset: () {
widget.iAction.getFilterModel().reset();
widget.iAction.refreshShopList();
},
onTap: (item) {
NavigateUtils.push(
path: LIFE_SHOP_PAGE,
arguments: {
'isAuditMode':widget.isAuditMode,
'shopId': item.combo != null
? item.combo.shopId
: item.restaurant.shopId,
'lat': widget.iAction.getModel().getLatitude(),
'lng': widget.iAction.getModel().getLongitude(),
'firstCateId': widget.iAction
.getFilterModel()
.getCheckedOneCategory()
.id,
'itemId': item.combo != null ? item.combo.dealId : "",
"cityName": widget.iAction.getModel().getCity()
},
isNative: false,
);
},
)
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_module/components/search_bar.dart';
import 'package:life_module/components/life_image.dart';
import 'package:common_module/utils/navigate_utils.dart';
import 'package:life_module/route/index.dart';
import 'package:life_module/views/life/models/model.dart';
import 'package:mvp/mvp.dart';
import '../actions/action.dart';
import 'package:common_module/utils/map_utils.dart';
class SearchArea extends StatefulWidget {
final IAction iAction;
final ValueNotifier<double> scrollOffset;
SearchArea({this.scrollOffset, this.iAction});
@override
State<StatefulWidget> createState() => _SearchAreaState();
}
class _SearchAreaState extends State<SearchArea> {
@override
Widget build(BuildContext context) {
// 最大滚动范围
double maxOffset = 44.rpx;
// 离左侧最大距离
double maxLeft = 101.rpx;
return Container(
child: QMProvider<Model>.value(
model: widget.iAction.getModel(),
builderWidget: (context, model, child) {
return ValueListenableBuilder<double>(
valueListenable: widget.scrollOffset,
builder: (BuildContext context, dynamic offset, Widget child) {
double opacity = 1;
if (offset >= maxOffset) {
// 超出最大滚动范围
opacity = 0.0;
offset = maxOffset;
} else if (offset > 0) {
// 滚动范围>0
opacity = 1 - offset / maxOffset;
} else if (offset < 0) {
// 滚动范围小于0
offset = 0.0;
}
double left = offset * (maxLeft / maxOffset) * 1.5;
if (left > maxLeft) {
left = maxLeft;
}
List<Widget> items = [
Opacity(
opacity: opacity,
child: _Locaion(
iAction: widget.iAction,
),
),
Positioned(
top: 44.rpx,
left: 0,
right: 0,
child: Container(
height: 44.rpx,
child: InkWell(
child: SearchBar(
enabled: false,
inputColor: rgba(245, 245, 245, 1),
),
onTap: () {
NavigateUtils.push(
path: RouteCityPath.LIFE_SEARCH_PAGE.path(),
arguments: {
'cityId': widget.iAction.getModel().getCityId(),
'latitude':
widget.iAction.getModel().getLatitude(),
'longitude':
widget.iAction.getModel().getLongitude(),
'cityName': widget.iAction.getModel().getCity(),
},
isNative: false,
);
},
),
margin: EdgeInsets.only(left: left),
padding: EdgeInsets.symmetric(
horizontal: 13.rpx, vertical: 5.rpx),
transform: Matrix4.translationValues(0, -offset, 0),
),
),
];
if (!model.getHasPermission()) {
items.add(Positioned(
top: 37.rpx,
right: 13.rpx,
child: opacity <= 0
? SizedBox()
: Opacity(
opacity: opacity,
child: _Bubble(widget.iAction),
),
));
}
return Container(
height: 88.rpx,
child: Stack(
children: items,
),
);
});
}),
);
}
}
/// 定位按钮
class _Locaion extends StatelessWidget {
final IAction iAction;
_Locaion({this.iAction});
@override
Widget build(BuildContext context) {
return QMProvider<Model>.value(
model: iAction.getModel(),
builderWidget: (context, model, child) {
return InkWell(
child: Container(
height: 44.rpx,
padding: EdgeInsets.only(right: 13.rpx),
alignment: Alignment.centerRight,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Image.asset('assets/ic_location.png',
width: 20.rpx, height: 20.rpx, fit: BoxFit.contain),
SizedBox(
width: 6.rpx,
),
Container(
constraints: BoxConstraints(maxWidth: 150.rpx),
child: Text(
model.getAddres(),
overflow: TextOverflow.clip,
maxLines: 1,
style: TextStyle(
color: rgba(48, 38, 0, 1),
fontSize: 13.rpx,
height: 1.1,
fontWeight: FontWeight.bold),
),
),
SizedBox(
width: 3.rpx,
),
Image.asset(
"assets/setup_enter.png",
height: 10.rpx,
color: rgba(48, 38, 0, 1),
width: 6.rpx,
fit: BoxFit.fitWidth,
),
SizedBox(
width: 7.rpx,
)
],
),
),
onTap: () async {
// 点击这里 返回不判断权限
model.setIsClickCitySelect(true);
var result = await NavigateUtils.push(
path: LIFE_LOCATION_PAGE,
arguments: {},
isNative: false,
);
if (result == null) {
return;
}
var location = result['location'].split(',');
var _latitude = '${location[1]}';
var _longitude = '${location[0]}';
List<num> arr = MapUtils.getInstance().gcj02ToGps84(
double.parse(_latitude), double.parse(_longitude));
_latitude = '${arr[0]}';
_longitude = '${arr[1]}';
model.setLocationInfo(result['cityname'] ?? result['city'],
result['name'], _latitude, _longitude);
await iAction.initData();
iAction.getModel().findPermission();
},
);
});
}
}
/// 气泡
class _Bubble extends StatelessWidget {
final IAction iAction;
_Bubble(this.iAction);
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
height: 38.rpx,
width: 200.rpx,
child: Column(
children: [
Container(
height: 8.rpx,
alignment: Alignment.topRight,
padding: EdgeInsets.only(right: 28.rpx, top: 2.5.rpx),
child: LifeImage(
name: "assets/images/life/ic_arrow_up.png",
color: rgba(0, 0, 0, 0.70),
width: 8.rpx,
height: 9.rpx,
fit: BoxFit.fitHeight,
),
),
Container(
height: 30.rpx,
decoration: BoxDecoration(
color: rgba(0, 0, 0, 0.70),
borderRadius: BorderRadius.circular(25.rpx),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
InkWell(
onTap: () async {
await iAction.getModel().reqPermission();
if (iAction.getModel().getHasPermission()) {
iAction.initLocation();
}
},
child: Container(
height: 30.rpx,
padding: EdgeInsets.only(
left: 15.rpx,
),
alignment: Alignment.center,
child: Text(
"开启定位查看附近精准攻略",
style: TextStyle(
color: Colors.white,
fontSize: 12.rpx,
height: 1.2),
),
)),
InkWell(
child: Container(
height: 30.rpx,
padding: EdgeInsets.only(right: 10.rpx, left: 6.rpx),
child: Icon(
Icons.close,
color: Colors.white,
size: 15.rpx,
),
),
onTap: () {
iAction.getModel().setPermission();
},
)
],
),
)
],
));
}
}
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_module/components/life_image.dart';
import 'package:life_module/views/life/actions/action.dart';
import 'package:mvp/mvp.dart';
import '../models/sec_kill_model.dart';
import 'package:life_repository/entity/sec_kill_goods.dart';
import 'package:life_repository/entity/sec_kill_session.dart';
import 'package:common_module/utils/date_time_helper.dart';
import 'package:common_module/widget/xiaoxiong_base_image_widget/widget.dart';
import 'package:flutter/cupertino.dart';
import 'package:common_module/utils/navigate_utils.dart';
import 'package:life_module/route/index.dart';
const SECOND = 1000;
const MINUTE = 60 * SECOND;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;
class SecKill extends StatefulWidget {
final IAction iAction;
SecKill({this.iAction});
@override
State<StatefulWidget> createState() => _SecKillState();
}
class _SecKillState extends State<SecKill> {
ValueNotifier<int> scrollIndex = ValueNotifier<int>(0);
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: QMProvider<SecKillModel>.value(
model: widget.iAction.getSecKillModel(),
builderWidget: (context, model, child) {
if (!model.isShow() && model.getSeckillSessionList().length == 0) {
return SizedBox();
}
return InkWell(
onTap: () {
NavigateUtils.push(
path: RouteCityPath.LIFE_SEC_KILL_PAGE.path(),
arguments: {
'tid': '',
'times': model.getSeckillSessionList(),
"cityId": widget.iAction.getModel().getCityId(),
"lat": widget.iAction.getModel().getLatitude(),
"lng": widget.iAction.getModel().getLongitude(),
"cityName": widget.iAction.getModel().getCity()
},
isNative: false,
);
},
child: Container(
height: 154.rpx,
margin:
EdgeInsets.only(left: 13.rpx, top: 8.rpx, right: 13.rpx),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.rpx)),
child: Column(
children: [
_SecKillTitle(
key: ValueKey(model
.getSeckillSessionList()
.map((e) => e.id)
.join("_${widget.iAction.getModel().getLatitude()}_${widget.iAction.getModel().getLongitude()}")),
seckillSessions: model.getSeckillSessionList(),
onNext: (seckillSession) async{
await widget.iAction.getModel().findCityId();
print('==============>_cityId:${widget.iAction.getModel().getCityId()}');
scrollIndex.value = 0;
model.loadSecKillGoodsByShowId(
widget.iAction.getModel().getCityId(),
seckillSession.id,
widget.iAction.getModel().getLatitude(),
widget.iAction.getModel().getLongitude());
},
),
_SecKillGoodsBody(
model: model,
onChange: (index) {
scrollIndex.value = index;
},
),
_SecKillPagination(
scrollIndex: scrollIndex,
model: model,
),
SizedBox(
height: 5.rpx,
)
],
),
));
}),
);
}
}
/// 秒杀标题
class _SecKillTitle extends StatefulWidget {
final List<SeckillSession> seckillSessions;
final Function(SeckillSession session) onNext;
_SecKillTitle({this.seckillSessions, this.onNext, Key key}) : super(key: key);
@override
State<StatefulWidget> createState() => _SecKillTitleState();
}
class _SecKillTitleState extends State<_SecKillTitle> {
List<SeckillSession> get seckillSessions => widget.seckillSessions;
String statusText = '';
int startTime;
int endTime;
bool isStart = false;
SeckillSession seckillSession;
@override
void initState() {
super.initState();
init();
}
void setStartAndEndTime(SeckillSession session) {
var timeArr = session.text.split("-");
String nowDate = DateUtil.formatDate(DateTime.now(), format: "yyyy-MM-dd");
startTime = DateUtil.getDateMsByTimeStr("$nowDate ${timeArr[0]}");
endTime = DateUtil.getDateMsByTimeStr("$nowDate ${timeArr[1]}");
}
void init() {
int nowTime = DateTime.now().millisecondsSinceEpoch;
seckillSession = seckillSessions.firstWhere((session) {
setStartAndEndTime(session);
if (startTime <= nowTime && endTime >= nowTime) {
isStart = true;
return true;
}
if (startTime > nowTime) {
isStart = false;
return true;
}
startTime = 0;
endTime = 0;
return false;
});
if (seckillSession == null) {
return;
}
if (startTime > nowTime) {
runStartTimeDown();
} else if (startTime < nowTime && endTime > nowTime) {
runEndTimeDown();
}
widget.onNext(seckillSession);
}
void next() {
int index = seckillSessions.indexOf(seckillSession);
if (index >= seckillSessions.length - 1) {
return;
}
seckillSession = seckillSessions[index + 1];
setStartAndEndTime(seckillSession);
int nowTime = DateTime.now().millisecondsSinceEpoch;
if (startTime > nowTime) {
isStart = false;
runStartTimeDown();
} else if (startTime < nowTime && endTime > nowTime) {
isStart = true;
runEndTimeDown();
}
widget.onNext(seckillSession);
}
void runStartTimeDown() {
int nowTime = DateTime.now().millisecondsSinceEpoch;
int dur = startTime - nowTime;
int h = ((dur % DAY) / HOUR).floor();
int i = ((dur % HOUR) / MINUTE).floor();
int s = ((dur % MINUTE) / SECOND).floor();
if (h <= 0 && i <= 0 && s <= 0) {
setState(() {
isStart = true;
});
runEndTimeDown();
return;
}
String ht = h < 10 ? "0$h" : "$h";
String it = i < 10 ? "0$i" : "$i";
String st = s < 10 ? "0$s" : "$s";
setState(() {
statusText = "$ht:$it:$st";
});
Future.delayed(Duration(seconds: 1)).then((value) => runStartTimeDown());
}
void runEndTimeDown() {
int nowTime = DateTime.now().millisecondsSinceEpoch;
int dur = endTime - nowTime;
int h = ((dur % DAY) / HOUR).floor();
int i = ((dur % HOUR) / MINUTE).floor();
int s = ((dur % MINUTE) / SECOND).floor();
if (h <= 0 && i <= 0 && s <= 0) {
setState(() {
statusText = "已结束";
});
Future.delayed(Duration(seconds: 1)).then((value) {
next();
});
return;
}
String ht = h < 10 ? "0$h" : "$h";
String it = i < 10 ? "0$i" : "$i";
String st = s < 10 ? "0$s" : "$s";
setState(() {
statusText = "$ht:$it:$st";
});
Future.delayed(Duration(seconds: 1)).then((value) => runEndTimeDown());
}
@override
Widget build(BuildContext context) {
return Container(
height: 52.rpx,
padding: EdgeInsets.symmetric(vertical: 16.rpx, horizontal: 8.rpx),
child: Row(
children: [
LifeImage(
name: "assets/images/life/ic_miaosha.png",
height: 20.rpx,
width: 76.rpx,
),
SizedBox(
width: 13.rpx,
),
Container(
height: 16.rpx,
width: 90.rpx,
padding: EdgeInsets.only(left: 40.rpx),
alignment: Alignment.center,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
"assets/images/life/ic_miaosha_time${!isStart ? '_s' : ''}.png",
package: "life_module")),
),
child: Text(
statusText,
style: TextStyle(
color: rgba(255, 4, 0, 1),
fontSize: 10.rpx,
fontWeight: FontWeight.bold),
),
),
Spacer(),
Container(
child: Row(
children: [
Text(
"全部场次",
style: TextStyle(
color: rgba(102, 102, 102, 1),
fontSize: 12.rpx,
height: 1.2),
),
SizedBox(
width: 4.rpx,
),
LifeImage(
name: "assets/images/life/ic_miaosha_arrow_right.png",
width: 8.rpx,
height: 8.rpx,
)
],
),
)
],
),
);
}
}
class _SecKillGoodsBody extends StatelessWidget {
final SecKillModel model;
final Function(int) onChange;
_SecKillGoodsBody({this.model, this.onChange});
@override
Widget build(BuildContext context) {
List<SecKillGoods> list = model.getSecKillGoodsList();
if (list.length == 0) {
return Expanded(
child: Container(
alignment: Alignment.center,
child: CupertinoActivityIndicator(radius: 10.rpx),
),
);
}
int length = (list.length / 2).ceil();
List<Widget> items = List.generate(length, (index) {
int limit = 2;
int offset = index * limit;
int size = offset + limit;
if (size > list.length) {
size = list.length;
}
var l = list.sublist(offset, size);
return _SecKillItem(
list: l,
);
});
return Expanded(
child: NotificationListener(
child: PageView(
onPageChanged: onChange,
children: items,
),
onNotification: (e) {
return true;
},
),
);
}
}
/// 单场秒杀
class _SecKillItem extends StatefulWidget {
final List<SecKillGoods> list;
_SecKillItem({this.list});
@override
State<StatefulWidget> createState() => _SecKillItemState();
}
class _SecKillItemState extends State<_SecKillItem> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 8.rpx),
child: Column(
children: [
_SecKillGoods(
list: widget.list,
),
],
));
}
}
/// 秒杀商品
class _SecKillGoods extends StatelessWidget {
final List<SecKillGoods> list;
_SecKillGoods({this.list});
@override
Widget build(BuildContext context) {
int len = list.length - 1;
List<Widget> childs = List.generate(2, (index) {
if (index > len) {
return Expanded(child: SizedBox());
}
return _SecKillGoodsItem(list[index]);
}).toList();
return Row(
children: childs,
);
}
}
/// 单个秒杀商品
class _SecKillGoodsItem extends StatelessWidget {
final SecKillGoods item;
_SecKillGoodsItem(this.item);
@override
Widget build(BuildContext context) {
return Expanded(
child: Container(
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(4.rpx),
child: Container(
height: 73.rpx,
width: 73.rpx,
child: XiaoxiongBaseImageWidget(
imageUrl: item.itemPic,
height: 73.rpx,
width: 73.rpx,
fit: BoxFit.cover,
),
)),
SizedBox(
width: 6.rpx,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 32.rpx,
child: Text(
item.itemTitle,
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: TextStyle(
color: rgba(48, 38, 0, 1),
fontSize: 12.rpx,
height: 1.1),
),
),
Container(
padding: EdgeInsets.symmetric(
horizontal: 4.rpx, vertical: 2.rpx),
decoration: BoxDecoration(
border:
Border.all(color: rgba(255, 128, 0, 1), width: 0.5),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(9.rpx),
topRight: Radius.circular(9.rpx),
bottomRight: Radius.circular(9.rpx))),
child: Text(
item.userCommissionText,
style: TextStyle(
color: rgba(255, 128, 0, 1),
fontSize: 11.rpx,
height: 1.2),
),
),
Container(
height: 22.rpx,
alignment: Alignment.bottomLeft,
child: Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
children: [
Text(
"¥",
style: TextStyle(
color: rgba(255, 4, 0, 1),
fontSize: 11.rpx,
),
),
Text(
item.endPrice,
style: TextStyle(
color: rgba(255, 4, 0, 1),
fontSize: 14.rpx,
),
),
SizedBox(
width: 4.rpx,
),
Text(
${item.originalPrice}",
style: TextStyle(
color: rgba(176, 176, 176, 1),
fontSize: 10.rpx,
decoration: TextDecoration.lineThrough),
)
],
),
)
],
),
)
],
),
),
);
}
}
/// 指示器
class _SecKillPagination extends StatelessWidget {
final SecKillModel model;
final ValueNotifier<int> scrollIndex;
_SecKillPagination({this.scrollIndex, this.model});
@override
Widget build(BuildContext context) {
if (model.getSecKillGoodsList().length == 0) {
return SizedBox(
height: 4.rpx,
);
}
return ValueListenableBuilder<int>(
valueListenable: scrollIndex,
builder: (BuildContext context, dynamic offset, Widget child) {
List<Widget> items = List.generate(
(model.getSecKillGoodsList().length / 2).ceil(), (index) {
Color color = rgba(216, 216, 216, 1);
if (index == scrollIndex.value) {
color = rgba(255, 128, 0, 1);
}
return Container(
width: 4.rpx,
height: 4.rpx,
margin: EdgeInsets.symmetric(horizontal: 2.rpx),
decoration: BoxDecoration(
color: color, borderRadius: BorderRadius.circular(2.rpx)),
);
});
return Container(
height: 4.rpx,
alignment: Alignment.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: items,
),
);
});
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_module/components/life_image.dart';
class TitleBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
height: 44.rpx,
padding: EdgeInsets.only(left: 13.rpx),
alignment: Alignment.centerLeft,
child: LifeImage(
name: "assets/images/life/ic_local.png",
width: 80.rpx,
height: 20.rpx),
);
}
}
import 'package:mvp/mvp.dart';
import 'package:life_repository/life_repository.dart';
class AppNavModel extends BaseModel {
List<AppNavEntity> _list = [];
Future<void> getAppNavList() async {
_list = await AppNavRepository.get().getAppNavList("3");
notifyListeners();
}
List<AppNavEntity> getList() {
return _list;
}
}
import 'package:mvp/mvp.dart';
import 'package:life_repository/life_repository.dart';
import 'package:common_module/utils/store_utils.dart';
import 'package:common_module/utils/date_time_helper.dart';
const String _SHOP_KEY_ = "__life_float_banner__";
class MiniBannerModel extends BaseModel {
List<ActivityConfigEntity> _list = [];
ActivityConfigEntity floatBanner;
Future<void> getActivityConfigList() async {
_list = await AppNavRepository.get().getActivityConfigList("20");
List<ActivityConfigEntity> floats =
await AppNavRepository.get().getActivityConfigList("22");
if (floats.length > 0) {
floatBanner = floats[0];
}
notifyListeners();
}
List<ActivityConfigEntity> getList() {
return _list;
}
ActivityConfigEntity getFloatBannerEntity() {
if (floatBanner != null) {
String type = floatBanner.showType;
if (type != '2') {
String dateStr = StoreUtils.getInstance()
.getForKey(key: "$_SHOP_KEY_${floatBanner.id}");
if (type == '1' && dateStr != null) {
return null;
}
String nowDateStr =
DateUtil.formatDate(DateTime.now(), format: "yyyyMMdd");
if (type == '3' && dateStr == nowDateStr) {
return null;
}
}
}
return floatBanner;
}
void removeFloatBannerEntity([bool show = false]) {
if (floatBanner != null) {
String nowDateStr =
DateUtil.formatDate(DateTime.now(), format: "yyyyMMdd");
StoreUtils.getInstance()
.setKV(key: "$_SHOP_KEY_${floatBanner.id}", value: nowDateStr);
if (!show) {
floatBanner = null;
notifyListeners();
}
}
}
}
import 'package:common_module/utils/map_utils.dart';
import 'package:mvp/mvp.dart';
import 'package:life_repository/life_repository.dart';
import 'package:life_module/util/resource.dart';
import 'package:common_module/utils/amap_utils.dart';
import 'package:modal/modal.dart';
class Model extends BaseModel {
bool _hasPermission = false;
String _address = '';
String _city = '';
String _cityId = '';
String _latitude = '';
String _longitude = '';
bool _isClickCitySelect = false;
Future<void> findCityId() async {
_cityId = await CityRepository.get().getCityId(_city);
}
Future<void> getLocation() async {
LocationEntity location = await AmapUtils.getInstance().getLocation();
if((location?.address??'').isEmpty){
String myLocation = '${location.longitude},${location.latitude}';
location = await Res.convertLocation(entity:location,location:myLocation);
}
_latitude = location.latitude.toString();
_longitude = location.longitude.toString();
List<num> arr = MapUtils.getInstance()
.gcj02ToGps84(double.parse(_latitude), double.parse(_longitude));
_latitude = '${arr[0]}';
_longitude = '${arr[1]}';
_city = location.city;
_address = location.aoiName;
notifyListeners();
}
Future<void> ipConfigAddress() async {
var res = await AMapRepository.get().ipConfigAddress(key: Res.aWebMapKey);
List<String> rectangles = res.rectangle.split(new RegExp(r";|,"));
if (rectangles.length > 1) {
_latitude = rectangles[1];
_longitude = rectangles[0];
List<num> arr = MapUtils.getInstance()
.gcj02ToGps84(double.parse(_latitude), double.parse(_longitude));
_latitude = '${arr[0]}';
_longitude = '${arr[1]}';
}
_city = res.city;
_address = _city;
notifyListeners();
}
Future<void> findPermission() async {
_hasPermission = await AmapUtils.getInstance().hasPermission();
if (_hasPermission) {
_hasPermission = await AmapUtils.getInstance().isOpenGPS;
}
notifyListeners();
}
Future<void> reqPermission() async {
bool confirm = await Modal.showModal(
title: "温馨提示",
msg: "为给您提供更好的服务,需获取您的位置权限",
cancelBtnText: '拒绝',
confirmBtnText: '同意');
if (!confirm) {
return;
}
_hasPermission = await AmapUtils.getInstance().reqPermission();
if (_hasPermission) {
_hasPermission = await AmapUtils.getInstance().isOpenGPS;
if (!_hasPermission) {
confirm = await Modal.showModal(
title: "温馨提示",
msg: "为给您提供更好的服务,请打开GPS",
cancelBtnText: '拒绝',
confirmBtnText: '同意');
if (!confirm) {
return;
}
_hasPermission = await AmapUtils.getInstance().enableGPS();
}
}
notifyListeners();
}
void setPermission() {
_hasPermission = true;
notifyListeners();
}
bool getHasPermission() {
return _hasPermission;
}
void setLocationInfo(
String city, String address, String latitude, String longitude) {
_city = city;
_address = address;
_latitude = latitude;
_longitude = longitude;
notifyListeners();
}
String getAddres() {
return _address??'';
}
void setName(String name) {
this._address = name;
}
String getCityId() {
return _cityId;
}
String getCity() {
return _city;
}
String getLatitude() {
return _latitude;
}
String getLongitude() {
return _longitude;
}
bool isClickCitySelect() {
return _isClickCitySelect;
}
void setIsClickCitySelect(bool isc) {
_isClickCitySelect = isc;
}
}
import 'package:mvp/mvp.dart';
import 'package:life_repository/life_repository.dart';
class SecKillModel extends BaseModel {
bool _show = false;
List<SeckillSession> _seckillSessionList = [];
List<SecKillGoods> _secKillGoodsList = [];
bool isShow() {
return _show;
}
List<SeckillSession> getSeckillSessionList() {
// 测试用
// if (_seckillSessionList.length > 0) {
// var now = DateTime.now();
// now = now.add(Duration(seconds: 10));
// var s = now.second < 10 ? '0${now.second}' : now.second;
// var i = now.minute < 10 ? '0${now.minute}' : now.minute;
// now = now.add(Duration(seconds: 10));
// var s1 = now.second < 10 ? '0${now.second}' : now.second;
// var i1 = now.minute < 10 ? '0${now.minute}' : now.minute;
// _seckillSessionList[0].text = "${now.hour}:$i:$s-${now.hour}:$i1:$s1";
// now = now.add(Duration(seconds: 20));
// s1 = now.second < 10 ? '0${now.second}' : now.second;
// i1 = now.minute < 10 ? '0${now.minute}' : now.minute;
// now = now.add(Duration(seconds: 20));
// var s2 = now.second < 10 ? '0${now.second}' : now.second;
// var i2 = now.minute < 10 ? '0${now.minute}' : now.minute;
// _seckillSessionList[1].text = "${now.hour}:$i1:$s1-${now.hour}:$i2:$s2";
// now = now.add(Duration(seconds: 15));
// s2 = now.second < 10 ? '0${now.second}' : now.second;
// i2 = now.minute < 10 ? '0${now.minute}' : now.minute;
// now = now.add(Duration(seconds: 15));
// var s3 = now.second < 10 ? '0${now.second}' : now.second;
// var i3 = now.minute < 10 ? '0${now.minute}' : now.minute;
// _seckillSessionList[2].text = "${now.hour}:$i2:$s2-${now.hour}:$i3:$s3";
// }
return _seckillSessionList;
}
List<SecKillGoods> getSecKillGoodsList() {
return _secKillGoodsList;
}
Future<void> findDisplay(String cityId) async {
_show = await SecKillRepository.get().findSeckillSwitch();
if (_show) {
findSeckillSession(cityId);
}
}
Future<void> findSeckillSession(String cityId) async {
_seckillSessionList =
await SecKillRepository.get().findSeckillSession(cityId, '1');
notifyListeners();
}
Future<void> loadSecKillGoodsByShowId(
String cityId, String showId, String lat, String lng) async {
// if (_secKillGoodsList.length > 0) {
// _secKillGoodsList.clear();
// notifyListeners();
// }
_secKillGoodsList = await SecKillRepository.get()
.findSeckillGoodsList(cityId, showId, lat, lng, 1);
notifyListeners();
}
}
import 'package:flutter/material.dart';
import 'package:common_module/base/base_umeng_page_view_item_state.dart';
import './components/title_bar.dart';
import './components/search_area.dart';
import 'package:common_module/utils/xapp_utils.dart';
import './components/scroll_body.dart';
import 'package:mvp/providers/provider.dart';
import './actions/action.dart';
import './components/float_banner.dart';
import 'package:common_module/widget/scroll_top_fixed_widget/widget.dart';
class LifeWidget extends StatefulWidget {
final String isAuditMode;
LifeWidget({Key key,@required this.isAuditMode}) : super(key: key);
@override
State<StatefulWidget> createState() => _LifeState();
}
class _LifeState extends BaseUmengPageViewItemState<LifeWidget>
with TickerProviderStateMixin {
ValueNotifier<double> scrollOffset = ValueNotifier<double>(0.0);
IAction iAction = IAction();
final ValueNotifier<bool> floatBannerNotifier = ValueNotifier(true);
ScrollToModel scrollToModel = ScrollToModel();
@override
void initState() {
super.initState();
scrollToModel.addScrollListener();
}
@override
Widget build(BuildContext context) {
super.build(context);
return Scaffold(
backgroundColor: rgba(245, 245, 245, 1),
floatingActionButton: ScrollToTopWidget(
model: scrollToModel,
),
body: SafeArea(
bottom: false,
child: ActionProvider(
action: iAction,
builder: (context, child) {
return Stack(
children: [
Positioned(
top: 44.rpx,
left: 0,
right: 0,
bottom: 0,
child: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification notification) {
switch (notification.runtimeType) {
case ScrollStartNotification:
floatBannerNotifier.value = false;
break;
case ScrollEndNotification:
floatBannerNotifier.value = true;
break;
}
return true;
},
child: ScrollBody(
isAuditMode: widget.isAuditMode,
iAction: iAction,
controller: scrollToModel.scrollController,
onScroll: (offset) {
scrollOffset.value = offset;
},
)),
),
Positioned(
top: 0,
left: 0,
right: 0,
child: TitleBar(),
),
Positioned(
top: 0,
left: 0,
right: 0,
child: SearchArea(
scrollOffset: scrollOffset,
iAction: iAction,
),
),
Positioned(
right: 10.rpx,
bottom: 100.rpx,
child: ValueListenableBuilder<bool>(
valueListenable: floatBannerNotifier,
builder: (BuildContext context, bool isScrollTStop,
Widget child) {
return AnimatedContainer(
duration: Duration(milliseconds: 100),
transform: Matrix4.translationValues(
isScrollTStop ? 0 : 60.rpx, 0, 0),
child: FloatBanner(
model: iAction.getMiniBannerModel(),
),
);
}),
),
],
);
},
),
),
);
}
@override
void onResume() async {
super.onResume();
if (!iAction.getModel().getHasPermission() &&
!iAction.getModel().isClickCitySelect()) {
await iAction.getModel().findPermission();
if (iAction.getModel().getHasPermission()) {
iAction.initLocation();
}
}
if (iAction.getModel().isClickCitySelect()) {
iAction.getModel().setIsClickCitySelect(false);
}
}
@override
String getPageViewName() {
return 'entry_life_widget';
}
@override
Map<String, String> getPageViewArgs() {
return {};
}
}
import 'package:mvp/mvp.dart';
import '../models/model.dart';
import 'package:life_module/models/filter_model.dart';
import 'package:life_module/models/shop_list_model.dart';
import 'package:common_module/utils/loading_dialog_utils.dart';
class IAction extends BaseAction<Model> {
String _cityId = '';
String _latitude = '';
String _longitude = '';
String _cityName = '';
FilterModel _filterModel = FilterModel();
ShopListModel _shopListModel = ShopListModel();
IAction(this._cityId, this._latitude, this._longitude, this._cityName);
@override
void initState() {
_init();
}
void _init() async {
setModel(Model(_cityId, _latitude, _longitude, _cityName));
if (_cityId != null && _cityId.isNotEmpty) {
await getFilterModel().getFilterList(_cityId);
}
}
Future<void> refreshShopList() async {
var loading = LoadingDialogUtils.getInstance().show();
Future.delayed(Duration(seconds: 2)).then((value) {
if (loading != null) {
loading.close();
}
});
try {
getShopListModel()
.setShopTypeNavIndex(getFilterModel().getShopTypeNavIndex());
getShopListModel().setCityId(getModel().getCityId());
getShopListModel().setLat(getModel().getLatitude());
getShopListModel().setLng(getModel().getLongitude());
getShopListModel()
.setFirstCateId(getFilterModel().getCheckedOneCategory().id);
getShopListModel()
.setSecondCateId(getFilterModel().getCheckedTwoCategory().id ?? "");
if (getFilterModel().getCheckedTwoRegion().id == null) {
getShopListModel()
.setRegionId(getFilterModel().getCheckedOneRegion().id);
} else {
getShopListModel()
.setRegionId(getFilterModel().getCheckedTwoRegion().id);
}
getShopListModel().setSortType(getFilterModel().getCheckedOneSort().id);
getShopListModel().clear();
await getShopListModel().refresh();
getModel().notifyListeners();
} catch (e) {}
if (loading != null) {
loading.close();
}
}
void refresh() async {
String cityId = getModel().getCityId();
if (cityId != null &&
cityId.isNotEmpty &&
getFilterModel().getCheckedOneCategory() != null) {
await refreshShopList();
}
}
FilterModel getFilterModel() {
return _filterModel;
}
ShopListModel getShopListModel() {
return _shopListModel;
}
}
import 'package:flutter/material.dart';
import 'package:life_module/components/persistent_header_delegate.dart';
import 'package:life_module/components/shop_type_nav.dart';
import 'package:life_module/components/filter_area.dart';
import 'package:life_module/components/shop_class_nav.dart';
import 'package:life_module/components/shop_list.dart';
import '../actions/action.dart';
import 'package:flutter/rendering.dart';
import 'package:common_module/widget/pull_widget/widget.dart';
import 'package:mvp/providers/provider.dart';
import 'package:common_module/utils/list_model_status_utils.dart';
import 'package:common_module/utils/navigate_utils.dart';
import 'package:life_module/route/index.dart';
import 'package:common_module/utils/xapp_utils.dart';
class ScrollBody extends StatefulWidget {
final IAction iAction;
ScrollBody(this.iAction);
@override
State<StatefulWidget> createState() => _ScrollBodyState();
}
class _ScrollBodyState extends State<ScrollBody> {
IAction get iAction => widget.iAction;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(
child: QMProvider.value(
model: iAction.getShopListModel(),
builderWidget: (context, model, child) {
return PullWidget(
headerInsertIndex: 3,
controller:iAction.getShopListModel().getRefreshController(),
onLoad: () {
iAction.getShopListModel().getData();
},
onRefresh: () {
iAction.getShopListModel().refresh();
},
child: CustomScrollView(
slivers: [
SliverPersistentHeader(
pinned: false,
delegate: PersistentHeaderDelegate(
minHeight: 37.rpx,
maxHeight: 37.rpx,
child: ShopTypeNav(
model: iAction.getFilterModel(),
onChange: () {
iAction.refreshShopList();
},
),
),
),
SliverPersistentHeader(
pinned: true,
delegate: PersistentHeaderDelegate(
minHeight: 48.rpx,
maxHeight: 48.rpx,
child: FilterArea(
topOffset: 81.rpx - 37.rpx,
model: widget.iAction.getFilterModel(),
onTap: (c) {
var _context =
widget.iAction.getShopListModel().getContext();
RenderSliver renderSliver = _context
.findAncestorRenderObjectOfType<RenderSliver>();
if (renderSliver != null &&
renderSliver.constraints.overlap <= 0) {
Scrollable.ensureVisible(_context);
}
},
onChange: () {
widget.iAction.refreshShopList();
},
))),
SliverPersistentHeader(
pinned: true,
delegate: PersistentHeaderDelegate(
minHeight: 40.rpx,
maxHeight: 40.rpx,
child: ShopClassNav(
model: widget.iAction.getFilterModel(),
onChange: () {
widget.iAction.refreshShopList();
},
))),
Builder(
builder: (context) {
Widget statusWidget =
ListModelStatusUtils.getInstance().form(
model: model,
onTap: () {
iAction.refresh();
});
if (statusWidget != null) {
return SliverToBoxAdapter(
child: statusWidget,
);
}
return ShopList(
model: iAction.getShopListModel(),
onReset: () {
widget.iAction.getFilterModel().reset();
widget.iAction.refreshShopList();
},
onTap: (item) {
NavigateUtils.push(
path: LIFE_SHOP_PAGE,
arguments: {
'shopId': item.combo != null
? item.combo.shopId
: item.restaurant.shopId,
'lat': widget.iAction
.getModel()
.getLatitude(),
'lng': widget.iAction
.getModel()
.getLongitude(),
'firstCateId': widget.iAction
.getFilterModel()
.getCheckedOneCategory()
.id,
'itemId': item.combo != null
? item.combo.dealId
: "",
"cityName": widget.iAction
.getModel()
.getCityName()
},
isNative: false,
);
},
);
},
)
],
),
);
}),
)
;
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_module/components/life_image.dart';
import '../../../util/search_history_utils.dart';
class SearchHistory extends StatefulWidget {
final Function(String) onTap;
SearchHistory({this.onTap});
@override
State<StatefulWidget> createState() => SearchHistoryState();
}
class SearchHistoryState extends State<SearchHistory> {
Function(String) get onTap => widget.onTap;
List<String> words = [];
@override
void initState() {
super.initState();
words = SearchHistoryUtils.getInstance().getWords();
}
@override
Widget build(BuildContext context) {
if (words.length == 0) {
return Container();
}
List<Widget> items = words.map((e) {
String text = e;
if (text.length > 5) {
text = "${text.substring(0, 5)}...";
}
return InkWell(
child: Container(
decoration: BoxDecoration(
color: rgba(248, 248, 247, 1),
borderRadius: BorderRadius.circular(14.rpx)),
padding: EdgeInsets.symmetric(horizontal: 12.rpx, vertical: 6.rpx),
child: Text(
text,
style: TextStyle(
color: rgba(102, 102, 102, 1), fontSize: 12.rpx, height: 1.rpx),
),
),
onTap: () {
onTap(e);
},
);
}).toList();
return Container(
color: Colors.white,
padding: EdgeInsets.symmetric(horizontal: 13.rpx),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 24.rpx,
),
Container(
height: 21.rpx,
child: Row(
children: [
Container(
height: 21.rpx,
child: Text(
"搜索历史",
style: TextStyle(
color: rgba(51, 51, 51, 1),
fontSize: 15.rpx,
fontWeight: FontWeight.bold),
),
),
Spacer(),
InkWell(
onTap: () {
setState(() {
words = [];
});
SearchHistoryUtils.getInstance().clear();
},
child: Container(
height: 21.rpx,
child: Row(
children: [
LifeImage(
name: "assets/images/ic_clear.png",
width: 18.rpx,
height: 18.rpx,
),
Text(
"清除",
style: TextStyle(
color: rgba(153, 153, 153, 1),
fontSize: 13.rpx,
),
)
],
),
),
)
],
),
),
SizedBox(
height: 12.rpx,
),
Expanded(
child: Container(
child: SingleChildScrollView(
child: Wrap(
spacing: 8.rpx,
runSpacing: 8.rpx,
crossAxisAlignment: WrapCrossAlignment.start,
children: items,
),
),
),
)
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_repository/life_repository.dart';
class SearchTips extends StatelessWidget {
final String keywords;
final List<SearchTipsItem> items;
final Function(SearchTipsItem) onTap;
SearchTips({this.items, this.keywords, this.onTap});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 13.rpx),
itemCount: items.length,
itemBuilder: (context, index) {
var item = items[index];
List<String> names = item.name.split("");
List<TextSpan> childs = names.map((s) {
bool isKeyWordChar = keywords.indexOf(s) > -1;
return TextSpan(
text: s,
style: TextStyle(
color: isKeyWordChar
? rgba(255, 128, 0, 1)
: rgba(48, 38, 0, 1)));
}).toList();
return InkWell(
child: Container(
height: 40.rpx,
alignment: Alignment.centerLeft,
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: rgba(151, 151, 151, 0.2), width: 0.5.rpx))),
child: Text.rich(
TextSpan(children: childs),
style: TextStyle(fontSize: 13.rpx),
),
),
onTap: () {
onTap(item);
},
);
}),
);
}
}
import 'package:mvp/mvp.dart';
import 'dart:async';
import 'package:life_repository/life_repository.dart';
class Model extends BaseModel {
String _cityId = '';
String _latitude = '';
String _longitude = '';
String keywords = '';
String _cityName;
Timer _searchTipsTimer;
List<SearchTipsItem> _searchTipsList = [];
Model(this._cityId, this._latitude, this._longitude, this._cityName);
String getCityId() {
return _cityId;
}
String getLatitude() {
return _latitude;
}
String getLongitude() {
return _longitude;
}
void findSearchTips(String firstCateId, String words) {
keywords = words;
if (keywords.isEmpty) {
clearSearchTipsList();
}
if (_searchTipsTimer != null && _searchTipsTimer.isActive) {
_searchTipsTimer.cancel();
_searchTipsTimer = null;
}
_searchTipsTimer =
Timer.periodic(Duration(milliseconds: 500), (timer) async {
_searchTipsTimer.cancel();
_searchTipsTimer = null;
List<SearchTipsItem> list = await FilterArepository.get().findSearchTips(
_cityId, _latitude, _longitude, firstCateId, keywords);
if (keywords != null && keywords.isNotEmpty) {
_searchTipsList = list;
notifyListeners();
}
});
}
List<SearchTipsItem> getSearchTipsList() {
return _searchTipsList;
}
void clearSearchTipsList() {
keywords = '';
_searchTipsList = [];
notifyListeners();
}
String getKeywords() {
return keywords;
}
String getCityName() {
return _cityName;
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:common_module/components/i_app_bar.dart';
import 'package:life_module/components/search_bar.dart';
import 'package:life_module/utils/filter_panel_utils.dart';
import './actions/action.dart';
import 'package:flutter/rendering.dart';
import 'package:mvp/providers/provider.dart';
import './components/scroll_body.dart';
import './components/search_tips.dart';
import './components/search_history.dart';
import '../../util/search_history_utils.dart';
class LifeSearchPage extends StatefulWidget {
final Map<String, dynamic> param;
LifeSearchPage({this.param});
@override
State<StatefulWidget> createState() => _SearchPageState();
}
class _SearchPageState extends State<LifeSearchPage>
with TickerProviderStateMixin {
IAction iAction;
FocusNode focusNode = FocusNode();
TextEditingController controller = TextEditingController();
@override
void initState() {
super.initState();
iAction = IAction(widget.param['cityId'], widget.param['latitude'],
widget.param['longitude'],
widget.param['cityName']);
Future.delayed(Duration(milliseconds: 300)).then((value) {
focusNode.requestFocus();
});
}
@override
Widget build(BuildContext context) {
return WillPopScope(child:Scaffold(
appBar: IAppBar(
leading: true,
elevation: 0,
color: Colors.white,
child: Container(
padding: EdgeInsets.only(left: 42.rpx, right: 13.rpx),
child: SearchBar(
enabled: true,
focusNode: focusNode,
controller: controller,
onChanged: (s) {
if (iAction.getFilterModel().getCheckedOneCategory() != null) {
iAction.getModel().findSearchTips(
iAction.getFilterModel().getCheckedOneCategory().id, s);
}
if (s.isEmpty) {
iAction.getShopListModel().setKeywords("");
iAction.getShopListModel().clear();
}
},
onSubmitted: (s) {
search(s);
},
),
),
),
body: ActionProvider(
action: iAction,
builder: (context, model) {
if (iAction.getModel().getSearchTipsList().length > 0) {
return SearchTips(
items: iAction.getModel().getSearchTipsList(),
keywords: iAction.getModel().getKeywords(),
onTap: (item) {
controller.text = item.name;
iAction.getModel().clearSearchTipsList();
search(item.name);
},
);
} else if (iAction.getShopListModel().getKeywords().isNotEmpty) {
return ScrollBody(iAction);
}
return SearchHistory(
onTap: (s) {
search(s);
},
);
},
),
),onWillPop:(){
if(FilterPanelUtils.overlayEntry != null){
FilterPanelUtils.close();
return Future.value(false);
}
return Future.value(true);
},);
}
void search(String wrods) {
controller.text = wrods;
iAction.getModel().clearSearchTipsList();
SearchHistoryUtils.getInstance().push(wrods);
focusNode.unfocus();
iAction.getShopListModel().setKeywords(wrods);
if (wrods.isNotEmpty) {
iAction.refresh();
} else {
iAction.getShopListModel().clear();
}
}
}
import 'package:mvp/mvp.dart';
import '../models/sec_kill_model.dart';
import '../models/model.dart';
import '../models/banner_model.dart';
class IAction extends BaseAction<Model> {
String _cityId;
String _showId;
String _lat;
String _lng;
String _cityName;
SecKillModel _killModel;
BannerModel _bannerModel;
IAction(this._cityId, this._showId, this._lat, this._lng, this._cityName);
@override
void initState() {
_killModel = SecKillModel(_cityId, _showId, _lat, _lng);
_bannerModel = BannerModel();
setModel(Model());
init();
}
void init() async {
await getModel().findSeckillSession(_cityId);
if (_showId == null || _showId.isEmpty) {
_showId = getModel().getSeckillSessionList()[0].id;
}
getSecKillModel().setShowId(_showId);
getSecKillModel().setCityId(_cityId);
getSecKillModel().setLat(_lat);
getSecKillModel().setLng(_lng);
refresh();
getModel().findPermission();
}
void refresh() async {
await getBannerModel().getActivityConfigList();
await getSecKillModel().refresh();
}
void setShowId(String id) {
_showId = id;
getSecKillModel().setShowId(id);
getSecKillModel().clear();
getSecKillModel().refresh();
}
void setLocaltionInfo(String cityId, String lat, String lng) {
_cityId = cityId;
_lat = lat;
_lng = lng;
_showId = null;
getSecKillModel().clear();
getModel().getSeckillSessionList().clear();
getModel().notifyListeners();
init();
}
String getCityName() {
return _cityName;
}
SecKillModel getSecKillModel() {
return _killModel;
}
BannerModel getBannerModel() {
return _bannerModel;
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import '../models/banner_model.dart';
import 'package:common_module/widget/xiaoxiong_base_image_widget/widget.dart';
import 'package:mvp/mvp.dart';
import 'package:life_module/utils/activity_config_tap_utils.dart';
import 'package:flutter_swiper/flutter_swiper.dart';
class AdBanner extends StatelessWidget {
final BannerModel model;
AdBanner(this.model);
@override
Widget build(BuildContext context) {
ValueNotifier<int> scrollIndex = ValueNotifier<int>(0);
return SliverToBoxAdapter(
child: QMProvider<BannerModel>.value(
model: model,
builderWidget: (context, model, child) {
if (model.getList().length == 0) {
return SizedBox();
}
return Container(
padding: EdgeInsets.symmetric(horizontal: 13.rpx),
height: 90.rpx,
child: Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(8.rpx),
child: Swiper(
key: UniqueKey(),
itemCount: model.getList().length,
autoplay: model.getList().length <= 1,
loop: model.getList().isNotEmpty &&
model.getList().length > 1,
onTap: (index) {
var item = model.getList()[index];
AppTapConfigUtils.getInstance().activityConfigTap(item.toJson());
},
onIndexChanged: (index) {
scrollIndex.value = index;
},
itemBuilder: (context, index) {
var item = model.getList()[index];
return XiaoxiongBaseImageWidget(
imageUrl: item.imgUrl,
height: 90.rpx,
fit: BoxFit.cover,
);
},
),
),
Positioned(
bottom: 5.rpx,
left: 0,
right: 0,
child: _SecKillPagination(
scrollIndex: scrollIndex,
length: model.getList().length,
),
)
],
));
},
),
);
}
}
/// 指示器
class _SecKillPagination extends StatelessWidget {
final int length;
final ValueNotifier<int> scrollIndex;
_SecKillPagination({this.scrollIndex, this.length});
@override
Widget build(BuildContext context) {
if (length == 0) {
return SizedBox(
height: 4.rpx,
);
}
return ValueListenableBuilder<int>(
valueListenable: scrollIndex,
builder: (BuildContext context, dynamic offset, Widget child) {
List<Widget> items = List.generate((length).ceil(), (index) {
Color color = rgba(255, 255, 255, 0.5);
if (index == scrollIndex.value) {
color = rgba(255, 255, 255, 1);
}
return Container(
width: 4.rpx,
height: 4.rpx,
margin: EdgeInsets.symmetric(horizontal: 2.rpx),
decoration: BoxDecoration(
color: color, borderRadius: BorderRadius.circular(2.rpx)),
);
});
return Container(
height: 4.rpx,
alignment: Alignment.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: items,
),
);
});
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_module/components/life_image.dart';
import '../models/sec_kill_model.dart';
import 'package:mvp/mvp.dart';
import 'package:life_repository/life_repository.dart';
import 'package:common_module/widget/xiaoxiong_base_image_widget/widget.dart';
import 'package:common_module/utils/meituan_utils.dart';
import 'package:common_module/utils/list_model_status_utils.dart';
import '../actions/action.dart';
import '../../../utils/buy_loading_dialog_utils.dart';
import 'package:common_module/utils/image_preview_utils.dart';
class GoodsItems extends StatelessWidget {
final IAction iAction;
GoodsItems(this.iAction);
@override
Widget build(BuildContext context) {
return SliverPadding(
padding: EdgeInsets.zero,
sliver: QMProvider<SecKillModel>.value(
model: iAction.getSecKillModel(),
builderWidget: (context, model, child) {
Widget statusWidget = ListModelStatusUtils.getInstance().form(
model: model,
onTap: () {
model.refresh();
});
if (statusWidget != null) {
return SliverToBoxAdapter(
child: statusWidget,
);
}
return SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return _GoodsItem(
enabled: index == 1,
item: model.getList()[index],
iAction: iAction,
);
},
childCount: model.getList().length,
));
},
));
}
}
class _GoodsItem extends StatelessWidget {
final SecKillGoods item;
final bool enabled;
final IAction iAction;
_GoodsItem({this.enabled = false, this.item, this.iAction});
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(left: 13.rpx, right: 13.rpx, bottom: 8.rpx),
padding: EdgeInsets.only(
left: 12.rpx,
right: 12.rpx,
top: 12.rpx,
),
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(8.rpx)),
child: Column(
children: [
Container(
height: 102.rpx,
child: Row(
children: [
InkWell(
onTap: () {
ImagePreviewUtils.showIndex(
context: context,
images: [item.itemPic],
heroTag: item.itemPic);
},
child: ClipRRect(
borderRadius: BorderRadius.circular(8.rpx),
child: Container(
height: 90.rpx,
width: 90.rpx,
child: XiaoxiongBaseImageWidget(
imageUrl: item.itemPic,
height: 90.rpx,
width: 90.rpx,
fit: BoxFit.cover,
),
))),
SizedBox(
width: 8.rpx,
),
Expanded(
child: Stack(
children: [
Container(
height: 90.rpx,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_Title(item),
SizedBox(
height: 6.rpx,
),
_PriceInfo(item),
SizedBox(
height: 6.rpx,
),
_Commission(item),
SizedBox(
height: 6.rpx,
),
_Progress(item),
],
),
),
_BuyBtn(item),
],
),
),
],
),
),
_LocaionInfo(item, iAction),
],
),
);
}
}
/// 标题
class _Title extends StatelessWidget {
final SecKillGoods item;
_Title(this.item);
@override
Widget build(BuildContext context) {
return Container(
height: 21.rpx,
child: Row(
children: [
Expanded(
child: Container(
child: Text(
item.itemTitle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: rgba(48, 38, 0, 1),
fontSize: 15.rpx,
height: 1.rpx,
fontWeight: FontWeight.bold),
),
),
)
],
),
);
}
}
class _Commission extends StatelessWidget {
final SecKillGoods item;
_Commission(this.item);
@override
Widget build(BuildContext context) {
return Container(
height: 18.rpx,
padding: EdgeInsets.symmetric(horizontal: 4.rpx, vertical: 2.rpx),
decoration: BoxDecoration(
border: Border.all(color: rgba(255, 128, 0, 1), width: 0.5),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(9.rpx),
topRight: Radius.circular(9.rpx),
bottomRight: Radius.circular(9.rpx))),
child: Text(
item.userCommissionText,
style: TextStyle(
color: rgba(255, 128, 0, 1), fontSize: 11.rpx, height: 1.2),
),
);
}
}
/// 价格信息
class _PriceInfo extends StatelessWidget {
final SecKillGoods item;
_PriceInfo(this.item);
@override
Widget build(BuildContext context) {
return Container(
height: 21.rpx,
child: Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
children: [
Text(
"¥",
style: TextStyle(
color: rgba(255, 4, 0, 1),
fontSize: 11.rpx,
),
),
Text(
item.endPrice,
style: TextStyle(
color: rgba(255, 4, 0, 1), fontSize: 18.rpx, height: 1.2.rpx),
),
SizedBox(
width: 4.rpx,
),
Container(
child: Text(
"秒杀价",
style: TextStyle(
color: rgba(255, 4, 0, 1), fontSize: 11.rpx, height: 1.rpx),
),
),
SizedBox(
width: 4.rpx,
),
Text(
${item.originalPrice}",
style: TextStyle(
color: rgba(153, 153, 153, 1),
fontSize: 11.rpx,
decoration: TextDecoration.lineThrough),
),
],
),
);
}
}
/// 马上抢
class _BuyBtn extends StatelessWidget {
final SecKillGoods item;
_BuyBtn(this.item);
@override
Widget build(BuildContext context) {
bool enabled = item.isRedirect == '2';
return Positioned(
right: 0,
bottom: 0,
width: 54.rpx,
height: 32.rpx,
child: InkWell(
onTap: () {
if (enabled) {
XAppUtils.getInstance().toast(item.menuText);
return;
}
var loading = BuyLoadingDialogUtils.getInstance().show();
Future.delayed(Duration(seconds: 1)).then((value) {
loading.close();
MeituanUtils.getInstance().openByUrl(item.couponUrl);
});
},
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: rgba(216, 216, 216, 1),
gradient: enabled
? null
: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [rgba(255, 128, 0, 1), rgba(255, 4, 0, 1)]),
borderRadius: BorderRadius.circular(20.rpx)),
child: Text(
item.menuText,
style: TextStyle(
color: rgba(255, 255, 255, 1),
fontSize: 13.rpx,
height: 1.2.rpx),
),
),
),
);
}
}
/// 进度
class _Progress extends StatelessWidget {
final SecKillGoods item;
_Progress(this.item);
@override
Widget build(BuildContext context) {
double maxWidth = 154.rpx;
double whiteTextWidth = 45.rpx;
double soldPercent = double.parse(item.soldPercent.replaceAll('%', ''));
double p = soldPercent / 100 * maxWidth;
bool showWhiteText = p >= whiteTextWidth;
return Container(
height: 12.rpx,
width: maxWidth,
child: Stack(
children: [
Container(
alignment: Alignment.centerLeft,
padding: EdgeInsets.only(left: p + 7.rpx),
decoration: BoxDecoration(
color: rgba(245, 245, 245, 1),
borderRadius: BorderRadius.circular(20.rpx)),
child: showWhiteText
? SizedBox()
: Text(
"已抢${item.soldPercent}",
style: TextStyle(
color: rgba(153, 153, 153, 1),
fontSize: 9.rpx,
height: 1.rpx),
),
),
Container(
height: 12.rpx,
width: p,
alignment: Alignment.centerLeft,
padding: EdgeInsets.only(left: 6.rpx),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [rgba(255, 128, 0, 1), rgba(255, 4, 0, 1)]),
borderRadius: BorderRadius.circular(20.rpx)),
child: !showWhiteText
? SizedBox()
: Text(
"已抢${item.soldPercent}",
maxLines: 1,
style: TextStyle(
color: Colors.white, fontSize: 9.rpx, height: 1.rpx),
),
)
],
),
);
}
}
/// 地址信息
class _LocaionInfo extends StatelessWidget {
final SecKillGoods item;
final IAction iAction;
_LocaionInfo(this.item, this.iAction);
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () async {
//String gps = await AmapUtils.getInstance() .addressToGps(item.address, iAction.getCityName());
//MapUtils.getInstance().open(gps, item.address);
},
child: Container(
height: 34.rpx,
padding: EdgeInsets.only(bottom: 4.rpx),
child: Row(
children: [
LifeImage(
name: "assets/images/sec_kill/ic_gps_gray.png",
width: 20.rpx,
height: 20.rpx,
),
Container(
constraints: BoxConstraints(maxWidth: 260.rpx),
child: Text(
item.address,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: rgba(102, 102, 102, 1),
fontSize: 11.rpx,
height: 1.rpx),
),
),
/*LifeImage(
name: "assets/images/life/ic_miaosha_arrow_right.png",
width: 8.rpx,
height: 8.rpx,
),*/
Spacer(),
Text(
item.distance,
style: TextStyle(
color: rgba(153, 153, 153, 1),
fontSize: 12.rpx,
height: 1.rpx),
)
],
),
));
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_module/components/life_image.dart';
import 'package:life_module/util/resource.dart';
import '../models/model.dart';
import 'package:common_module/utils/loading_dialog_utils.dart';
import 'package:common_module/base/base_route_widget_state.dart';
class LocaltionTips extends StatefulWidget {
final Model model;
final Function(String cityId, String lat, String lon) onLocationChange;
LocaltionTips({this.model, this.onLocationChange});
@override
State<StatefulWidget> createState() => LocaltionTipsState();
}
class LocaltionTipsState extends BaseRouteWeigetState<LocaltionTips> {
Model get model => widget.model;
@override
Widget build(BuildContext context) {
if (model.hasPermission()) {
return SliverToBoxAdapter(
child: SizedBox(),
);
}
return SliverToBoxAdapter(
child: Container(
height: 56.rpx,
margin: EdgeInsets.only(left: 13.rpx, top: 8.rpx, right: 13.rpx),
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(8.rpx)),
child: Row(
children: [
SizedBox(
width: 12.rpx,
),
Expanded(
child: Container(
height: 36.rpx,
child: Text(
"未获取定位权限,以下商品门店距您所在位置可能会存在偏差,建议您开启定位获取精准门店商品",
style: TextStyle(
color: rgba(48, 38, 0, 1),
fontSize: 11.rpx,
height: 1.6.rpx),
),
),
),
SizedBox(
width: 10.rpx,
),
InkWell(
onTap: () async {
await model.reqPermission();
hasPermission();
},
child: Container(
width: 64.rpx,
height: 26.rpx,
alignment: Alignment.center,
decoration: BoxDecoration(
color: rgba(255, 128, 0, 0.1),
borderRadius: BorderRadius.circular(8.rpx)),
child: Text(
"开启定位",
style: TextStyle(
color: rgba(255, 128, 0, 1),
fontSize: 12.rpx,
height: 1.rpx),
),
)),
InkWell(
onTap: () {
model.setPermission();
},
child: Container(
width: 17.rpx,
height: 56.rpx,
alignment: Alignment.topRight,
child: LifeImage(
name: "assets/images/sec_kill/ic_cycle_close.png",
width: 17.rpx,
height: 17.rpx,
),
))
],
),
),
);
}
void hasPermission() async {
if (model.hasPermission()) {
var loading = LoadingDialogUtils.getInstance().show();
try {
var location = await model.getLocation();
if((location?.address??'').isEmpty){
String myLocation = '${location.longitude},${location.latitude}';
location = await Res.convertLocation(entity:location,location:myLocation);
}
var cityId = await model.findCityId(location.city);
loading.close();
widget.onLocationChange(cityId, location.latitude.toString(),
location.longitude.toString());
} catch (e) {
loading.close();
}
}
}
@override
void onResume() async {
super.onResume();
if (!model.hasPermission()) {
await model.findPermission();
hasPermission();
}
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_repository/life_repository.dart';
import '../actions/action.dart';
import 'package:mvp/mvp.dart';
import '../models/sec_kill_model.dart';
class Times extends StatefulWidget {
final IAction iAction;
final List<SeckillSession> times;
final Function(String tid) onChange;
Times({this.iAction, this.times, this.onChange});
@override
State<StatefulWidget> createState() => _TimesState();
}
class _TimesState extends State<Times> {
ScrollController controller = ScrollController();
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return QMProvider<SecKillModel>.value(
model: widget.iAction.getSecKillModel(),
builderWidget: (context, module, child) {
return Container(
height: 64.rpx,
padding: EdgeInsets.only(bottom: 8.rpx),
color: rgba(245, 245, 245, 1),
child: ListView.builder(
physics: ClampingScrollPhysics(),
controller: controller,
padding: EdgeInsets.symmetric(horizontal: 9.rpx),
scrollDirection: Axis.horizontal,
itemBuilder: (context, i) {
var item = widget.times[i];
Color timeColor = rgba(48, 38, 0, 1);
Color statusColor = rgba(102, 102, 102, 1);
double timeSize = 20.rpx;
List<String> timeArr = item.text.split("-");
String timeText = timeArr[0];
String statusText = item.statusText;
bool isSelected =
widget.iAction.getSecKillModel().getShowId() == item.id;
if (isSelected) {
timeColor = Colors.white;
statusColor = Colors.white;
timeSize = 15.rpx;
timeText = item.text;
Future.delayed(Duration(milliseconds: 300)).then((value) {
Size size = MediaQuery.of(context).size;
controller.animateTo(
(108.rpx * i - size.width / 2 + 54.rpx).toDouble(),
duration: Duration(milliseconds: 300),
curve: Curves.linear);
});
}
return InkWell(
onTap: () {
if (widget.iAction.getSecKillModel().getShowId() == item.id) {
return;
}
if (widget.iAction.getSecKillModel().viewState !=
ViewState.idle) {
return;
}
widget.onChange(item.id);
},
child: Container(
width: 100.rpx,
height: 56.rpx,
margin: EdgeInsets.symmetric(horizontal: 4.rpx),
decoration: BoxDecoration(
color: Colors.white,
gradient: isSelected
? LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
rgba(255, 128, 0, 1),
rgba(255, 4, 0, 1)
])
: null,
borderRadius: BorderRadius.circular(8.rpx)),
child: Column(
children: [
SizedBox(
height: 10.rpx,
),
Container(
height: 18.rpx,
child: Text(
timeText,
style: TextStyle(
color: timeColor,
fontSize: timeSize,
height: 1.rpx,
fontWeight: FontWeight.bold),
),
),
SizedBox(
height: 6.rpx,
),
Container(
child: Text(
statusText,
style: TextStyle(
color: statusColor,
fontSize: 12.rpx,
height: 1.rpx),
),
)
],
),
),
);
},
itemCount: widget.times.length,
),
);
},
);
}
}
import 'package:mvp/mvp.dart';
import 'package:life_repository/life_repository.dart';
class BannerModel extends BaseModel {
List<ActivityConfigEntity> list = [];
Future<void> getActivityConfigList() async {
list = await AppNavRepository.get().getActivityConfigList("21");
notifyListeners();
}
List<ActivityConfigEntity> getList() {
return list;
}
}
import 'package:mvp/mvp.dart';
import 'package:common_module/utils/amap_utils.dart';
import 'package:modal/modal.dart';
import 'package:life_repository/life_repository.dart';
class Model extends BaseModel {
bool _hasPermission = true;
List<SeckillSession> _seckillSessionList = [];
Future<void> findPermission() async {
_hasPermission = await AmapUtils.getInstance().hasPermission();
if (_hasPermission) {
_hasPermission = await AmapUtils.getInstance().isOpenGPS;
}
notifyListeners();
}
bool hasPermission() {
return _hasPermission;
}
void setPermission() {
_hasPermission = true;
notifyListeners();
}
List<SeckillSession> getSeckillSessionList() {
return _seckillSessionList;
}
Future<LocationEntity> getLocation() {
return AmapUtils.getInstance().getLocation();
}
Future<String> findCityId(String city) async {
return CityRepository.get().getCityId(city);
}
Future<void> findSeckillSession(String cityId) async {
_seckillSessionList =
await SecKillRepository.get().findSeckillSession(cityId, '2');
notifyListeners();
}
Future<void> reqPermission() async {
bool confirm = await Modal.showModal(
title: "温馨提示",
msg: "为给您提供更好的服务,需获取您的位置权限",
cancelBtnText: '拒绝',
confirmBtnText: '同意');
if (!confirm) {
return;
}
_hasPermission = await AmapUtils.getInstance().reqPermission();
if (_hasPermission) {
_hasPermission = await AmapUtils.getInstance().isOpenGPS;
if (!_hasPermission) {
confirm = await Modal.showModal(
title: "温馨提示",
msg: "为给您提供更好的服务,请打开GPS",
cancelBtnText: '拒绝',
confirmBtnText: '同意');
if (!confirm) {
return;
}
_hasPermission = await AmapUtils.getInstance().enableGPS();
}
}
notifyListeners();
}
}
import 'package:mvp/mvp.dart';
import 'package:life_repository/life_repository.dart';
class SecKillModel extends BaseListModel<SecKillGoods> {
String _cityId;
String _showId;
String _lat;
String _lng;
SecKillModel(this._cityId, this._showId, this._lat, this._lng);
@override
Future<List<SecKillGoods>> request() async {
viewState = ViewState.busy;
var res = await SecKillRepository.get()
.findSeckillGoodsList(_cityId, _showId, _lat, _lng, getPage());
viewState = ViewState.idle;
return res;
}
void setShowId(String id) {
_showId = id;
}
String getShowId() {
return _showId;
}
void setCityId(String cityId) {
_cityId = cityId;
}
void setLat(String lat) {
_lat = lat;
}
void setLng(String lng) {
_lng = lng;
}
void setLocaltionInfo(String cityId, String lat, String lng) {
_cityId = cityId;
_lat = lat;
_lng = lng;
}
}
import 'package:flutter/material.dart';
import 'package:common_module/components/i_app_bar.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_module/components/life_image.dart';
import './components/times.dart';
import './components/ad_banner.dart';
import './components/localtion_tips.dart';
import './components/goods_items.dart';
import 'package:mvp/providers/provider.dart';
import './actions/action.dart';
import 'package:common_module/widget/pull_widget/widget.dart';
import 'package:flutter/cupertino.dart';
class LifeSecKillPage extends StatefulWidget {
final Map<String, dynamic> param;
LifeSecKillPage({this.param});
@override
State<StatefulWidget> createState() => _LifeSecKillPageState();
}
class _LifeSecKillPageState extends State<LifeSecKillPage> {
String get tid => widget.param['tid'];
IAction iAction;
@override
void initState() {
super.initState();
iAction = IAction(widget.param['cityId'], tid, widget.param['lat'],
widget.param['lng'], widget.param['cityName']);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: rgba(245, 245, 245, 1),
appBar: IAppBar(
elevation: 0,
color: rgba(245, 245, 245, 1),
child: Container(
alignment: Alignment.centerLeft,
padding: EdgeInsets.only(left: 45.rpx),
child: LifeImage(
name: 'assets/images/life/ic_miaosha.png',
width: 100.rpx,
height: 24.rpx,
),
),
),
body: ActionProvider(
action: iAction,
builder: (context, child) {
if (iAction.getModel().getSeckillSessionList().length == 0) {
return Container(
alignment: Alignment.center,
child: CupertinoActivityIndicator(radius: 10.rpx),
);
}
return Column(
children: [
Times(
iAction: iAction,
times: iAction.getModel().getSeckillSessionList(),
onChange: (tid) {
iAction.setShowId(tid);
},
),
Expanded(
child: Container(
child: PullWidget(
controller: iAction.getSecKillModel().getRefreshController(),
onLoad: () {
iAction.getSecKillModel().getData();
},
onRefresh: () {
iAction.refresh();
},
child: CustomScrollView(
slivers: [
AdBanner(iAction.getBannerModel()),
LocaltionTips(
model: iAction.getModel(),
onLocationChange: (cityId, lat, lon) {
iAction.setLocaltionInfo(cityId, lat, lon);
},
),
SliverToBoxAdapter(
child: Container(
height: 8.rpx,
),
),
GoodsItems(iAction),
],
),
)))
],
);
}),
);
}
}
import 'package:mvp/mvp.dart';
import '../models/model.dart';
import '../models/outer_info_model.dart';
class IAction extends BaseAction<Model> {
String shopId;
String lat;
String lng;
String firstCateId;
String itemId;
final String _cityName;
OuterInfoModel _outerInfoModel;
Function() load;
IAction(this.shopId, this.lat, this.lng, this.firstCateId, this.itemId,
this._cityName);
@override
void initState() {
setModel(Model(shopId, lat, lng, this._cityName,itemId));
_outerInfoModel = OuterInfoModel(shopId, firstCateId);
init();
}
void init() async {
await getModel().findShopDetail();
await getOuterInfoModel().findShopOuterInfo();
if (load != null) {
load();
}
}
void onLoad(Function() cb) {
load = cb;
}
String getFirstCateId() {
return firstCateId;
}
OuterInfoModel getOuterInfoModel() {
return _outerInfoModel;
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_module/components/life_image.dart';
class AllAppraise extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: Container(
height: 40.rpx,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(bottom: Radius.circular(8.rpx))),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
"查看全部评价",
style: TextStyle(
color: rgba(102, 102, 102, 1), fontSize: 12.rpx, height: 1.rpx),
),
LifeImage(
name: "assets/images/life/ic_miaosha_arrow_right.png",
width: 10.rpx,
height: 20.rpx,
),
SizedBox(
width: 13.rpx,
)
],
),
));
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_module/components/appraise_item.dart';
import 'package:life_module/views/shop/models/outer_info_model.dart';
import 'package:mvp/mvp.dart';
import 'package:flutter/cupertino.dart';
class AppraiseItems extends StatelessWidget {
final OuterInfoModel model;
AppraiseItems(this.model);
@override
Widget build(BuildContext context) {
return QMProvider<OuterInfoModel>.value(
model: model,
builderWidget: (context, model, child) {
if (model.getShopOuterInfoEntity() == null) {
return SliverToBoxAdapter(
child: Container(
alignment: Alignment.center,
color: Colors.white,
child: CupertinoActivityIndicator(
radius: 12,
),
));
}
var list = model.getShopOuterInfoEntity().commentItemEntitys;
if (list.length == 0) {
return SliverToBoxAdapter(
child: Container(
alignment: Alignment.center,
padding: EdgeInsets.only(bottom: 15.rpx,top: 10.rpx),
decoration: BoxDecoration(
color: Colors.white,
borderRadius:
BorderRadius.vertical(bottom: Radius.circular(8.rpx))),
child: Text(
"暂无评价",
style: TextStyle(color: Colors.black87, fontSize: 12.rpx),
),
));
}
return SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
bool isLast = index == list.length - 1;
return Column(
children: [
ClipRRect(
borderRadius:
isLast ? BorderRadius.circular(8.rpx) : BorderRadius.zero,
child: AppraiseItem(list[index]),
),
isLast
? SizedBox()
: Container(
height: 0.5.rpx,
padding: EdgeInsets.symmetric(horizontal: 12.rpx),
child: Container(
color: rgba(151, 151, 151, 0.1),
),
)
],
);
},
childCount: list.length,
));
},
);
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_module/utils/activity_config_tap_utils.dart';
class FooterExplain extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: SafeArea(
top: false,
child: Container(
child: Column(
children: [
InkWell(
onTap: () {
Map<String, dynamic> map = {'redirect_type': '11'};
AppTapConfigUtils.getInstance().activityConfigTap(map);
},
child: Container(
padding: EdgeInsets.only(top: 12.rpx, bottom: 4.rpx),
child: Text.rich(
TextSpan(text: "小熊有好货仅为您提供优惠渠道或", children: [
TextSpan(
text: "疑问解答",
style: TextStyle(color: rgba(255, 128, 0, 1))),
]),
style: TextStyle(
color: rgba(102, 102, 102, 1), fontSize: 11.rpx),
),
),
),
Container(
padding: EdgeInsets.only(bottom: 8.rpx),
child: Text(
"若发生退款或维权等有损自身利益情况请联系美团工作人员进行处理",
style:
TextStyle(color: rgba(102, 102, 102, 1), fontSize: 11.rpx),
),
)
],
),
),
));
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_module/views/shop/models/model.dart';
import 'package:life_module/components/type_icon.dart';
import 'package:life_repository/life_repository.dart';
import 'package:common_module/widget/xiaoxiong_base_image_widget/widget.dart';
import 'package:common_module/utils/meituan_utils.dart';
import '../../../utils/buy_loading_dialog_utils.dart';
import 'package:common_module/utils/image_preview_utils.dart';
class GoodsItems extends StatelessWidget {
final Model model;
GoodsItems(this.model);
@override
Widget build(BuildContext context) {
return SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return _GoodsItem(
isFirst: index == 0,
isLast: index == 1,
item: model.getShopInfoEntity().shopCouponItems[index],
);
},
childCount: model.getShopInfoEntity().shopCouponItems.length,
));
}
}
class _GoodsItem extends StatelessWidget {
final bool isFirst;
final bool isLast;
final ShopCouponItem item;
_GoodsItem({this.isFirst, this.isLast, this.item});
@override
Widget build(BuildContext context) {
double top = isFirst ? 16.rpx : 12.rpx;
return Container(
height: 90.rpx + top,
padding: EdgeInsets.only(
left: 12.rpx, right: 12.rpx, top: top, bottom: 16.rpx),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(bottom: Radius.circular(isLast ? 8.rpx : 0))),
child: Row(
children: [
InkWell(
child: ClipRRect(
borderRadius: BorderRadius.circular(8.rpx),
child: Container(
height: 74.rpx,
width: 74.rpx,
child: XiaoxiongBaseImageWidget(
imageUrl: item.itemImg,
height: 74.rpx,
width: 74.rpx,
fit: BoxFit.cover,
),
)),
onTap: () {
ImagePreviewUtils.showIndex(
context: context,
images: [item.itemImg],
heroTag: item.itemImg);
},
),
SizedBox(
width: 12.rpx,
),
Expanded(
child: Stack(
children: [
Container(
height: 74.rpx,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_Title(item),
SizedBox(
height: 6.rpx,
),
_Commission(item),
SizedBox(
height: 8.rpx,
),
_PriceInfo(item),
],
),
),
_BuyBtn(item),
],
),
),
],
),
);
}
}
/// 标题
class _Title extends StatelessWidget {
final ShopCouponItem item;
_Title(this.item);
@override
Widget build(BuildContext context) {
return Container(
height: 21.rpx,
child: Row(
children: [
TypeIcon(
type: int.parse(item.dealType),
),
SizedBox(
width: 4.rpx,
),
Expanded(
child: Container(
child: Text(
item.itemTitle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: rgba(48, 38, 0, 1),
fontSize: 15.rpx,
height: 1.rpx,
fontWeight: FontWeight.bold),
),
),
)
],
),
);
}
}
class _Commission extends StatelessWidget {
final ShopCouponItem item;
_Commission(this.item);
@override
Widget build(BuildContext context) {
if (double.parse(item.userCommission) == 0) {
Container(height: 18.rpx);
}
return Container(
height: 18.rpx,
padding: EdgeInsets.symmetric(horizontal: 4.rpx, vertical: 2.rpx),
decoration: BoxDecoration(
border: Border.all(color: rgba(255, 128, 0, 1), width: 0.5),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(9.rpx),
topRight: Radius.circular(9.rpx),
bottomRight: Radius.circular(9.rpx))),
child: Text(
item.userCommissionText,
style: TextStyle(
color: rgba(255, 128, 0, 1), fontSize: 11.rpx, height: 1.2),
),
);
}
}
/// 价格信息
class _PriceInfo extends StatelessWidget {
final ShopCouponItem item;
_PriceInfo(this.item);
@override
Widget build(BuildContext context) {
return Container(
height: 21.rpx,
child: Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
children: [
Text(
"¥",
style: TextStyle(
color: rgba(255, 4, 0, 1),
fontSize: 11.rpx,
),
),
Text(
item.endPrice,
style: TextStyle(
color: rgba(255, 4, 0, 1),
fontSize: 16.rpx,
),
),
SizedBox(
width: 4.rpx,
),
Text(
${item.originalPrice}",
style: TextStyle(
color: rgba(153, 153, 153, 1),
fontSize: 12.rpx,
decoration: TextDecoration.lineThrough),
),
SizedBox(
width: 8.rpx,
),
Container(
padding: EdgeInsets.symmetric(horizontal: 4.rpx, vertical: 3.rpx),
decoration: BoxDecoration(
color: rgba(255, 128, 0, 0.06),
borderRadius: BorderRadius.circular(34.rpx)),
child: Text(
"${item.discountPercent}折",
style: TextStyle(
color: rgba(255, 128, 0, 1), fontSize: 11.rpx, height: 1.rpx),
),
)
],
),
);
}
}
class _BuyBtn extends StatelessWidget {
final ShopCouponItem item;
_BuyBtn(this.item);
@override
Widget build(BuildContext context) {
return InkWell(
child: Container(
alignment: Alignment.centerRight,
child: Container(
width: 54.rpx,
height: 32.rpx,
alignment: Alignment.center,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [rgba(255, 128, 0, 1), rgba(255, 4, 0, 1)]),
borderRadius: BorderRadius.circular(20.rpx)),
child: Text(
"抢购",
style: TextStyle(
color: rgba(255, 255, 255, 1), fontSize: 13.rpx, height: 1.rpx),
),
),
),
onTap: () {
var loading = BuyLoadingDialogUtils.getInstance().show();
Future.delayed(Duration(seconds: 1)).then((value) {
loading.close();
MeituanUtils.getInstance().openByUrl(item.couponUrl);
});
},
);
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
class ItemBar extends StatelessWidget {
final GlobalKey globalKey;
final String text;
ItemBar(this.globalKey, this.text);
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: Container(
key: globalKey,
height: 34.rpx,
padding: EdgeInsets.only(top: 12.rpx, left: 12.rpx),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(8.rpx))),
child: Text(
text,
style: TextStyle(
color: rgba(48, 38, 0, 1),
fontSize: 15.rpx,
fontWeight: FontWeight.bold),
),
));
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:navigation_bar/navigation_bar.dart';
import 'package:life_module/components/persistent_header_delegate.dart';
class NavBar extends StatelessWidget {
final TabController controller;
final Function(int) onChange;
NavBar({this.controller, this.onChange});
@override
Widget build(BuildContext context) {
return SliverPersistentHeader(
pinned: true,
delegate: PersistentHeaderDelegate(
minHeight: 60.rpx,
maxHeight: 60.rpx,
child: Container(
height: 60.rpx,
color: rgba(245, 245, 245, 1),
padding: EdgeInsets.symmetric(vertical: 16.rpx),
child: Container(
height: 28.rpx,
child: Row(
children: [
Expanded(
child: Container(
child: NavigationBar(
controller: controller,
items: ["优惠", "推荐","评价"],
selectColor: rgba(255, 128, 0, 1),
normalColor: rgba(48, 38, 0, 1),
selectStyle: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.rpx,
height: 1),
normalStyle: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.rpx,
height: 1,
),
isScrollable: true,
indicatorSize: TabBarIndicatorSize.label,
onTap: onChange,
indicator: RoundRectIndicator(
round: 14.rpx,
color: rgba(255, 128, 0, 1),
height: 2.rpx,
marginBottom: 0),
),
),
),
Container(
child: Text(
"到店订单次日查看",
style: TextStyle(
color: rgba(255, 128, 0, 1), fontSize: 12.rpx),
),
)
],
),
),
)),
);
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_module/views/shop/models/outer_info_model.dart';
import 'package:mvp/mvp.dart';
import 'package:common_module/widget/xiaoxiong_base_image_widget/widget.dart';
import 'package:flutter/cupertino.dart';
import 'package:common_module/utils/image_preview_utils.dart';
class Recommend extends StatelessWidget {
final OuterInfoModel model;
Recommend(this.model);
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: Container(
height: 132.rpx,
padding: EdgeInsets.symmetric(vertical: 16.rpx, horizontal: 6.rpx),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(bottom: Radius.circular(8.rpx))),
child: QMProvider<OuterInfoModel>.value(
model: model,
builderWidget: (context, model, child) {
if (model.getShopOuterInfoEntity() == null) {
return Container(
alignment: Alignment.center,
child: CupertinoActivityIndicator(
radius: 12,
),
);
}
if (model.getShopOuterInfoEntity().recommendItemEntitys.length == 0) {
return Container(
alignment: Alignment.center,
padding: EdgeInsets.only(bottom: 15.rpx, top: 10.rpx),
decoration: BoxDecoration(
color: Colors.white,
borderRadius:
BorderRadius.vertical(bottom: Radius.circular(8.rpx))),
child: Text(
"暂无推荐",
style: TextStyle(color: Colors.black87, fontSize: 12.rpx),
),
);
}
List<Widget> items =
model.getShopOuterInfoEntity().recommendItemEntitys.map((e) {
return Expanded(
child: InkWell(
onTap: () {
ImagePreviewUtils.showIndex(
context: context,
images: [e.itemPic],
heroTag: e.itemPic);
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 6.rpx),
child: Column(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(8.rpx),
child: Hero(
tag: e.itemPic,
child: XiaoxiongBaseImageWidget(
height: 75.rpx,
width: 100.rpx,
imageUrl: e.itemPic,
fit: BoxFit.cover,
),
)),
SizedBox(
height: 8.rpx,
),
Container(
height: 17.rpx,
alignment: Alignment.centerLeft,
child: Text(
e.itemTitle,
style: TextStyle(
color: rgba(48, 38, 0, 1),
fontSize: 12.rpx,
fontWeight: FontWeight.bold),
),
)
],
),
),
),
);
}).toList();
return Row(
children: items,
);
},
),
));
}
}
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_module/components/life_image.dart';
import 'package:life_module/views/shop/models/model.dart';
import 'package:common_module/widget/xiaoxiong_base_image_widget/widget.dart';
import 'package:flutter_native_toast/flutter_native_toast.dart';
import 'package:common_module/utils/clipboard_utils.dart';
import 'package:common_module/utils/audit_mode_utils.dart';
import 'package:common_module/utils/image_preview_utils.dart';
class ShopInfo extends StatelessWidget {
final Model model;
ShopInfo(this.model);
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: ClipRRect(
borderRadius: BorderRadius.circular(8.rpx),
child: Container(
margin: EdgeInsets.only(top: 8.rpx),
color: Colors.white,
child: Column(
children: [
_ShopInfo(model),
SizedBox(
height: 12.rpx,
),
_Location(model),
AuditModeUtils.getInstance().isAuditMode()
? SizedBox()
: Container(
height: 48.rpx,
child: LifeImage(
name: "assets/images/shop/img_step.png",
height: 48.rpx,
),
)
],
),
)),
);
}
}
/// 店铺展示信息
class _ShopInfo extends StatelessWidget {
final Model model;
_ShopInfo(this.model);
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 12.rpx),
child: Column(
children: [
Container(
alignment: Alignment.centerLeft,
padding: EdgeInsets.only(top: 12.rpx, bottom: 10.rpx),
child: Text(
model.getShopInfoEntity().shopInfo.shopTitle,
style: TextStyle(
color: rgba(48, 38, 0, 1), fontSize: 18.rpx, height: 1.rpx,fontWeight:FontWeight.w600),
),
),
Container(
height: 80.rpx,
child: Row(
children: [
InkWell(
child: ClipRRect(
borderRadius: BorderRadius.circular(8.rpx),
child: Container(
height: 80.rpx,
width: 80.rpx,
child: XiaoxiongBaseImageWidget(
imageUrl: model.getShopInfoEntity().shopInfo.shopImg,
height: 80.rpx,
width: 80.rpx,
fit: BoxFit.cover,
),
)),
onTap: () {
ImagePreviewUtils.showIndex(
context: context,
images: [model.getShopInfoEntity().shopInfo.shopImg],
heroTag: model.getShopInfoEntity().shopInfo.shopImg);
},
),
SizedBox(
width: 12.rpx,
),
Expanded(
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_ScoreInfo(model),
SizedBox(
height: 4.rpx,
),
Container(
height: 16.rpx,
child: Text(
${model.getShopInfoEntity().shopInfo.avgPrice}/人",
style: TextStyle(
color: rgba(102, 102, 102, 1),
fontSize: 11.rpx),
),
),
SizedBox(
height: 4.rpx,
),
// Container(
// height: 16.rpx,
// child: Text(
// "季销${model.getShopInfoEntity().shopInfo.quarterVolume}",
// style: TextStyle(
// color: rgba(102, 102, 102, 1),
// fontSize: 11.rpx),
// ),
// ),
SizedBox(
height: 4.rpx,
),
Container(
height: 16.rpx,
child: Text(
"营业时间 ${model.getShopInfoEntity().shopInfo.businessTime}",
style: TextStyle(
color: rgba(48, 38, 0, 1), fontSize: 12.rpx),
),
)
],
),
),
)
],
),
),
],
),
);
}
}
/// 评分和距离信息
class _ScoreInfo extends StatelessWidget {
final Model model;
_ScoreInfo(this.model);
@override
Widget build(BuildContext context) {
var showStarScore = model.getShopInfoEntity().shopInfo.showStarScore;
List<InlineSpan> widgets = [];
double score = double.parse(showStarScore);
for (int star = 1, len = score.ceil(); star <= len; star++) {
widgets.add(WidgetSpan(
child: Image.asset(
star <= score
? "assets/ic_star_small_full.png"
: "assets/ic_star_small_half.png",
width: 12.rpx,
height: 12.rpx,
)));
}
for (int star = widgets.length; star < 5; star++) {
widgets.add(WidgetSpan(
child: Image.asset(
"assets/ic_star_small_blank.png",
width: 12.rpx,
height: 12.rpx,
)));
}
widgets.add(WidgetSpan(
child: SizedBox(
width: 2.rpx,
)));
return Container(
height: 18.rpx,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
alignment: Alignment.centerLeft,
height: 14.rpx,
child: Text.rich(TextSpan(children: widgets)),
),
Container(
child: Text(
double.parse(model.getShopInfoEntity().shopInfo.commentScore)
.toStringAsFixed(1),
style: TextStyle(
color: rgba(255, 128, 0, 1),
fontSize: 14.rpx,
fontWeight: FontWeight.bold,
)),
),
Spacer(),
Text(
model.getShopInfoEntity().shopInfo.distance,
style: TextStyle(color: rgba(102, 102, 102, 1), fontSize: 11.rpx),
)
],
),
);
}
}
/// 位置信息
class _Location extends StatelessWidget {
final Model model;
_Location(this.model);
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(vertical: 8.rpx, horizontal: 12.rpx),
decoration: BoxDecoration(
image: DecorationImage(
alignment: Alignment.topLeft,
image: AssetImage(
"assets/images/shop/img_map.png",
package: "life_module",
))),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Container(
child: Text(
model.getShopInfoEntity().shopInfo.address,
style: TextStyle(color: rgba(48, 38, 0, 1), fontSize: 12.rpx),
),
),
),
SizedBox(
width: 12.rpx,
),
InkWell(
onTap: () {
ClipboardUtils.copyText(
model.getShopInfoEntity().shopInfo.address);
FlutterNativeToast.showToast('门店地址已复制');
},
child: Container(
child: Column(
children: [
Container(
height: 20.rpx,
width: 20.rpx,
child: LifeImage(
name: "assets/images/shop/ic_copy.png",
width: 20.rpx,
height: 20.rpx,
),
),
Container(
height: 18.rpx,
child: Text(
"复制",
style: TextStyle(
color: rgba(153, 153, 153, 1), fontSize: 11.rpx),
),
)
],
),
),
),
/*SizedBox(
width: 20.rpx,
),
InkWell(
onTap: () async {
String gps = await AmapUtils.getInstance().addressToGps(
model.getShopInfoEntity().shopInfo.address,
model.getCityName());
MapUtils.getInstance().open(gps,model.getShopInfoEntity().shopInfo.address);
},
child: Container(
child: Column(
children: [
Container(
height: 20.rpx,
width: 20.rpx,
child: LifeImage(
name: "assets/images/shop/ic_gps_black.png",
width: 20.rpx,
height: 20.rpx,
),
),
Container(
height: 18.rpx,
child: Text(
"导航",
style: TextStyle(
color: rgba(153, 153, 153, 1), fontSize: 11.rpx),
),
)
],
),
))*/
],
),
);
}
}
import 'package:mvp/mvp.dart';
import 'package:life_repository/life_repository.dart';
class Model extends BaseModel {
final String _cityName;
final String _shopId;
final String _lat;
final String _lng;
final String _itemId;
ShopInfoEntity _shopInfoEntity;
Model(this._shopId, this._lat, this._lng, this._cityName, this._itemId);
Future<void> findShopDetail() async {
var data =
await ShopInfoRepository.get().findShopDetail(_shopId, _lat, _lng);
var list = data.shopCouponItems;
if (_itemId != null && _itemId.isNotEmpty) {
try {
var index = list.indexWhere((item) {
return item.dealId == _itemId;
});
if (index > -1) {
var item = list.removeAt(index);
list.insert(0, item);
}
} catch (e) {}
}
_shopInfoEntity = data;
notifyListeners();
}
ShopInfoEntity getShopInfoEntity() {
return _shopInfoEntity;
}
String getCityName() {
return _cityName;
}
}
import 'package:mvp/mvp.dart';
import 'package:life_repository/life_repository.dart';
class OuterInfoModel extends BaseModel {
final String _shopId;
final String _firstCateId;
ShopOuterInfoEntity _outerInfoEntity;
OuterInfoModel(this._shopId, this._firstCateId);
Future<void> findShopOuterInfo() async {
_outerInfoEntity =
await ShopInfoRepository.get().findShopOuterInfo(_shopId, _firstCateId);
notifyListeners();
}
ShopOuterInfoEntity getShopOuterInfoEntity() {
return _outerInfoEntity;
}
}
import 'package:common_module/utils/audit_mode_utils.dart';
import 'package:flutter/material.dart';
import 'package:common_module/components/i_app_bar.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:life_module/components/life_image.dart';
import './components/shop_info.dart';
import './components/nav_bar.dart';
import './components/item_bar.dart';
import './components/goods_items.dart';
import './components/recommend.dart';
import './components/footer_explain.dart';
import 'package:mvp/providers/provider.dart';
import './actions/action.dart';
import 'package:flutter/cupertino.dart';
import '../../utils/share_dialog_utils.dart';
import 'components/appraise_items.dart';
class LifeShopPage extends StatefulWidget {
final Map<String, dynamic> param;
LifeShopPage({this.param});
@override
State<StatefulWidget> createState() => _LifeShopPageState();
}
class _LifeShopPageState extends State<LifeShopPage>
with TickerProviderStateMixin {
TabController tabController;
IAction iAction;
ScrollController controller = ScrollController();
GlobalKey globalKey1 = GlobalKey();
GlobalKey globalKey2 = GlobalKey();
GlobalKey globalKey3 = GlobalKey();
double offset1;
double offset2;
double offset3;
bool isTap = false;
@override
void initState() {
super.initState();
var param = widget.param;
iAction = IAction(param['shopId'], param['lat'], param['lng'],
param['firstCateId'], param['itemId'], param['cityName']);
iAction.onLoad(() {
Future.delayed(Duration(milliseconds: 500)).then((value) {
RenderBox renderBox1 = globalKey1.currentContext.findRenderObject();
RenderBox renderBox2 = globalKey2.currentContext.findRenderObject();
RenderBox renderBox3 = globalKey3.currentContext.findRenderObject();
offset1 = renderBox1.localToGlobal(Offset(0, 0)).dy;
offset2 = renderBox2.localToGlobal(Offset(0, 0)).dy;
offset3 = renderBox3.localToGlobal(Offset(0, 0)).dy;
});
});
tabController = TabController(length: 3, vsync: this);
controller.addListener(() {
if (isTap) {
return;
}
var size = MediaQuery.of(context).padding;
var offset = controller.offset + 104.rpx + size.top;
if (offset >= offset3) {
tabController.animateTo(2);
} else if (offset >= offset2) {
tabController.animateTo(1);
} else {
tabController.animateTo(0);
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: rgba(245, 245, 245, 1),
appBar: IAppBar(
title: "全部优惠",
color: Colors.white,
actions:AuditModeUtils.getInstance().isAuditMode()?null:[
InkWell(
child: Container(
width: 54.rpx,
padding: EdgeInsets.symmetric(horizontal: 14.rpx),
child: LifeImage(
name: "assets/images/shop/ic_share_black.png",
width: 26.rpx,
height: 26.rpx,
),
),
onTap: () {
if (iAction.getModel().getShopInfoEntity() == null) {
return;
}
ShareDialogUtils.getInstance().show(
context: context,
firstCateId: iAction.getFirstCateId(),
shopInfo: iAction.getModel().getShopInfoEntity().shopInfo);
},
)
],
),
body: ActionProvider(
action: iAction,
builder: (context, child) {
if (iAction.getModel().getShopInfoEntity() == null) {
return Container(
padding: EdgeInsets.only(top: 100.rpx),
alignment: Alignment.topCenter,
child: CupertinoActivityIndicator(
radius: 12,
),
);
}
return Container(
padding: EdgeInsets.symmetric(horizontal: 13.rpx),
child: CustomScrollView(
controller: controller,
slivers: [
ShopInfo(iAction.getModel()),
NavBar(
controller: tabController,
onChange: (index) async {
isTap = true;
if (index == 0) {
await Scrollable.ensureVisible(
globalKey1.currentContext);
} else if (index == 1) {
await Scrollable.ensureVisible(
globalKey2.currentContext);
} else if (index == 2) {
await Scrollable.ensureVisible(
globalKey3.currentContext);
}
isTap = false;
},
),
ItemBar(globalKey1, "优惠"),
GoodsItems(iAction.getModel()),
SliverToBoxAdapter(
child: SizedBox(
height: 8.rpx,
)),
ItemBar(globalKey2, "推荐"),
Recommend(iAction.getOuterInfoModel()),
SliverToBoxAdapter(
child: SizedBox(
height: 8.rpx,
)),
ItemBar(globalKey3, "评价"),
AppraiseItems(iAction.getOuterInfoModel()),
//AllAppraise(),
FooterExplain(),
],
),
);
}));
}
}
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
alarm_calendar:
dependency: transitive
description:
name: alarm_calendar
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.0.5"
amaps_location:
dependency: transitive
description:
path: "."
ref: null-safety
resolved-ref: ef36da83a9a57b4519234cf7f3f037253cf838d3
url: "git@git.xiaomanxiong.com:flutter-plugin/amaps-location.git"
source: git
version: "1.0.0"
android_device:
dependency: transitive
description:
path: "."
ref: null-safety
resolved-ref: "943de0283089fbe7bd0292b8465a8b7c344aaa71"
url: "http://git.xiaomanxiong.com/gitlab/lizengqiang/flutter_android_device.git"
source: git
version: "1.0.0"
async:
dependency: transitive
description:
name: async
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.8.1"
azlistview:
dependency: "direct main"
description:
name: azlistview
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.0-nullsafety.0"
base_repository:
dependency: transitive
description:
path: "."
ref: null-safety
resolved-ref: "5b60d47410e4bb47d0e8f4fc8c6be00e28b2002d"
url: "git@git.xiaomanxiong.com:flutter-plugin/app-base-repository.git"
source: git
version: "1.0.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
cached_network_image:
dependency: transitive
description:
name: cached_network_image
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.0"
cached_network_image_platform_interface:
dependency: transitive
description:
name: cached_network_image_platform_interface
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.0"
cached_network_image_web:
dependency: transitive
description:
name: cached_network_image_web
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.1"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.1"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.0"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.15.0"
common_module:
dependency: "direct main"
description:
path: "."
ref: null-safety
resolved-ref: c3dad0485f56f98c09f69a2c42ad4e66e80a4131
url: "git@git.xiaomanxiong.com:flutter-plugin/common_module.git"
source: git
version: "1.0.0"
config_module:
dependency: transitive
description:
path: "."
ref: null-safety
resolved-ref: "0f3041f75dbf7e71970bf76ff5d557a485710d9f"
url: "git@git.xiaomanxiong.com:flutter-plugin/config_module.git"
source: git
version: "1.0.0"
connectivity:
dependency: transitive
description:
name: connectivity
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.6"
connectivity_for_web:
dependency: transitive
description:
name: connectivity_for_web
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.4.0+1"
connectivity_macos:
dependency: transitive
description:
name: connectivity_macos
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.1+2"
connectivity_platform_interface:
dependency: transitive
description:
name: connectivity_platform_interface
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.1"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.1"
device_info:
dependency: transitive
description:
name: device_info
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.2"
device_info_platform_interface:
dependency: transitive
description:
name: device_info_platform_interface
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.1"
dio:
dependency: transitive
description:
name: dio
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.0.0"
event_bus:
dependency: transitive
description:
name: event_bus
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.0"
extended_list_library:
dependency: transitive
description:
name: extended_list_library
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.0"
extended_nested_scroll_view:
dependency: transitive
description:
name: extended_nested_scroll_view
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.0.0"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.0"
ffi:
dependency: transitive
description:
name: ffi
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.2"
file:
dependency: transitive
description:
name: file
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.1.2"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_album_save:
dependency: transitive
description:
path: "."
ref: null-safety
resolved-ref: fef0dbbb7411eb50d2e0066f71877421484649f5
url: "git@git.xiaomanxiong.com:front/flutter/flutter_album_save.git"
source: git
version: "1.0.0"
flutter_blurhash:
dependency: transitive
description:
name: flutter_blurhash
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.6.0"
flutter_boost:
dependency: transitive
description:
path: "."
ref: "v3.0-null-safety-preview.13"
resolved-ref: "153de2fbaa1f03c6685af74ec68fa8cb292a8887"
url: "https://gitee.com/flutter-plugin/flutter_boost.git"
source: git
version: "3.0.0"
flutter_cache_manager:
dependency: transitive
description:
name: flutter_cache_manager
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.2"
flutter_native_toast:
dependency: transitive
description:
path: "."
ref: null-safety
resolved-ref: "9b4fec6bbe36dcf371d0e79eb5aafec2386a8936"
url: "git@git.xiaomanxiong.com:flutter-plugin/flutter_native_toast.git"
source: git
version: "1.0.0"
flutter_page_indicator:
dependency: transitive
description:
path: "."
ref: HEAD
resolved-ref: "6812decd361038f5ac8afe1de69f5ab12de0e2e5"
url: "git@git.xiaomanxiong.com:flutter-plugin/flutter_page_indicator.git"
source: git
version: "0.0.3"
flutter_secure_storage:
dependency: transitive
description:
name: flutter_secure_storage
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.2.1"
flutter_staggered_grid_view:
dependency: transitive
description:
name: flutter_staggered_grid_view
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.4.1"
flutter_swiper:
dependency: transitive
description:
path: "."
ref: null-safety
resolved-ref: "454016ddb1dd3b37265c6e5d8364860b40cf2653"
url: "git@git.xiaomanxiong.com:flutter-plugin/flutter_swiper.git"
source: git
version: "1.1.6"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
fluttet_clipboard:
dependency: transitive
description:
path: "."
ref: null-safety
resolved-ref: e2134c7dd8747d2f77f71b7f9ce4c82bf2be66e7
url: "http://git.xiaomanxiong.com/gitlab/lizengqiang/fluttet_clipboard.git"
source: git
version: "1.0.6"
fluwx:
dependency: transitive
description:
name: fluwx
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.6.1+3"
http:
dependency: transitive
description:
name: http
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.13.4"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.0.0"
js:
dependency: transitive
description:
name: js
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.6.3"
life_repository:
dependency: "direct main"
description:
path: "."
ref: null-safety
resolved-ref: "97bf3e0f9c3304ffd86579cf28ffda7d554d49c0"
url: "git@git.xiaomanxiong.com:flutter-plugin/app-life-repository.git"
source: git
version: "0.0.1"
localstorage:
dependency: transitive
description:
name: localstorage
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.0.0+1"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.12.10"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.7.0"
modal:
dependency: transitive
description:
path: "."
ref: null-safety
resolved-ref: e4c3dc9abcb73bc78dba38754df6e2fbf3491d2f
url: "git@git.xiaomanxiong.com:front/flutter/flutter_modal.git"
source: git
version: "1.2.6"
mvp:
dependency: transitive
description:
path: "."
ref: null-safety
resolved-ref: a16149d1de577c73af52064862ee7af585a6300b
url: "git@git.xiaomanxiong.com:flutter-plugin/mvp.git"
source: git
version: "0.0.1"
navigation_bar:
dependency: transitive
description:
path: "."
ref: null-safety
resolved-ref: "7e9377e44c84824900f23454c5784f55a31d2a23"
url: "git@git.xiaomanxiong.com:wangll/navigation_bar.git"
source: git
version: "1.0.0"
nested:
dependency: transitive
description:
name: nested
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.0"
octo_image:
dependency: transitive
description:
name: octo_image
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.0+1"
package_info:
dependency: transitive
description:
name: package_info
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.2"
path:
dependency: transitive
description:
name: path
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.8.0"
path_provider:
dependency: transitive
description:
name: path_provider
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.5"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
path_provider_macos:
dependency: transitive
description:
name: path_provider_macos
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.2"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.1"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.3"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.11.1"
permission_handler:
dependency: transitive
description:
name: permission_handler
url: "https://pub.flutter-io.cn"
source: hosted
version: "8.2.5"
permission_handler_platform_interface:
dependency: transitive
description:
name: permission_handler_platform_interface
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.7.0"
photo_view:
dependency: transitive
description:
name: photo_view
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.13.0"
platform:
dependency: transitive
description:
name: platform
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.2"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.2"
process:
dependency: transitive
description:
name: process
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.2.3"
provider:
dependency: transitive
description:
name: provider
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.0.1"
pull_to_refresh:
dependency: transitive
description:
name: pull_to_refresh
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.0"
qr:
dependency: transitive
description:
name: qr
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
qr_flutter:
dependency: transitive
description:
name: qr_flutter
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.0.0"
rxdart:
dependency: transitive
description:
name: rxdart
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.27.2"
scrollable_positioned_list:
dependency: transitive
description:
name: scrollable_positioned_list
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.2"
share_extend:
dependency: transitive
description:
name: share_extend
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.0"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.8.1"
sqflite:
dependency: transitive
description:
name: sqflite
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.0+4"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.1+1"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.10.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.0"
synchronized:
dependency: transitive
description:
name: synchronized
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.0"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.4.2"
transformer_page_view:
dependency: transitive
description:
path: "."
ref: HEAD
resolved-ref: f04b1b9ec098707ab63864aba868fe515decfe3c
url: "git@git.xiaomanxiong.com:flutter-plugin/transformer_page_view.git"
source: git
version: "0.1.6"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.0"
umeng_common_sdk:
dependency: transitive
description:
path: "."
ref: null-safety
resolved-ref: "9ba58020e6e1f98616120a3385015ff180012f85"
url: "git@git.xiaomanxiong.com:flutter-plugin/umeng_common_flutter.git"
source: git
version: "1.2.2"
umeng_crash:
dependency: transitive
description:
path: "."
ref: null-safety
resolved-ref: "15441a7eb61a1320279c6a9a25bc5a8f8eea49c1"
url: "git@git.xiaomanxiong.com:flutter-plugin/umeng-crash.git"
source: git
version: "2.0.1"
url_launcher:
dependency: transitive
description:
name: url_launcher
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.0.12"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.2"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.2"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.4"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.4"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.2"
uuid:
dependency: transitive
description:
name: uuid
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.5"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
waterfall_flow:
dependency: transitive
description:
name: waterfall_flow
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.1"
win32:
dependency: transitive
description:
name: win32
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.9"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.0"
sdks:
dart: ">=2.14.0 <3.0.0"
flutter: ">=2.5.0"
name: life_module
description: A new Flutter package project.
version: 0.0.1
homepage: /
publish_to: none
environment:
sdk: ">=2.7.0 <3.0.0"
flutter: ">=1.17.0"
dependencies:
flutter:
sdk: flutter
azlistview: ^2.0.0-nullsafety.0
common_module:
git:
url: 'git@git.xiaomanxiong.com:flutter-plugin/common_module.git'
ref: 'null-safety'
life_repository:
git:
url: 'git@git.xiaomanxiong.com:flutter-plugin/app-life-repository.git'
ref: 'null-safety'
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
assets:
- assets/images/life/
- assets/images/shop/
- assets/images/sec_kill/
- assets/images/share/
- assets/data/
- assets/images/
# To add assets to your package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/assets-and-images/#from-packages
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# To add custom fonts to your package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/custom-fonts/#from-packages
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