Commit 62c5f3da 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
pubspec.lock
# 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: 84f3d28555368a70270e9ac8390a9441df95e752
channel: stable
project_type: package
{
"dart.flutterSdkPath": "/Users/qiaomeng/flutter_2.5.1"
}
\ No newline at end of file
## [1.0.0] - TODO: 支持Flutter 2.5.1.
* TODO: 支持Flutter当前最新版本(2021-10-16).
\ No newline at end of file
# xiaoxiong_price_module
A new Flutter package project.
## Getting Started
This project is a starting point for a Dart
[package](https://flutter.dev/developing-packages/),
a library module containing code that can be shared easily across
multiple Flutter or Dart projects.
For help getting started with Flutter, view our
[online documentation](https://flutter.dev/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
import 'package:flutter/foundation.dart';
class NotifyEventType {
NotifyEventEnum eventType;
NotifyEventType({@required this.eventType});
}
enum NotifyEventEnum {
PlatformIconEnum,
MeiQiaEnum,
}
\ No newline at end of file
class PlatformIconEvent {
Map<int,dynamic> map;
PlatformIconEvent({this.map = const {}});
}
\ No newline at end of file
import 'package:flutter/material.dart';
abstract class IDSAction<VM> {
VM vm;
void initState();
///
/// 当页面初始化的时候调用
/// context 上下文的对象
///
void initBuilder(BuildContext context) {}
///
/// 组件被销毁时候调用;
/// 通常在该方法中执行一些资源的释放工作,比如监听器的移除,channel的销毁等
///
void dispose() {}
}
import 'package:flutter/cupertino.dart';
import 'package:price_module/framework/model/ds_model.dart';
import 'package:provider/provider.dart';
class DSProvider<VO extends IDSModel> extends ChangeNotifierProvider<VO> {
final VO vo;
final Widget Function(
BuildContext context,
VO value,
Widget child,
) builderWidget;
DSProvider.value({
@required this.vo,
@required this.builderWidget,
}) : super.value(
value: vo,
child: Consumer<VO>(
builder: builderWidget,
),
);
}
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:price_module/framework/action/ds_action.dart';
import 'package:price_module/framework/model/ds_model.dart';
import 'package:provider/provider.dart';
class DSProviderWidget<VM extends IDSModel, T extends IDSAction<VM>> extends StatefulWidget {
final T dsAction;
final Widget child;
final Widget Function(BuildContext context,Widget child) builder;
const DSProviderWidget({Key key,@required this.dsAction,@required this.builder,this.child}):super(key:key);
@override
_DSProviderWidgetState<VM,T> createState() => _DSProviderWidgetState<VM,T>();
}
class _DSProviderWidgetState<VM extends IDSModel,T extends IDSAction<VM>> extends State<DSProviderWidget<VM,T>> {
T action;
Widget get _child => widget.child;
@override
void initState() {
action = widget.dsAction;
action?.initState();
super.initState();
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<VM>.value(
value: action.vm,
child: Consumer<VM>(
builder:(context,vm,child){
action?.initBuilder(context);
return widget.builder(context,child);
},
child: _child,
),
);
}
@override
void dispose() {
action?.dispose();
super.dispose();
}
}
\ No newline at end of file
class DSNetException implements Exception {
final String message;
final int code;
@pragma("vm:entry-point")
const DSNetException({this.message = "", this.code});
String toString() {
Map<String,dynamic> data = {'message':this.message,'code':this.code};
return "$data";
}
}
\ No newline at end of file
import 'package:flutter/foundation.dart';
abstract class IDSModel extends ChangeNotifier {
///
/// 当前页面的状态[viewState]
///
DSViewState viewState = DSViewState.idle;
void notifyListener([bool notify = true]) {
if (notify) {
super.notifyListeners();
}
}
}
/// 页面状态类型
enum DSViewState {
idle, //加载完成
busy, //加载中
empty, //无数据
error, //加载失败
unAuthorized, //未登录
}
import 'package:flutter/material.dart';
import 'package:price_module/eventbus/notify_event_type.dart';
import 'package:price_module/eventbus/platform_icon_event.dart';
import 'package:price_module/framework/action/ds_action.dart';
import 'package:price_module/framework/model/ds_model.dart';
import 'package:price_module/page/model/details/index.dart';
import 'package:price_module/page/service/details/index.dart';
import 'package:price_module/utils/event_bus_utils.dart';
import 'package:price_module/utils/time_utils.dart';
import 'package:xiaoxiong_repository/app_xiaoxiong_repository.dart';
import 'package:xiaoxiong_repository/entity/turn_chain_entity.dart';
import 'package:price_module/framework/exception/ds_net_exception.dart';
abstract class IPriceDetailsAction<PriceDetailsVo>
extends IDSAction<PriceDetailsVo> {
///
/// 商品举报
/// [reportType] 举报类型
///
void report({String reportType});
///
/// 用户点赞
/// [status] 点赞的状态
///
void userLike({bool status = true});
///
/// 用户收藏
/// [status] 收藏的状态
///
void userCollect({bool status = true});
///
/// 用户点击购买
///
Future<TurnChainEntity> goToBuyEvent();
///
/// 凑单购买
/// [itemId] 商品Id
/// [platform] 平台
///
///
Future<TurnChainEntity> goToBuyItemId({String itemId, String platform});
///
/// 获取举报配置信息
///
List<dynamic> get sellReportList;
}
class PriceDetailsAction extends IPriceDetailsAction<PriceDetailsVo> {
PriceDetailsService _detailsService = PriceDetailsService();
IPriceDetailsActionDelegate _actionDelegate;
Map<int, dynamic> _platformIconMap;
List<dynamic> _sellReportList = [];
@override
List<dynamic> get sellReportList => _sellReportList;
PriceDetailsAction(
{Map<String, dynamic> param, IPriceDetailsActionDelegate delegate}) {
eventBus.on<PlatformIconEvent>().listen((event) {
_platformIconMap = event.map;
});
eventBus.fire(NotifyEventType(eventType: NotifyEventEnum.PlatformIconEnum));
this._actionDelegate = delegate;
this.vm = PriceDetailsVo(productId: param['productId']);
if (param['src'] != null && param['src'] is String) {
this.vm.itemPic = [param['src']];
}
if (param['title'] != null && param['title'] is String) {
this.vm.itemTitle = param['title'];
}
if (param['sellReportList'] is List) {
_sellReportList = List<dynamic>.from(param['sellReportList']);
try {
this.vm.reports =
List<ReportModel>.generate(sellReportList.length, (index) {
Map<String, dynamic> sellReport =
Map<String, dynamic>.from(sellReportList[index]);
List<dynamic> subList = List<dynamic>.from(sellReport['sub']);
return ReportModel(
text: sellReport['text'],
value: sellReport['value'],
sub: List<ReportModel>.generate(subList.length, (index) {
Map<String, dynamic> sellSUb =
Map<String, dynamic>.from(subList[index]);
return ReportModel(
sub: [], text: sellSUb['text'], value: sellSUb['value']);
}));
});
} catch (e) {
print(e);
}
}
}
@override
void report({String reportType}) async {
_detailsService
.report(id: this.vm.productId, reportType: reportType)
.asStream()
.first
.then((value) {
if (value['code'] == 2000) {
_actionDelegate?.showTipsMsg(msg: '举报成功,感谢您的反馈!');
}
});
}
bool disableLike = false, disableCollect = false;
@override
void userLike({bool status = true}) {
if (disableLike) {
return;
}
disableLike = true;
_detailsService
.userLike(
markId: this.vm.productId,
platform: this.vm.platform,
status: status ? '2' : '1')
.asStream()
.first
.then((value) {
if (value['code'] == 2000) {
if (status) {
try {
this.vm.likeTimes = '${int.parse(this.vm.likeTimes) - 1}';
} catch (e) {}
} else {
try {
this.vm.likeTimes = '${int.parse(this.vm.likeTimes) + 1}';
} catch (e) {}
}
this.vm.isLike = !status;
this.vm.notifyListener(true);
}
})
.catchError((error) {})
.whenComplete(() {
disableLike = false;
});
}
@override
void userCollect({bool status = true}) {
if (disableCollect) {
return;
}
disableCollect = true;
_detailsService
.userCollect(
itemId: this.vm.productId,
platform: this.vm.platform,
status: status ? '2' : '1')
.asStream()
.first
.then((value) {
if (value['code'] == 2000) {
if (!status) {
_actionDelegate?.showTipsMsg(msg: '收藏成功');
}
if (status) {
try {
this.vm.collegeTimes = '${int.parse(this.vm.collegeTimes) - 1}';
} catch (e) {}
} else {
try {
this.vm.collegeTimes = '${int.parse(this.vm.collegeTimes) + 1}';
} catch (e) {}
}
this.vm.isFavorites = !status;
this.vm.notifyListener(true);
}
})
.catchError((error) {})
.whenComplete(() {
disableCollect = false;
});
}
@override
void initState() async {
this.vm.viewState = DSViewState.busy;
_detailsService
.qryProductDetails(productId: this.vm.productId)
.asStream()
.first
.then((value) {
this.vm.viewState = DSViewState.idle;
this.vm.itemPic = value.itemPic;
this.vm.itemId = value.itemId;
this.vm.platform = value.platform;
this.vm.platformText = _getPlatformName(int.parse(value.platform));
this.vm.itemTitle = value.itemTitle;
this.vm.subTitle = value.subTitle;
this.vm.status = value.status;
this.vm.likeTimes = value.likeTimes;
this.vm.collegeTimes = value.collegeTimes;
this.vm.buttonText = value.buttonText;
this.vm.description = value.description;
this.vm.releaseTime = value.releaseTime;
this.vm.couponText = value.couponText;
this.vm.checkText = value.checkText;
this.vm.coupons = value.coupons;
this.vm.shareUrl = value.shareUrl;
this.vm.price = value.price;
this.vm.endPrice = value.endPrice;
this.vm.shareMessage = value.shareMessage;
this.vm.isLike = value.isLike;
this.vm.isFavorites = value.isFavorites;
this.vm.isOverdue = value.isOverdue;
this.vm.shopTitle = value.shopTitle;
this.vm.rebate = value.rebate;
this.vm.collectOrders = value.collectOrders;
this.vm.newItemInfo = value.newItemInfo;
}).catchError((error) {
this.vm.viewState = DSViewState.idle;
}).catchError((error) {
this.vm.viewState = DSViewState.error;
}).whenComplete(() {
_detailsService
.getSellingRecommends(
excludeId: this.vm.productId,
keywords: this.vm.itemTitle,
page: 1,
pagesize: 7,
)
.then((value) {
if (value.isSuccess) {
this.vm.productEntitys = value.data;
this.vm.productEntitys.forEach((ele) {
ele.releaseTime = TimeUtils.formatTime(timestamp: ele.releaseTime);
ele.platform = _getPlatformName(int.parse(ele.platform));
});
}
}).catchError((error) {
print(error);
}).whenComplete(() {
this.vm.notifyListener(true);
});
});
}
///
/// 将平台的id,转换为平台的名称
/// [platform] 平台id
///
String _getPlatformName(int platform) {
if (_platformIconMap != null && _platformIconMap[platform] is Map) {
Map<String, dynamic> platformMap =
Map<String, dynamic>.from(_platformIconMap[platform]);
return platformMap['name'] ?? '';
}
return '';
}
@override
Future<TurnChainEntity> goToBuyEvent() async {
DSResponseObject<TurnChainEntity> resObject = await _detailsService
.turnChainForItemId(
itemId: this.vm.itemId, platform: this.vm.platform, type: '6')
.asStream()
.first;
if (resObject.isSuccess) {
return resObject.data;
}
throw DSNetException(code: resObject.code, message: resObject.msg);
}
@override
Future<TurnChainEntity> goToBuyItemId({
String itemId,
String platform,
}) async {
if (platform == null) {
platform = this.vm.platform;
}
DSResponseObject<TurnChainEntity> resObject = await _detailsService
.turnChainForItemId(itemId: itemId, platform: platform, type: '6')
.asStream()
.first;
if (resObject.isSuccess) {
return resObject.data;
}
throw DSNetException(code: resObject.code, message: resObject.msg);
}
}
///
/// 代理类,类似接口
///
abstract class IPriceDetailsActionDelegate {
void showTipsMsg({@required String msg});
}
import 'package:price_module/eventbus/notify_event_type.dart';
import 'package:price_module/eventbus/platform_icon_event.dart';
import 'package:price_module/framework/action/ds_action.dart';
import 'package:price_module/framework/model/ds_model.dart';
import 'package:price_module/page/model/discount/index.dart';
import 'package:price_module/utils/event_bus_utils.dart';
import 'package:price_module/utils/time_utils.dart';
import 'package:xiaoxiong_repository/entity/product_item_entity.dart';
import 'package:xiaoxiong_repository/repository/price_repository.dart';
abstract class IPriceDiscountAction<PriceDiscountVo>
extends IDSAction<PriceDiscountVo> {
IPriceDiscountAction();
///
/// 加载数据
///
void reloadData();
///
/// 加载更多
///
void onLoad();
///
/// 获取当前数据
///
PriceDiscountVo get getVo;
}
class PriceDiscountAction extends IPriceDiscountAction<PriceDiscountVo> {
PriceRepository _priceRepository = PriceRepository.get();
Map<int, dynamic> _platformIconMap;
IPriceDiscountDelegate _discountDelegate;
Map<String, dynamic> _pageParam;
PriceDiscountAction({
IPriceDiscountDelegate delegate,
Map<String, dynamic> pageParam,
}) {
this._pageParam = pageParam;
_discountDelegate = delegate;
this.vm = PriceDiscountVo();
eventBus.on<PlatformIconEvent>().listen((event) {
_platformIconMap = event.map;
});
eventBus.fire(NotifyEventType(eventType: NotifyEventEnum.PlatformIconEnum));
}
@override
void initState() {
this._getSellingRecommends(pagesize: 10, page: 1, isFirst: true);
}
int currentPage = 1;
@override
void reloadData() {
this._getSellingRecommends(
page: 1,
pagesize: 10,
isRefresh: true,
);
this.vm.notifyListener();
}
@override
void onLoad() {
this._getSellingRecommends(
page: currentPage + 1,
pagesize: 10,
isLoad: true,
);
}
///
/// 查询数据
///
void _getSellingRecommends({
int page = 1,
int pagesize = 10,
bool isFirst = false,
bool isLoad = false,
bool isRefresh = false,
}) {
currentPage = page;
if (isFirst) {
this.vm.viewState = DSViewState.busy;
}
_priceRepository
.getSellingRecommends(
keywords: _pageParam['keywords'],
excludeId: _pageParam['excludeId'],
page: page,
pagesize: pagesize,
)
.then((value) {
if (value.isSuccess) {
List<ProductItemEntity> productEntitys = value.data;
productEntitys.forEach((ele) {
ele.releaseTime = TimeUtils.formatTime(timestamp: ele.releaseTime);
ele.platform = _getPlatformName(int.parse(ele.platform));
});
if (isLoad) {
if (this.vm.productEntitys == null) {
this.vm.productEntitys = List<ProductItemEntity>.empty(growable: true);
}
this.vm.productEntitys.addAll(productEntitys);
} else {
this.vm.productEntitys = productEntitys;
if (this.vm.productEntitys.isEmpty) {
this.vm.viewState = DSViewState.empty;
} else {
this.vm.viewState = DSViewState.idle;
}
}
}
if (isLoad) {
_discountDelegate.onLoadComplete(isLoading: true);
} else if (isRefresh) {
_discountDelegate.onLoadComplete(isLoading: false);
}
}).catchError((error) {
this.vm.viewState = DSViewState.error;
if (isLoad) {
_discountDelegate.onloadFailed(isLoading: true);
} else if (isRefresh) {
_discountDelegate.onloadFailed(isLoading: false);
}
}).whenComplete(() {
this.vm.notifyListener();
});
}
///
/// 将平台的id,转换为平台的名称
/// [platform] 平台id
///
String _getPlatformName(int platform) {
if (_platformIconMap != null && _platformIconMap[platform] is Map) {
Map<String, dynamic> platformMap =
Map<String, dynamic>.from(_platformIconMap[platform]);
return platformMap['name'] ?? '';
}
return '';
}
@override
PriceDiscountVo get getVo => this.vm;
}
///
/// 代理类,类似接口
///
abstract class IPriceDiscountDelegate {
///
/// 数据加载完成
///
void onLoadComplete({bool isLoading = true});
///
/// 数据加载失败
///
void onloadFailed({bool isLoading = true, String msg});
}
import 'package:price_module/framework/action/ds_action.dart';
import 'package:price_module/framework/model/ds_model.dart';
import 'package:price_module/page/model/index.dart';
import 'package:price_module/utils/time_utils.dart';
import 'package:xiaoxiong_repository/entity/product_item_entity.dart';
import 'package:xiaoxiong_repository/repository/price_repository.dart';
abstract class IPriceAction<PriceVo> extends IDSAction<PriceVo> {
///
/// 代理设置
///
IPriceAction({IPriceActionDelegate delegate, bool isGrid});
///
/// 设置分类参数
///
void setPriceCategory(
{List<dynamic> sellingTabList,
List<dynamic> priceCategory,
Map<int, dynamic> platformIconMap,
String keywords});
///
/// 获取分类Tab
///
TabListVo getTabListVo();
///
/// 获取快捷分类列表
///
List<dynamic> get sellingTabs;
///
/// 获取产品列表
///
ProductListVo getProductListVo();
///
/// 关闭Tab
///
void closeTab();
///
/// 重置筛选条件
///
void resetProductList();
///
/// 查询筛选条件
///
void qryProductListEvent({String keywords, int page});
///
/// 刷新
///
void onRefresh();
///
/// 加载更多
///
void loadMore();
}
class PriceAction extends IPriceAction<PriceVo> {
PriceRepository _repository = PriceRepository.get();
Map<int, dynamic> platformIconMap;
String keywords;
bool isGrid = false;
IPriceActionDelegate delegate;
PriceAction({this.delegate, this.isGrid}) : super(delegate: delegate);
@override
void initState() async {
if (this.vm == null) {
this.vm = PriceVo();
}
if (this.vm?.productListVo == null) {
this.vm?.productListVo = ProductListVo();
}
this.vm?.productListVo?.isGrid = isGrid;
this.vm?.productListVo?.viewState = DSViewState.busy;
}
@override
List<dynamic> get sellingTabs => this.vm.sellingTabs ?? [];
@override
void setPriceCategory(
{List<dynamic> sellingTabList,
List<dynamic> priceCategory,
Map<int, dynamic> platformIconMap,
String keywords}) {
this.platformIconMap = platformIconMap;
this.page = 1;
if (keywords != null && keywords.isNotEmpty) {
isGrid = true;
}
this.keywords = keywords;
if (this.vm == null) {
this.vm = PriceVo();
}
if (this.vm.tabListVo == null) {
this.vm.tabListVo = TabListVo();
}
this.vm.sellingTabs = sellingTabList;
dynamic data = this
.vm
.sellingTabs
?.singleWhere((ele) => ele['is_default'] == '1', orElse: () => null);
if (data != null && data is Map<String, dynamic>) {
this.vm.tabListVo.sellTab = Map<String, dynamic>.from(data);
}
if (priceCategory != null) {
List<TabVo> tabList = List<TabVo>.empty(growable: true);
priceCategory.forEach((ele) {
Map<String, dynamic> categoryMap = Map<String, dynamic>.from(ele);
tabList.add(TabVo.fromJson(
'${categoryMap['type']}', Map<String, dynamic>.from(ele)));
});
TabListVo tabListVo = this.vm.tabListVo;
tabListVo.tabList = tabList;
this.vm.tabListVo = tabListVo;
}
TabListVo tabListVo = this.vm.tabListVo;
String categoryId;
if (tabListVo.sellTab != null && tabListVo.sellTab['id'] != null) {
categoryId = "${tabListVo.sellTab['id']}";
}
_qryProductList(keywords: keywords, categoryId: categoryId);
}
@override
TabListVo getTabListVo() {
return this.vm.tabListVo ?? TabListVo();
}
@override
ProductListVo getProductListVo() {
return this.vm?.productListVo ?? ProductListVo(productList: []);
}
///
/// 重置筛选条件
///
@override
void resetProductList() async {
for (TabVo item in this.vm.tabListVo?.tabList ?? []) {
item.selected = null;
item.open = false;
}
this.vm.tabListVo?.sellTab = null;
this.vm.tabListVo?.notifyListener(true);
_qryProductList(keywords: this.keywords);
}
///
/// 过滤筛选条件
///
@override
void qryProductListEvent({String keywords, int page = 1}) async {
if (keywords != null) {
this.keywords = keywords;
}
List<dynamic> filters;
this.vm.tabListVo?.tabList?.forEach((ele) {
if (filters == null) {
filters = List<dynamic>.empty(growable: true);
}
if (ele.selected != null) {
Map<String, dynamic> data;
if (ele.keyName == 'category') {
data = {
'key_name': ele.keyName,
'key_value': '${ele.selected['id']}'
};
} else if (ele.keyName == 'price_section') {
data = {
'key_name': ele.keyName,
'key_value': ele.selected['price_section']
};
} else {
data = {
'key_name': ele.keyName,
'key_value': '${ele.selected['type']}'
};
}
if (data != null && data.isNotEmpty) {
filters.add(data);
}
}
});
TabListVo tabListVo = this.vm.tabListVo;
String categoryId;
if (tabListVo.sellTab != null && tabListVo.sellTab['id'] != null) {
categoryId = "${tabListVo.sellTab['id']}";
}
_qryProductList(
keywords: this.keywords,
filters: filters,
page: page,
categoryId: categoryId);
}
@override
void closeTab() {
TabListVo tabListVo = this.vm.tabListVo;
tabListVo.tabList.forEach((ele) {
ele.open = false;
});
tabListVo.notifyListener(true);
}
String getPlatformName(int platform) {
if (platformIconMap != null && platformIconMap[platform] is Map) {
Map<String, dynamic> platformMap =
Map<String, dynamic>.from(platformIconMap[platform]);
return platformMap['name'] ?? '';
}
return '';
}
void _qryProductList(
{int page = 1,
int pagesize = 10,
String keywords,
String categoryId,
List<dynamic> filters}) async {
this.page = page ?? 1;
await _repository
.qryProductList(
page: page,
pagesize: pagesize,
keywords: keywords,
categoryId: categoryId,
filters: filters,
)
.then((value) {
this.vm?.productListVo?.viewState = DSViewState.idle;
if (page == 1) {
List<ProductItemEntity> productList = [];
if (value.isSuccess) {
productList = value.data;
productList.forEach((ele) {
ele.releaseTime = TimeUtils.formatTime(timestamp: ele.releaseTime);
ele.platform = getPlatformName(int.parse(ele.platform));
});
}
this.vm?.productListVo?.productList = productList;
} else {
List<ProductItemEntity> productList = [];
if (value.isSuccess) {
productList = value.data;
productList.forEach((ele) {
ele.releaseTime = TimeUtils.formatTime(timestamp: ele.releaseTime);
ele.platform = getPlatformName(int.parse(ele.platform));
});
this.vm?.productListVo?.productList?.addAll(productList);
}
}
delegate?.onLoadComplete();
}).catchError((error) {
this.vm?.productListVo?.viewState = DSViewState.error;
delegate?.onloadFailed();
}).whenComplete(() {
this.vm?.productListVo?.notifyListener(true);
});
}
int page = 1;
@override
void loadMore() {
qryProductListEvent(page: page + 1);
}
@override
void onRefresh() {
qryProductListEvent(page: 1);
}
}
///
/// 代理类,类似接口
///
abstract class IPriceActionDelegate {
///
/// 数据加载完成
///
void onLoadComplete();
///
/// 数据加载失败
///
void onloadFailed({String msg});
}
import 'package:price_module/framework/model/ds_model.dart';
import 'package:xiaoxiong_repository/entity/product_item_entity.dart';
class PriceDetailsVo extends IDSModel {
String productId;
List<String> itemPic;
String itemTitle;
String subTitle;
String description;
String status;
//商品的ItemId
String itemId;
//平台Id
String platform;
//平台名称
String platformText;
//喜欢次数
String likeTimes;
//收藏次数
String collegeTimes;
//是否喜欢和收藏
bool isLike = false;
bool isFavorites = false;
//Button按钮的文字
String buttonText;
String price;
String endPrice;
//发布时间
String releaseTime;
String checkText;
//分享文字和分享URL
String shareUrl;
String shareMessage;
//优惠卷
List<Coupon> coupons = [];
//举报
List<ReportModel> reports = [];
//优惠卷过期描述
String couponText;
String isOverdue;
String shopTitle;
String rebate;
List<dynamic> collectOrders;
Map<String, dynamic> newItemInfo;
//商品推荐相关信息
List<ProductItemEntity> productEntitys;
PriceDetailsVo({
this.productId,
this.itemPic,
this.itemTitle,
this.subTitle,
this.description,
this.itemId,
this.platform,
this.platformText,
this.likeTimes,
this.collegeTimes,
this.isLike = false,
this.isFavorites = false,
this.buttonText,
this.releaseTime,
this.checkText,
this.reports,
this.shareUrl,
this.shareMessage,
this.price,
this.endPrice,
this.status,
this.couponText,
this.isOverdue,
this.shopTitle,
this.collectOrders,
this.newItemInfo,
this.productEntitys,
});
}
class Coupon {
String url;
String name;
String status;
Coupon({this.url, this.name, this.status});
}
class ReportModel {
String value;
String text;
List<ReportModel> sub = [];
ReportModel({this.value, this.text, this.sub});
}
import 'package:price_module/framework/model/ds_model.dart';
import 'package:xiaoxiong_repository/entity/product_item_entity.dart';
class PriceDiscountVo extends IDSModel{
///
/// 好价商品列表
///
List<ProductItemEntity> productEntitys;
}
\ No newline at end of file
import 'package:price_module/framework/model/ds_model.dart';
import 'package:xiaoxiong_repository/entity/product_item_entity.dart';
class PriceVo extends IDSModel {
// 商品列表
ProductListVo productListVo;
//筛选Tab
TabListVo tabListVo;
// 快捷分类列表
List<dynamic> sellingTabs;
PriceVo({this.productListVo, this.tabListVo, this.sellingTabs});
}
class TabListVo extends IDSModel {
//分类
List<TabVo> tabList = [];
//快捷分类
Map<String, dynamic> sellTab;
TabListVo({this.tabList = const [], this.sellTab});
}
class TabVo extends IDSModel {
String type;
String text;
String keyName;
bool open = false;
Map<String, dynamic> selected;
List<dynamic> structure;
TabVo({this.type, this.text, this.keyName, this.structure, this.selected});
TabVo.fromJson(String type, Map<String, dynamic> json) {
this.type = type;
this.text = '${json['text']}';
this.keyName = '${json['key_name']}';
this.structure = [];
if (json['structure'] != null && json['structure'] is List) {
this.structure = List.from(json['structure']);
}
}
}
class ProductListVo extends IDSModel {
bool isGrid = false;
List<ProductItemEntity> productList = [];
ProductListVo({this.productList = const [], this.isGrid = false});
}
import 'package:flutter/material.dart';
import 'package:price_module/framework/model/ds_model.dart';
import 'package:price_module/page/model/details/index.dart';
import 'package:price_module/utils/time_utils.dart';
import 'package:xiaoxiong_repository/entity/product_details_entity.dart';
import 'package:xiaoxiong_repository/entity/product_item_entity.dart';
import 'package:xiaoxiong_repository/repository/price_repository.dart';
import 'package:xiaoxiong_repository/entity/turn_chain_entity.dart';
import 'package:xiaoxiong_repository/app_xiaoxiong_repository.dart';
class PriceDetailsService {
PriceRepository _priceRepository = PriceRepository.get();
///
/// 查询商品详情
/// [productId] 查询的商品Id
///
Future<PriceDetailsVo> qryProductDetails({@required String productId}) async {
return await _priceRepository
.qryProductDetails(id: productId)
.asStream()
.map((event) {
PriceDetailsVo detailsVo;
if (event.isSuccess) {
detailsVo = PriceDetailsVo();
ProductDetailsEntity entity = event.data;
detailsVo.viewState = DSViewState.idle;
detailsVo.likeTimes = entity.likeTimes;
detailsVo.collegeTimes = entity.collegeTimes;
detailsVo.itemTitle = entity.itemTitle;
detailsVo.subTitle = entity.special;
detailsVo.itemPic = entity.itemPics;
detailsVo.description = entity.description;
detailsVo.status = entity.itemStatus;
detailsVo.buttonText = entity.buttonText;
detailsVo.itemId = entity.itemId;
detailsVo.platform = entity.platform;
detailsVo.couponText = entity.couponText;
detailsVo.isLike = entity.isLike == '1';
detailsVo.isFavorites = entity.isCollect == '1';
detailsVo.shareMessage = entity.shareMessage ?? '';
detailsVo.shareUrl = entity.shareUrl ?? '';
if (entity.releaseTime != null && entity.releaseTime.length > 0) {
detailsVo.releaseTime =
TimeUtils.formatTime(timestamp: entity.releaseTime);
}
detailsVo.checkText = entity.checkText ?? '';
detailsVo.endPrice = entity.endPrice;
detailsVo.price = entity.price;
detailsVo.coupons =
List.generate(entity.couponInfos?.length, (index) {
CouponInfo info = entity.couponInfos[index];
return Coupon(
name: info.couponName, url: info.url, status: info.status);
});
detailsVo.isOverdue = entity.isOverdue;
detailsVo.shopTitle = entity.shopTitle;
detailsVo.rebate = entity.rebate;
detailsVo.collectOrders = entity.collectOrders;
detailsVo.newItemInfo = entity.newItemInfo;
}
return detailsVo ?? PriceDetailsVo();
})
.first
.catchError((error) {
print("==================> error:$error");
});
}
///
/// 好价推荐列表
/// [keywords] 搜索关键词
/// [excludeId] 排除id
/// [page] 当前页码
/// [pagesize] 当前页显示的大小
///
Future<DSResponseList<ProductItemEntity>> getSellingRecommends({
String keywords,
String excludeId,
int page = 1,
int pagesize = 10,
}) async {
return _priceRepository.getSellingRecommends(
keywords: keywords,
excludeId: excludeId,
page: page,
pagesize: pagesize,
);
}
///
/// 举报
/// [id] 举报的商品Id
/// [report_type] 举报的商品类型
///
Future<dynamic> report(
{@required String id, @required String reportType}) async {
return await _priceRepository
.report(id: id, reportType: reportType)
.asStream()
.map((event) {
return {'code': event.code, 'msg': event.msg};
}).first;
}
///
/// 用户喜欢
/// [markId] 产品Id
/// [type] 类型
/// [status] 状态
/// [platform] 平台
///
Future<dynamic> userLike(
{@required String markId,
@required String platform,
String type = '2',
String status = '1'}) async {
return await _priceRepository
.userLike(
markId: markId, type: type, status: status, platform: platform)
.asStream()
.map((event) {
return {'code': event.code, 'msg': event.msg};
}).first;
}
///
/// 用户收藏
///
Future<dynamic> userCollect(
{@required String itemId,
@required String platform,
String type = '2',
String status = '1'}) async {
return await _priceRepository
.userCollect(
itemId: itemId, type: type, status: status, platform: platform)
.asStream()
.map((event) {
return {'code': event.code, 'msg': event.msg};
}).first;
}
///
/// 通过商品Id进行转链
/// [itemId] 商品id
/// [platform] 平台,1淘宝 2京东 3拼多多 4唯品会
/// [type] 转链类型 好价传6 默认为5
///
Future<DSResponseObject<TurnChainEntity>> turnChainForItemId(
{@required String itemId,
@required String platform,
String type = '6'}) async {
return await _priceRepository
.turnChainForItemId(itemId: itemId, platform: platform, type: type)
.asStream()
.first;
}
}
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_native_toast/flutter_native_toast.dart';
import 'package:fluwx/fluwx.dart';
import 'package:price_module/framework/base/ds_provider_widget.dart';
import 'package:price_module/framework/exception/ds_net_exception.dart';
import 'package:price_module/framework/model/ds_model.dart';
import 'package:price_module/page/action/details/index.dart';
import 'package:price_module/page/model/details/index.dart';
import 'package:price_module/page/widget/details/index.dart';
import 'package:price_module/route/index.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:xiaoxiong_repository/entity/product_item_entity.dart';
class PriceDetailsPage extends StatefulWidget {
final Map<String, dynamic> pageParam;
final Function(Map<String, dynamic>) onCallbackParams;
final Function(String, String) onH5Callback;
final Function(String) onCopyLink;
final Widget Function(
BuildContext context,
PriceDetailsVo detailsVo,
GlobalKey convertKey,
) onShareWidget;
final Function(BuildContext context,
{String path, String type, @required GlobalKey convertKey}) onShare;
const PriceDetailsPage(
{Key key,
@required this.pageParam,
this.onShareWidget,
this.onCallbackParams,
this.onH5Callback,
@required this.onCopyLink,
@required this.onShare})
: super(key: key);
@override
_PriceDetailsPageState createState() => _PriceDetailsPageState();
}
class _PriceDetailsPageState extends State<PriceDetailsPage>
with TickerProviderStateMixin, IPriceDetailsActionDelegate {
//分享图片key
GlobalKey _convertKey = GlobalKey();
IPriceDetailsAction<PriceDetailsVo> _detailsAction;
ScrollController scrollController = ScrollController();
bool silverCollapsed = false;
ValueNotifier<int> _appBarListenable = ValueNotifier<int>(0);
AnimationController likeAnimationController;
AnimationController favoritesAnimationController;
bool isAudituser;
@override
void initState() {
likeImages.add('assets/images/like/ic_like_unselected.png');
for (var i = 0; i < 18; i++) {
likeImages.add('assets/images/like/$i.png');
}
favoritesImages.add('assets/images/collect/ic_favorites_unselected.png');
for (var i = 0; i < 18; i++) {
favoritesImages.add('assets/images/collect/$i.png');
}
likeAnimationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 400));
Animation<double> likeAnimation =
Tween<double>(begin: 1, end: likeImages.length.toDouble() - 1)
.animate(likeAnimationController);
likeAnimationController.addListener(() {
likeValueNotifier.value = likeAnimation.value.toInt();
});
favoritesAnimationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 400));
Animation<double> favoritesAnimation =
Tween<double>(begin: 1, end: favoritesImages.length.toDouble() - 1)
.animate(favoritesAnimationController);
favoritesAnimationController.addListener(() {
favoritesValueNotifier.value = favoritesAnimation.value.toInt();
});
super.initState();
try {
if (widget.pageParam["isAudituser"] != null) {
isAudituser = widget.pageParam["isAudituser"] as bool;
}
} catch (e) {}
_detailsAction =
PriceDetailsAction(param: widget.pageParam, delegate: this);
scrollController.addListener(() {
if (scrollController.offset >= 300) {
if (silverCollapsed == false) {
silverCollapsed = true;
setState(() {});
}
} else {
if (silverCollapsed == true) {
silverCollapsed = false;
setState(() {});
}
}
});
}
///
/// 好价详情
///
Widget _buildBody(BuildContext context,
{ScrollController controller, @required PriceDetailsVo detailsVo}) {
return NotificationListener(
child: Stack(children: [
detailsVo.viewState == DSViewState.busy
? Container()
: widget.onShareWidget(context, detailsVo, _convertKey),
Container(
color: Colors.white,
child: ListView(
children: [
Container(
padding: EdgeInsets.only(left: 13.w, right: 13.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//顶部的滚动栏
buildSwiperWidget(images: detailsVo?.itemPic ?? []),
SizedBox(
height: 16,
),
Container(
child: LayoutBuilder(builder:
(BuildContext context, BoxConstraints constraints) {
String value = detailsVo.itemTitle;
return Text(
value,
style: TextStyle(
fontSize: 18.w,
fontWeight: FontWeight.w600,
color: Color(0xFF333333),
),
overflow: TextOverflow.ellipsis,
maxLines: 2,
);
}),
),
SizedBox(
height: 12,
),
detailsVo.isOverdue == '1'?Text('过期 | ${detailsVo.subTitle ?? ''}',textAlign: TextAlign.center,style:TextStyle(color:Color(0xFF999999),fontWeight: FontWeight.w600,fontSize: 18.w,)):Text('${detailsVo.subTitle ?? ''}',style:TextStyle(
color: Color(0xFFFF0400),
fontWeight: FontWeight.w600,fontSize: 18.w,
)),
/// 商品最新信息
buildProductNewlastWidget(
newInfo: detailsVo.newItemInfo,
goBuy: () {
_detailsAction.goToBuyEvent().then((value) {
if (value != null &&
widget.onH5Callback != null) {
widget.onH5Callback(
value.appLink, value.platform);
}
}).catchError((error) {
if (error is DSNetException) {
DSNetException exception = error;
FlutterNativeToast.showToast(
'${exception.message}');
}
});
},
onCouponTap: (Map<String, dynamic> coupon) {
if (coupon['status'] == '1' &&
coupon['url'] != null) {
if (widget.onH5Callback != null) {
widget.onH5Callback(
coupon['url'], detailsVo.platform);
}
}
}),
/// 店铺信息
buildStoreInfoWidget(
vo: detailsVo,
onCouponTap: (Coupon coupon) {
if (coupon.status == '1' && coupon.url != null) {
if (widget.onH5Callback != null) {
widget.onH5Callback(
coupon.url, detailsVo.platform);
}
}
},
onPurchase: (data) {
if (data == null || data['item_id'] == null) {
return;
}
String itemId = data['item_id'];
String platform = data['platform'];
_detailsAction
.goToBuyItemId(itemId: itemId, platform: platform)
.then(
(value) {
if (value != null &&
widget.onH5Callback != null) {
widget.onH5Callback(
value.appLink, value.platform);
}
},
).catchError((error) {
if (error is DSNetException) {
DSNetException exception = error;
FlutterNativeToast.showToast(
'${exception.message}');
}
});
},
),
//商品详情
Container(
margin: EdgeInsets.only(top: 16.w),
child: Html(data: detailsVo?.description ?? '')),
//举报和提示
detailsVo.viewState == DSViewState.busy
? Container(
alignment: Alignment.topCenter,
child: CupertinoActivityIndicator(
animating: true,
radius: 16,
),
)
: buildPromptWidget(
releaseTime: detailsVo.releaseTime,
checkText: detailsVo.checkText,
extras: {
'platform': detailsVo.platform,
'item_id': detailsVo.itemId,
'product_id': detailsVo.productId
},
onReportTap: () {
_showReceiveModalBottom(context,
reports: detailsVo.reports ?? []);
}),
],
),
),
SizedBox(
height: 4.w,
),
//相关优惠信息
buildRelatedDiscountWidget(
productEntitys: detailsVo.productEntitys,
onMoreTap: () async {
await BoostNavigator.instance.push(
RoutePath.PRICE_DISCOUNT_PAGE.path(),
arguments: {
'isAudituser': isAudituser,
'sellReportList': _detailsAction.sellReportList,
'excludeId': detailsVo.productId,
'keywords': detailsVo.itemTitle,
},
);
},
onItemTap: (ProductItemEntity entity, int index) async {
Map<String, dynamic> params = {
'isAudituser': isAudituser,
'sellReportList': _detailsAction.sellReportList,
'productId': entity.id,
'src': entity.itemPic,
'title': entity.itemTitle,
'remark': entity.special,
};
await BoostNavigator.instance.push(
RoutePath.PRICE_DETAIL_PAGE.path(),
withContainer: false,
arguments: params,
);
}),
],
)),
buildAppBar(context,
audituser: isAudituser,
detailsVo: detailsVo,
listenable: _appBarListenable,
onSharePressed: onSharePressed),
]),
onNotification: (ScrollNotification notification) {
if (notification.metrics.outOfRange == true ||
notification.metrics.axis != Axis.vertical) {
return false;
}
double pixels = notification.metrics.pixels;
if (pixels < 50 && _appBarListenable.value != 0) {
_appBarListenable.value = 0;
} else if (pixels >= 50 && pixels < 200) {
int alpha = ((pixels - 50) / 150 * 255).toInt();
if (alpha != _appBarListenable.value) {
_appBarListenable.value = alpha;
}
} else if (pixels >= 200 && _appBarListenable.value != 255) {
_appBarListenable.value = 255;
}
return true;
},
);
}
void onSharePressed({@required PriceDetailsVo detailsVo}) {
List<Map<String, dynamic>> items = [
{
'key': 'friend',
'name': 'assets/images/ic_share_friend.png',
'title': '朋友圈'
},
{
'key': 'wechat',
'name': 'assets/images/ic_share_wechat.png',
'title': '微信好友'
},
{
'key': 'copyLink',
'name': 'assets/images/ic_share_copy.png',
'title': '复制链接'
},
{
'key': 'product',
'name': 'assets/images/ic_share_product.png',
'title': '分享商品'
}
];
showShareModalBottom<String>(context, items: items, extras: {
'product_id': detailsVo.productId,
'platform': detailsVo.platform,
'item_id': detailsVo.itemId,
}).then((value) async {
if (value == 'product') {
if (widget.onCallbackParams != null) {
if (detailsVo.platform == '10' || detailsVo.platform == '1') {
widget.onCallbackParams(
{'itemId': detailsVo.itemId, 'platform': detailsVo.platform});
} else {
_showShareQRModalBottom(context: context, detailsVo: detailsVo);
}
}
} else if (value == 'wechat' || value == 'friend') {
try {
WeChatShareWebPageModel model = WeChatShareWebPageModel(
detailsVo.shareUrl ?? '',
title: detailsVo.subTitle ?? '小熊有好货',
description: detailsVo.itemTitle ?? '小熊有好货',
scene: value == 'wechat'
? WeChatScene.SESSION
: WeChatScene.TIMELINE,
thumbnail: WeChatImage.network(detailsVo.itemPic[0]));
shareToWeChat(model).then((value) {});
} catch (e) {
print("------------------------------> value:$e");
}
} else if (value == 'copyLink') {
if (detailsVo.shareMessage != null &&
detailsVo.shareMessage.isNotEmpty) {
widget.onCopyLink(detailsVo.shareMessage);
FlutterNativeToast.showToast('复制成功');
}
}
});
}
///
/// 京东和拼多多分享图片
/// context 上下文
/// detailsVo 商品详情
///
void _showShareQRModalBottom(
{BuildContext context, PriceDetailsVo detailsVo}) {
List<Map<String, dynamic>> items = [
{
'key': 'photo_save',
'name': 'assets/images/download_icon.png',
'title': '保存图片'
},
{
'key': 'photo_wechat',
'name': 'assets/images/ic_share_wechat.png',
'title': '微信好友'
},
{
'key': 'photo_friend',
'name': 'assets/images/ic_share_friend.png',
'title': '朋友圈'
}
];
showShareModalBottom<String>(context, items: items, extras: {
'product_id': detailsVo.productId,
'platform': detailsVo.platform,
'item_id': detailsVo.itemId,
}).then((value) async {
widget.onShare(context,
path: value, type: value, convertKey: _convertKey);
});
}
///
/// 举报弹窗
/// [context]
/// [reports] 举报弹窗的信息
///
void _showReceiveModalBottom(BuildContext context,
{@required List<ReportModel> reports}) {
showReceiveModalBottom<ReportModel>(context, items: reports).then((value) {
if (value != null) {
if (value.sub != null && value.sub.length > 0) {
_showReceiveModalBottom(context, reports: value.sub);
} else {
_detailsAction.report(reportType: value.value);
}
}
});
}
@override
Widget build(BuildContext context) {
return DSProviderWidget<PriceDetailsVo,
IPriceDetailsAction<PriceDetailsVo>>(
dsAction: _detailsAction,
builder: (BuildContext context, child) {
return Scaffold(
backgroundColor: Colors.white,
body: _buildBody(context,controller: scrollController, detailsVo: _detailsAction.vm),
bottomNavigationBar: _buildNavigationBar(detailsVo: _detailsAction.vm, detailsAction: _detailsAction),
);
},
);
}
ValueNotifier<int> likeValueNotifier = ValueNotifier(0);
ValueNotifier<int> favoritesValueNotifier = ValueNotifier(0);
List<String> likeImages = List<String>.empty(growable: true);
List<String> favoritesImages = List<String>.empty(growable: true);
///
/// 底部按钮区域
///
Widget _buildNavigationBar({
@required PriceDetailsVo detailsVo,
IPriceDetailsAction<PriceDetailsVo> detailsAction,
}) {
return buildBottomNavWidget(
like: detailsVo?.likeTimes ?? '0',
favorites: detailsVo?.collegeTimes ?? '0',
isLike: detailsVo?.isLike ?? false,
status: detailsVo.status,
isFavorites: detailsVo?.isFavorites ?? false,
text: detailsVo.viewState == DSViewState.busy
? '加载中'
: detailsVo?.buttonText ?? '',
extras: {
'platform': detailsVo.platform,
'item_id': detailsVo.itemId,
'product_id': detailsVo.productId
},
likeImages: likeImages,
likeValueNotifier: likeValueNotifier,
favoritesImages: favoritesImages,
favoritesValueNotifier: favoritesValueNotifier,
onShareTap: () => onSharePressed(detailsVo: detailsVo),
onBuyTap: () {
///
/// 如果是淘宝、天猫、和拼多多需要提示授权
///
detailsAction.goToBuyEvent().then((value) {
if (value != null && widget.onH5Callback != null) {
widget.onH5Callback(value.appLink, value.platform);
}
}).catchError((error) {
if (error is DSNetException) {
DSNetException exception = error;
FlutterNativeToast.showToast('${exception.message}');
}
});
},
onFavoritesTap: () {
detailsAction.userCollect(status: detailsVo?.isFavorites ?? false);
if (detailsVo?.isFavorites ?? false) {
favoritesValueNotifier.value = 0;
} else {
favoritesAnimationController.forward(from: 0);
}
},
onLikeTap: () {
detailsAction.userLike(status: detailsVo?.isLike ?? false);
if (detailsVo?.isLike ?? false) {
likeValueNotifier.value = 0;
} else {
likeAnimationController.forward(from: 0);
}
},
);
}
@override
void dispose() {
likeAnimationController?.dispose();
favoritesAnimationController?.dispose();
likeValueNotifier?.dispose();
favoritesValueNotifier?.dispose();
super.dispose();
}
@override
void showTipsMsg({String msg}) {
FlutterNativeToast.showToast(msg);
}
}
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_boost/boost_navigator.dart';
import 'package:price_module/framework/base/ds_provider_widget.dart';
import 'package:price_module/framework/model/ds_model.dart';
import 'package:price_module/page/action/discount/index.dart';
import 'package:price_module/page/model/discount/index.dart';
import 'package:price_module/page/widget/discount/index.dart';
import 'package:price_module/route/index.dart';
import 'package:price_module/widget/failed/failed_load_page.dart';
import 'package:price_module/widget/price/price_item.dart';
import 'package:price_module/widget/pull/pull_widget.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:xiaoxiong_repository/entity/product_item_entity.dart';
import 'package:common_module/utils/xapp_utils.dart';
///
/// 好价更多优惠页面
///
class PriceDiscountPage extends StatefulWidget {
final Map<String, dynamic> pageParam;
const PriceDiscountPage({Key key, @required this.pageParam})
: super(key: key);
@override
_PriceDiscountPageState createState() => _PriceDiscountPageState();
}
class _PriceDiscountPageState extends State<PriceDiscountPage>
with IPriceDiscountDelegate {
IPriceDiscountAction _discountAction;
bool _isAudituser = false;
List<dynamic> _sellReportList;
@override
void initState() {
_isAudituser = widget.pageParam['isAudituser'];
_sellReportList = widget.pageParam['sellReportList'];
_discountAction = PriceDiscountAction(
delegate: this,
pageParam: widget.pageParam,
);
super.initState();
}
RefreshController _refreshController = RefreshController();
@override
Widget build(BuildContext context) {
return DSProviderWidget<PriceDiscountVo,
IPriceDiscountAction<PriceDiscountVo>>(
dsAction: _discountAction,
builder: (BuildContext context, Widget child) {
return Scaffold(
appBar: buildAppBar(),
body: _buildBody1(vo: _discountAction.getVo),
);
},
);
}
Widget _buildBody1({PriceDiscountVo vo}) {
return PullWidget(
controller: _refreshController,
child: Container(child: _buildBody(vo: vo)),
onRefresh: () {
_discountAction.reloadData();
},
onLoad: () {
_discountAction.onLoad();
},
);
}
Widget _buildBody({PriceDiscountVo vo}) {
if (vo.viewState == DSViewState.busy) {
return LoadingPage(
padding: EdgeInsets.all(13.w),
length: 8,
);
} else if (vo.viewState == DSViewState.empty ||
vo.viewState == DSViewState.error) {
return FailedLoadPage(
margin: EdgeInsets.only(top: 68.w),
errorMsg:
vo.viewState == DSViewState.empty ? '未发现数据,请刷新重试' : '加载失败,请刷新重试',
onReloadTap: () {
//重新加载数据
_discountAction.reloadData();
},
);
}
return PriceListWidget(
padding: EdgeInsets.only(
bottom: 20.w,
left: 13.w,
right: 13.w,
),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
list: vo.productEntitys,
onItemTap: (ProductItemEntity entity, int index) async {
Map<String, dynamic> params = {
'isAudituser': _isAudituser,
'sellReportList': _sellReportList,
'productId': entity.id,
'src': entity.itemPic,
'title': entity.itemTitle,
'remark': entity.special
};
await BoostNavigator.instance.push(
RoutePath.PRICE_DETAIL_PAGE.path(),
withContainer: false,
arguments: params,
);
},
);
}
@override
void onLoadComplete({bool isLoading = true}) {
if (isLoading) {
_refreshController.loadComplete();
} else {
_refreshController.refreshCompleted();
}
}
@override
void onloadFailed({bool isLoading = true, String msg}) {
if (isLoading) {
_refreshController.loadFailed();
} else {
_refreshController.refreshFailed();
}
}
}
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:price_module/framework/base/ds_provider.dart';
import 'package:price_module/framework/base/ds_provider_widget.dart';
import 'package:price_module/framework/model/ds_model.dart';
import 'package:price_module/page/action/index.dart';
import 'package:price_module/page/model/index.dart';
import 'package:price_module/page/widget/index.dart';
import 'package:price_module/widget/failed/failed_load_page.dart';
import 'package:price_module/widget/price/price_item.dart';
import 'package:price_module/widget/pull/pull_widget.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:common_module/base/base_umeng_page_view_item_state.dart';
import 'package:xiaoxiong_repository/entity/product_item_entity.dart';
abstract class PricePageDelegate {
///
/// 路由
/// [type] 1、白菜专区 2、爆单页面 3、搜索页面 4、商品详情
/// [data] 当[type]等于4时,[data]为传递的数据
///
void onRouteTap({@required String type, Map<String, dynamic> data});
///
/// 获取好价的快捷Tab
///
List<dynamic> get sellingTabList;
}
class PricePage extends StatefulWidget {
final PricePageDelegate pageDelegate;
final List<dynamic> priceCategory;
final Map<int, dynamic> platformIconMap;
const PricePage(
{Key key, this.pageDelegate, this.platformIconMap, this.priceCategory})
: super(key: key);
@override
_PricePageState createState() => _PricePageState();
}
class _PricePageState extends BaseUmengPageViewItemState<PricePage>
with IPriceActionDelegate {
IPriceAction<PriceVo> _priceAction;
@override
void initState() {
super.initState();
_priceAction = PriceAction(delegate: this);
_priceAction.setPriceCategory(
sellingTabList: widget.pageDelegate.sellingTabList,
priceCategory: widget.priceCategory,
platformIconMap: widget.platformIconMap);
}
@override
Widget build(BuildContext context) {
super.build(context);
return DSProviderWidget<PriceVo, IPriceAction<PriceVo>>(
dsAction: _priceAction,
builder: (BuildContext context, child) {
return Scaffold(
backgroundColor: Color(0xFFF5F5F5),
appBar: buildAppBar(
onSearchTap: () {
widget.pageDelegate?.onRouteTap(type: '3');
},
isGrid: _priceAction.vm?.productListVo?.isGrid ?? false,
onLayoutTap: (bool isGrid) async {
_priceAction.vm?.productListVo?.isGrid = isGrid ?? true;
_priceAction.vm?.notifyListener(true);
}),
body: _buildBody(action: _priceAction),
);
});
}
RefreshController _refreshController = RefreshController();
ScrollController _scrollViewController = ScrollController();
ValueNotifier<int> scrollIndex = ValueNotifier<int>(0);
ValueNotifier<double> backColorListenable = ValueNotifier<double>(136.0.w);
GlobalKey anchorKey = GlobalKey();
Widget _buildBody({IPriceAction<PriceVo> action}) {
return Stack(
children: [
// 底部设置一个背景跟随滑动
backgroundColorWidget(valueListenable: backColorListenable),
NotificationListener(
child: PullWidget(
child: CustomScrollView(
controller: _scrollViewController,
cacheExtent: 1000,
slivers: [
//白菜专区和爆款专区
SliverToBoxAdapter(
child: buildZoneWidget(
onValueTap: (String type) {
widget.pageDelegate?.onRouteTap(type: type);
},
),
),
//分类标签
DSProvider.value(
vo: action.getTabListVo(),
builderWidget: (BuildContext context, TabListVo tabListVo,
Widget child) {
return buildCategoryTapWidget(
action.getTabListVo().tabList ?? [],
action.sellingTabs,
anchorKey: anchorKey,
sellTab: tabListVo.sellTab,
onChange: (int index, bool enable) {
tabListVo?.tabList[index].open = enable;
tabListVo.notifyListener(true);
}, onChangeSort: (Map<String, dynamic> data) {
tabListVo.sellTab = data;
tabListVo.notifyListener(true);
_priceAction.qryProductListEvent(page: 1);
double offset = _scrollViewController.offset;
if (offset > 70.w) {
_scrollViewController.animateTo(70.w,
duration: Duration(milliseconds: 50),
curve: Curves.linear);
}
}, onNodeChange: (int index, TabVo tabVo) {
tabVo.open = false;
if (tabVo.selected != null &&
tabVo.selected['is_default'] == '1') {
tabVo.selected = null;
}
tabListVo.notifyListener(true);
_priceAction.qryProductListEvent(page: 1);
double offset = _scrollViewController.offset;
if (offset > 70.w) {
_scrollViewController.animateTo(70.w,
duration: Duration(milliseconds: 50),
curve: Curves.linear);
}
});
}),
//商品列表
SliverToBoxAdapter(
child: DSProvider.value(
vo: action.getProductListVo(),
builderWidget: (BuildContext context,
ProductListVo productListVo, Widget child) {
if (productListVo.productList.isEmpty) {
if (productListVo.viewState == DSViewState.busy) {
return LoadingPage(
padding: EdgeInsets.all(13.w),
length: 6,
);
} else {
return buildEmptyProductList(
refresh: productListVo.viewState ==
DSViewState.error,
onResetTap: () {
action.resetProductList();
});
}
}
if (productListVo.isGrid ?? false) {
return PriceGridWidget(
shrinkWrap: true,
isHaoJia: true,
padding: EdgeInsets.only(
left: 13.w,
right: 13.w,
bottom: 13.w,
),
physics: NeverScrollableScrollPhysics(),
list: productListVo.productList,
onItemTap: (int index) {
ProductItemEntity itemVo =
productListVo.productList[index];
Map<String, dynamic> goodDetails = {
'productId': itemVo.id,
'src': itemVo.itemPic,
'title': itemVo.itemTitle,
'remark': itemVo.special
};
widget.pageDelegate
?.onRouteTap(type: '4', data: goodDetails);
},
);
} else {
return PriceListWidget(
shrinkWrap: true,
scroll: false,
isHaoJia: true,
padding: EdgeInsets.only(
left: 13.w, right: 13.w, bottom: 13.w),
physics: NeverScrollableScrollPhysics(),
list: productListVo.productList,
onItemTap: (ProductItemEntity entity, int index) {
Map<String, dynamic> goodDetails = {
'productId': entity.id,
'src': entity.itemPic,
'title': entity.itemTitle,
'remark': entity.special
};
widget.pageDelegate
?.onRouteTap(type: '4', data: goodDetails);
},
);
}
})),
],
),
onLoad: () {
action?.loadMore();
},
onRefresh: () {
action?.onRefresh();
},
controller: _refreshController,
),
onNotification: (ScrollNotification scroll) {
if (scroll.metrics.axis != Axis.vertical) {
return false;
}
//回到顶部
if (scroll.metrics.pixels > 500 && scrollIndex.value == 0) {
scrollIndex.value = 1;
} else if (scroll.metrics.pixels <= 500 && scrollIndex.value == 1) {
scrollIndex.value = 0;
}
RenderBox renderBox = anchorKey.currentContext.findRenderObject();
var offset =
renderBox.localToGlobal(Offset(0.0, renderBox.size.height));
if (scroll.runtimeType == ScrollStartNotification) {
print(
'=================> 滑动开始 ${scroll.metrics.pixels} ${offset.dx} ${offset.dy}');
} else if (scroll.runtimeType == ScrollEndNotification) {
print(
'=================> 滑动结束 ${scroll.metrics.pixels} ${offset.dx} ${offset.dy}');
} else if (scroll.runtimeType == ScrollUpdateNotification) {
///背景色 滑动更新
double value = (offset.dy - 136.w).ceilToDouble();
if (value != backColorListenable.value) {
backColorListenable.value = value;
}
}
return true;
},
),
//回到顶部
backTopWidget(
valueListenable: scrollIndex,
onPressed: () {
_scrollViewController.animateTo(0,
duration: Duration(milliseconds: 300), curve: Curves.linear);
})
],
);
}
@override
bool get wantKeepAlive => true;
@override
void onLoadComplete() {
_refreshController.loadComplete();
_refreshController.refreshCompleted();
}
@override
void onloadFailed({String msg}) {
_refreshController.loadFailed();
_refreshController.refreshCompleted(resetFooterState: true);
}
@override
void dispose() {
super.dispose();
}
@override
String getPageViewName() {
return 'entry_price_widget';
}
@override
Map<String, String> getPageViewArgs() {
return {};
}
}
import 'dart:ui';
import 'package:flutter/services.dart';
import 'package:html/dom.dart' as dom;
import 'package:cached_network_image/cached_network_image.dart';
import 'package:common_module/utils/audit_mode_utils.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_swiper/flutter_swiper.dart';
import 'package:price_module/page/model/details/index.dart';
import 'package:price_module/route/index.dart';
import 'package:price_module/widget/animation/image_animation_widget.dart';
import 'package:price_module/widget/image/image_widget.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:price_module/widget/pop/index.dart';
import 'package:price_module/widget/price/price_item.dart';
import 'package:xiaoxiong_repository/entity/product_item_entity.dart';
///
/// 顶部AppBar
/// [context] 上下文
/// [audituser] 是否是审核模式
/// [title] 标题
/// [actions] action
///
Widget buildAppBar(BuildContext context,
{@required PriceDetailsVo detailsVo,
bool audituser = false,
String title = '',
ValueListenable<int> listenable,
void Function({PriceDetailsVo detailsVo}) onSharePressed}) {
return ValueListenableBuilder<int>(
valueListenable: listenable,
builder: (BuildContext context, int value, Widget child) {
return Container(
height: 48.w + MediaQuery.of(context).padding.top,
child: AppBar(
backgroundColor: Colors.white.withAlpha(value.toInt()),
elevation: 0,
title: Text(
value <= 120 ? '' : '好价详情',
style: TextStyle(fontSize: 17.w, color: Color(0xFF333333)),
),
centerTitle: true,
leading: IconButton(
icon: ImageIcon(
AssetImage('assets/web_view_back_btn.png'),
size: 26.w,
),
onPressed: () {
Navigator.maybePop(context);
},
),
actions: audituser
? []
: [IconButton(
icon: Image.asset('assets/images/ic_btn_share1.png',package: 'price_module',width: 26.w),
onPressed: () => onSharePressed(detailsVo: detailsVo)
)],
));
},
);
}
///
/// Body区域布局
///
///
Widget buildBody(BuildContext context,
{@required ScrollController controller,
@required List<Widget> slivers,
PointerMoveEventListener onPointerMove,
PointerUpEventListener onPointerUp}) {
return Listener(
onPointerMove: onPointerMove,
onPointerUp: onPointerUp,
child: Container(
color: Color(0xFFF5F5F5),
child: CustomScrollView(
physics: ClampingScrollPhysics(),
controller: controller,
slivers: slivers,
)),
);
}
///
/// 好价商品详情滚动栏图片
/// [images] 滚动栏图片
///
Widget buildSwiperWidget({List<String> images}) {
Widget _swiperWidget;
int length = (images?.length ?? 0);
if (length <= 0) {
_swiperWidget = Image.asset(
'assets/images/placeholder_image.png',
package: 'price_module',
width: 200,
);
} else if (length == 1) {
_swiperWidget = _buildCachedImage(image: images[0]);
} else {
_swiperWidget = Swiper(
itemCount: length,
loop: false,
autoplay: false,
autoplayDelay: 5 * 1000,
onIndexChanged: (int index) {},
pagination: _buildSwiperPlugin(),
itemBuilder: (BuildContext context, int index) {
String image = images[index];
return _buildCachedImage(image: image);
},
);
}
return Container(
height: 200,
alignment: Alignment.bottomCenter,
child: _swiperWidget,
);
}
Widget _buildCachedImage({String image}) {
return CachedNetworkImage(
imageUrl: image,
placeholder: (BuildContext c, String s) {
return Image.asset(
'assets/images/placeholder_image.png',
package: 'price_module',
);
},
);
}
dynamic _buildSwiperPlugin() {
return SwiperCustomPagination(
builder: (BuildContext context, SwiperPluginConfig config) {
return Positioned(
bottom: 0.w,
right: 0.w,
child: Container(
width: 43.w,
height: 21.w,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Color(0xFF979797),
borderRadius: BorderRadius.circular(10.w)),
child: Text(
'${config.activeIndex + 1}/${config.itemCount}',
style: TextStyle(
color: Colors.white,
fontSize: 12.w,
fontWeight: FontWeight.w500),
),
));
});
}
///
/// 商品顶部的banner
/// [leading] 返回布局
/// [action] 右边菜单
/// [title] 顶部标题
/// [itemCount] banner的个数
/// [itemBuilder] banner Item布局
///
Widget buildSliverAppBar(
{double top = 0,
@required Widget leading,
Widget action,
@required bool isBusy,
String title,
@required double expandedHeight,
@required int itemCount,
@required IndexedWidgetBuilder itemBuilder}) {
return SliverAppBar(
elevation: 0.1,
backgroundColor: isBusy ? Color(0xFFE9E9E9) : Colors.white,
leading: leading,
actions: [action],
expandedHeight: expandedHeight,
title: Text(
title,
style: TextStyle(fontSize: 17.w, fontWeight: FontWeight.w600),
),
floating: false,
pinned: true,
snap: false,
systemOverlayStyle: SystemUiOverlayStyle(statusBarBrightness: Brightness.light),
flexibleSpace: FlexibleSpaceBar(
background: Container(
margin: EdgeInsets.only(top: top),
height: 200.w,
child: isBusy
? null
: Swiper(
key: UniqueKey(),
itemCount: itemCount,
loop: true,
autoplay: true,
autoplayDelay: 5 * 1000,
itemBuilder: itemBuilder,
onIndexChanged: (int index) {},
pagination: _buildCustomPagination(),
),
)));
}
///
/// 商品详情部分
///
Widget buildDetailsWidget({@required List<Widget> children}) {
return SliverToBoxAdapter(
child: Container(
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: children,
),
),
);
}
///
/// 商品详情标题和副标题
/// [title] 商品标题
/// [subTitle] 商品副标题
/// [expired] 是否过期
///
Widget buildTitleWidget({
@required String title,
@required String subTitle,
bool expired = false,
}) {
return Container(
margin: EdgeInsets.only(left: 13.w, right: 13.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.only(top: 13.w, bottom: 8.w),
child: Text(
title,
style: TextStyle(
fontSize: 18.w,
fontWeight: FontWeight.w600,
color: Color(0xFFFF0400)),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
Text(
subTitle,
style: TextStyle(
fontSize: 15.w,
fontWeight: FontWeight.w600,
color: Color(expired ? 0xFFFF0400 : 0xFF333333)),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
));
}
///
/// 商品信息
///
Widget buildStoreInfoWidget(
{@required PriceDetailsVo vo,
void Function(Coupon) onCouponTap,
void Function(Map<String, dynamic>) onPurchase}) {
if ((vo.shopTitle ?? '').isEmpty) {
return Container();
}
/// 凑单购买
Widget getCollectOrders({List<dynamic> orders}) {
Widget getOrdersItemWidget(dynamic data) {
return InkWell(
child: Container(
margin: EdgeInsets.only(left: 0),
child: Row(
children: [
Container(
child: Text(
data['item_title'],
style: TextStyle(
color: Color(0xFFFF0400),
fontSize: 12.w,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
constraints: BoxConstraints(maxWidth: 255)),
Image.asset(
'assets/images/ic_arrow_gray.png',
package: 'price_module',
width: 14.w,
height: 10.w,
color: Color(0xFFFF0400),
)
],
),),
onTap: () {
if (onPurchase != null) {
onPurchase(data);
}
},
);
}
return Wrap(
runSpacing:6.rpx,
children: List.generate(
orders.length,
(index) => getOrdersItemWidget(orders[index]),
),
);
}
///
/// 优惠卷Widget
/// [coupon] 优惠卷
///
Widget getCouponWidget({Coupon coupon}) {
return InkWell(child:RichText(text: TextSpan(children:[
WidgetSpan(child:Container(height:16.w,child:Text(coupon.name,style: TextStyle(color:Color(coupon.status == '1' ? 0xFFFF0400 : 0xFF999999),fontSize: 12.w)),),),
WidgetSpan(child:Container(
width: 16.w,
alignment:Alignment.centerLeft,
padding: EdgeInsets.only(top:1.w),
height:16.w,
child:Image.asset('assets/images/ic_arrow_gray.png',package: 'price_module',width: 14.w,height: 10.w,color: Color(coupon.status == '1' ? 0xFFFF0400 : 0xFF999999))))
])),onTap:(){
if (onCouponTap != null) {
onCouponTap(coupon);
}
},);
}
return Container(
margin: EdgeInsets.only(top: 16.w),
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: Color(0xFFFAFAFA),
borderRadius: BorderRadius.all(Radius.circular(8.0))),
child: Column(
children: [
Row(
children: [
Text('店铺',
style: TextStyle(fontSize: 12.w, color: Color(0xFF999999))),
SizedBox(
width: 12.w,
),
Text(vo.platformText ?? '',
style: TextStyle(fontSize: 12.w, color: Color(0xFF302600))),
Container(
margin: EdgeInsets.symmetric(horizontal: 8.w),
color: Color(0xFFD8D8D8),
width: 0.5.w,
height: 13.w,
),
Text(vo.shopTitle,
style: TextStyle(fontSize: 12.w, color: Color(0xFF302600))),
],
),
//凑单购买
(vo.collectOrders?.length ?? 0) > 0
? Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('凑单',
style: TextStyle(
fontSize: 12.w, color: Color(0xFF999999))),
SizedBox(
width: 12.w,
),
Container(
width: 280.w,
child: getCollectOrders(orders: vo.collectOrders),
),
],
),
margin: EdgeInsets.only(
top: 12.w,
),
)
: Container(),
//优惠
(vo.coupons?.length ?? 0) > 0
? Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('优惠',
style: TextStyle(
fontSize: 12.w, color: Color(0xFF999999))),
SizedBox(
width: 12.w,
),
Container(
width: 280.w,
child: Wrap(
direction:Axis.horizontal,
alignment: WrapAlignment.start,
runSpacing:6.w,
children: List.generate(
vo.coupons?.length ?? 0,
(index) => getCouponWidget(
coupon: vo.coupons[index],
),
),
))
],
),
margin: EdgeInsets.only(top: 12.w),
)
: Container(),
//返利
(vo.rebate ?? '').isNotEmpty
? Container(
child: Row(
children: [
Text('返利',
style: TextStyle(
fontSize: 12.w, color: Color(0xFF999999))),
SizedBox(
width: 12.w,
),
Container(
padding: EdgeInsets.symmetric(
horizontal: 4.w, vertical: 1.w),
decoration: BoxDecoration(
border: Border.all(
color: Color(0x80FF8000), width: 0.5.w),
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(8.0),
topRight: Radius.circular(8.0),
topLeft: Radius.circular(8.0))),
child: Text(vo.rebate,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 11.w,
color: Color(0xFFFF8000))))
],
),
margin: EdgeInsets.only(top: 12.w),
)
: Container(),
],
));
}
///
/// 商品信息
///
Widget buildProductNewlastWidget(
{@required Map<String, dynamic> newInfo,
void Function() goBuy,
void Function(Map<String, dynamic>) onCouponTap}) {
if (newInfo == null) {
return Container();
}
List<dynamic> couponInfo = [];
if (newInfo['coupon_info'] is List) {
couponInfo = newInfo['coupon_info'];
}
return Container(
margin: EdgeInsets.only(top: 16.w),
padding: EdgeInsets.only(left: 12.w, right: 12.w, top: 12.w, bottom: 10.w),
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Color(0x14000000),
spreadRadius: 2.2,
offset: Offset(0, 1),
blurRadius: 3)
],
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(8.0))),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('商品最新信息',
style: TextStyle(fontSize: 12.w, color: Color(0xFF999999))),
SizedBox(
height: 4.w,
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('${newInfo['item_title']}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Color(0xFF333333),
fontSize: 15.w,
fontWeight: FontWeight.w600)),
SizedBox(
height: 4.w,
),
Text('${newInfo['end_price_text']}',
style: TextStyle(
color: Color(0xFFFF0400),
fontSize: 14.w,
fontWeight: FontWeight.w600)),
],
),
flex: 1,
),
InkWell(
child: Container(
padding:
EdgeInsets.symmetric(vertical: 7.w, horizontal: 12.w),
child: Text('${newInfo['button_text']}',
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 12.w,
color: Color(0xFF302600))),
decoration: BoxDecoration(
color: Color(0xFFFFCA00),
borderRadius: BorderRadius.circular(28.w)),
),
onTap: goBuy),
],
),
couponInfo.isNotEmpty
? SizedBox(
height: 4.w,
)
: Container(),
couponInfo.isNotEmpty ? Divider() : Container(),
couponInfo.isNotEmpty
? Row(
children: [
Text('优惠',
style:
TextStyle(color: Color(0xFF999999), fontSize: 12.w)),
Expanded(
flex: 1,
child: Container(
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: couponInfo.length,
padding: EdgeInsets.zero,
itemBuilder: (BuildContext context, int index) {
Map<String, dynamic> data = couponInfo[index];
return InkWell(
child: Container(
alignment: Alignment.centerLeft,
margin: EdgeInsets.only(left: 12.w),
height:16.w,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(data['coupon_name'],
style: TextStyle(
height: 1.2,
color: Color(0xFFFF0400),
fontSize: 12.w)),
Container(alignment:Alignment.centerLeft,width:14.w,height:10.w,child:Image.asset(
'assets/images/ic_arrow_gray.png',
package: 'price_module',
width: 14.w,
height: 10.w,
color: Color(data['status'] == '1'
? 0xFFFF0400
: 0xFF999999),),)
],
)),
onTap: () {
if (onCouponTap != null) {
onCouponTap(data);
}
},
);
},
),
height: 18.w,
),
),
],
)
: Container(),
],
),
);
}
///
/// 优惠卷
/// [onReceiveTap] 点击领取优惠卷
///
Widget buildCouponWidget(BuildContext context,
{@required List<Coupon> coupons,
@required String couponText,
@required Function(Coupon) onReceiveTap}) {
if (coupons == null || coupons.isEmpty) {
return Container();
}
///
/// 优惠卷全部过期
///
if (couponText != null && couponText.isNotEmpty) {
Coupon coupon;
try {
coupon =
coupons.singleWhere((ele) => '${ele?.status}' == '1', orElse: () {
return null;
});
} catch (e) {
print(e);
}
return InkWell(
child: Container(
margin: EdgeInsets.only(top: 12.w, left: 13.w, right: 13.w),
decoration: BoxDecoration(
color: Color(0xFFF5F5F5),
borderRadius: BorderRadius.all(Radius.circular(8))),
child: Column(
children: [
Container(
margin: EdgeInsets.only(top: 10.w, left: 12.w),
alignment: Alignment.centerLeft,
child: Text(
'$couponText',
style: TextStyle(color: Color(0xFFFF8000), fontSize: 12.w),
),
),
Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
Container(
alignment: Alignment.centerLeft,
margin: EdgeInsets.only(left: 12.w, top: 12.w),
child: Text('优惠:',
style:
TextStyle(fontSize: 14.w, color: Color(0xFF302600))),
),
Expanded(
child: Container(
margin: EdgeInsets.only(top: 5.w, bottom: 10.w),
child: Wrap(
runSpacing:6.rpx,
children:
List<Widget>.generate(coupons.length, (index) {
Coupon coupon = coupons[index];
return Container(
child: Text(
'${coupon.name}',
style: TextStyle(
color: Color(coupon.status == '2'
? 0xFF999999
: 0xFFFF0400),
fontSize: 12.w,
fontWeight: FontWeight.w500,
),
),
margin: EdgeInsets.only(left: 8.w, top: 6.w),
padding: EdgeInsets.only(
top: 2.w, bottom: 2.w, left: 4.w, right: 4.w),
decoration: BoxDecoration(
border: Border.all(
color: Color(coupon.status == '2'
? 0xFFFFFFFF
: 0xFFFF0400),
width: 0.5.w),
borderRadius: BorderRadius.circular(6.w),
color: Color(0xFFFFFFFF)));
}),
),
),
flex: 1),
coupon == null
? Container(
alignment: Alignment.centerRight,
margin: EdgeInsets.only(right: 11.w, top: 12.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'已失效',
style: TextStyle(
fontSize: 14.w, color: Color(0xFFFF0400)),
),
],
),
)
: Container(
alignment: Alignment.centerRight,
margin: EdgeInsets.only(right: 11.w, top: 12.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'领取',
style: TextStyle(
fontSize: 14.w, color: Color(0xFF999999)),
),
Container(
child: Image.asset(
'assets/images/ic_arrow_gray.png',
package: 'price_module',
width: 14.w,
height: 10.w,
),
margin: EdgeInsets.only(top: 5.w),
)
],
),
),
])
],
),
),
onTap: coupon == null
? null
: () {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Color(0xFFF5F5F5),
borderRadius: BorderRadius.vertical(
top: Radius.circular(20))),
child: SafeArea(
child: Container(
height: 284.w,
decoration: BoxDecoration(
color: Color(0xFFF5F5F5),
borderRadius: BorderRadius.vertical(
top: Radius.circular(20))),
child: ListView.builder(
itemCount: coupons.length + 1,
itemBuilder:
(BuildContext context, int index) {
if (index == 0) {
return Container(
height: 50.w,
margin:
EdgeInsets.only(bottom: 2.w),
child: Stack(children: [
Center(
child: Text(
'领取优惠券',
style: TextStyle(
color:
Color(0xFF333333),
fontWeight:
FontWeight.w500,
fontSize: 15.w),
),
),
Align(
child: IconButton(
icon: Image.asset(
'assets/images/ic_pop_close.png',
package: 'price_module',
width: 24.w,
height: 24.w,
),
onPressed: () {
Navigator.maybePop(
context);
}),
alignment: Alignment.topRight,
),
]));
}
Coupon coupon = coupons[index - 1];
return Container(
margin: EdgeInsets.only(
left: 13.w,
right: 13.w,
top: index > 1 ? 8.w : 0),
padding: EdgeInsets.only(
left: 12.w, right: 12.w),
height: 48,
alignment: Alignment.centerLeft,
decoration: BoxDecoration(
color: Color(0xFFFFFFFF),
borderRadius: BorderRadius.all(
Radius.circular(8.w))),
child: Row(children: [
Expanded(
child: Text(
coupon.name,
style: TextStyle(
fontSize: 15.w,
fontWeight: FontWeight.w500,
color: Color(
coupon.status == '1'
? 0xFFFF0400
: 0xFF999999)),
)),
InkWell(
child: Container(
alignment: Alignment.center,
width: 60.w,
height: 30.w,
child: Text(
'领取',
style: TextStyle(
color: Color(
coupon.status == '1'
? 0xFFFFFFFF
: 0xFF999999)),
),
decoration: BoxDecoration(
color: Color(
coupon.status == '1'
? 0xFFFF0400
: 0xFFE9E9E9),
borderRadius:
BorderRadius.all(Radius
.circular(16.w))),
),
onTap: () {
if (onReceiveTap != null &&
coupon.status == '1') {
onReceiveTap(coupon);
Navigator.maybePop(context);
}
},
),
]),
);
}))));
}).then((value) {});
});
}
return InkWell(
child: Container(
margin: EdgeInsets.only(top: 12.w, left: 13.w, right: 13.w),
decoration: BoxDecoration(
color: Color(0xFFF5F5F5),
borderRadius: BorderRadius.all(Radius.circular(8))),
child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
Container(
alignment: Alignment.centerLeft,
margin: EdgeInsets.only(left: 12.w, top: 12.w),
child: Text('优惠:',
style: TextStyle(fontSize: 14.w, color: Color(0xFF302600))),
),
Expanded(
child: Container(
margin: EdgeInsets.only(top: 5.w, bottom: 10.w),
child: Wrap(
runSpacing: 6.rpx,
children: List<Widget>.generate(coupons.length, (index) {
Coupon coupon = coupons[index];
return Container(
child: Text(
'${coupon.name}',
style: TextStyle(
color: Color(
coupon.status == '2' ? 0xFF999999 : 0xFFFF0400),
fontSize: 12.w,
fontWeight: FontWeight.w500,
),
),
margin: EdgeInsets.only(left: 8.w, top: 6.w),
padding: EdgeInsets.only(
top: 2.w, bottom: 2.w, left: 4.w, right: 4.w),
decoration: BoxDecoration(
border: Border.all(
color: Color(coupon.status == '2'
? 0xFFFFFFFF
: 0xFFFF0400),
width: 0.5.w),
borderRadius: BorderRadius.circular(6.w),
color: Color(0xFFFFFFFF)));
}),
),
),
flex: 1),
Container(
alignment: Alignment.centerRight,
margin: EdgeInsets.only(right: 11.w, top: 12.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'领取',
style: TextStyle(fontSize: 14.w, color: Color(0xFF999999)),
),
Container(
child: Image.asset(
'assets/images/ic_arrow_gray.png',
package: 'price_module',
width: 14.w,
height: 10.w,
))
],
),
),
]),
),
onTap: () {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Color(0xFFF5F5F5),
borderRadius:
BorderRadius.vertical(top: Radius.circular(20))),
child: SafeArea(
child: Container(
height: 284.w,
decoration: BoxDecoration(
color: Color(0xFFF5F5F5),
borderRadius: BorderRadius.vertical(
top: Radius.circular(20))),
child: ListView.builder(
itemCount: coupons.length + 1,
itemBuilder: (BuildContext context, int index) {
if (index == 0) {
return Container(
height: 50.w,
margin: EdgeInsets.only(bottom: 2.w),
child: Stack(children: [
Center(
child: Text(
'领取优惠券',
style: TextStyle(
color: Color(0xFF333333),
fontWeight: FontWeight.w500,
fontSize: 15.w),
),
),
Align(
child: IconButton(
icon: Image.asset(
'assets/images/ic_pop_close.png',
package: 'price_module',
width: 24.w,
height: 24.w,
),
onPressed: () {
Navigator.maybePop(context);
}),
alignment: Alignment.topRight,
),
]));
}
Coupon coupon = coupons[index - 1];
return Container(
margin: EdgeInsets.only(
left: 13.w,
right: 13.w,
top: index > 1 ? 8.w : 0),
padding:
EdgeInsets.only(left: 12.w, right: 12.w),
height: 48,
alignment: Alignment.centerLeft,
decoration: BoxDecoration(
color: Color(0xFFFFFFFF),
borderRadius: BorderRadius.all(
Radius.circular(8.w))),
child: Row(children: [
Expanded(
child: Text(
coupon.name,
style: TextStyle(
fontSize: 15.w,
fontWeight: FontWeight.w500,
color: Color(coupon.status == '1'
? 0xFFFF0400
: 0xFF999999)),
)),
InkWell(
child: Container(
alignment: Alignment.center,
width: 60.w,
height: 30.w,
child: Text(
'领取',
style: TextStyle(
color: Color(coupon.status == '1'
? 0xFFFFFFFF
: 0xFF999999)),
),
decoration: BoxDecoration(
color: Color(coupon.status == '1'
? 0xFFFF0400
: 0xFFE9E9E9),
borderRadius: BorderRadius.all(
Radius.circular(16.w))),
),
onTap: () {
if (onReceiveTap != null &&
coupon.status == '1') {
onReceiveTap(coupon);
Navigator.maybePop(context);
}
},
),
]),
);
}))));
}).then((value) {});
});
}
///
/// 商品图文详情
/// [html] 商品图文信息
///
Widget buildProductDetailsWidget(BuildContext context,
{@required String html}) {
return Container(
margin: EdgeInsets.only(top: 16.w, bottom: 4.w, left: 4.w, right: 4.w),
child: Html(
data: html,
style: {},
customRender: {},
onLinkTap:(String url,RenderContext context,Map<String, String> attributes,dom.Element element) {
print("=========>onLinkTap $url");
},
onImageTap:(String url,RenderContext context,Map<String, String> attributes,dom.Element element) {
print("=========>onImageTap $url");
},
onImageError: (exception, stackTrace) {
print("=========>onImageError $stackTrace");
}),
);
}
///
/// 提示信息
/// [onReportTap] 举报按钮
///
Widget buildPromptWidget(
{@required String releaseTime,
@required String checkText,
VoidCallback onReportTap,
Map<String, dynamic> extras}) {
return Container(
child: Column(
children: [
Container(
margin: EdgeInsets.only(bottom: 8),
child: Row(children: [
Expanded(
child: Container(
child: Text(
'${releaseTime ?? '5s前'}发布 | ${checkText ?? '检查有效'}',
style: TextStyle(
fontWeight: FontWeight.w400,
fontSize: 12.w,
color: Color(0xFF666666)),
)),
flex: 1),
_buildIconText(
image: 'assets/images/ic_report.png',
width: 16.w,
text: '优惠举报',
onTap: onReportTap,
style: TextStyle(fontSize: 12.w, color: Color(0xFFFF8000)),
margin: EdgeInsets.only(left: 10.w),
)
])),
Container(
padding: EdgeInsets.only(top: 7.w, bottom: 12.w),
decoration: BoxDecoration(
border: Border(
top: BorderSide(color: Color(0xFFE9E9E9), width: 0.5.w))),
child: Text(
'折扣信息来自于平台推荐和网络,购买前请核实促销,若发现优惠过期或不实信息,请使用优惠举报功能',
style: TextStyle(fontSize: 12.w, color: Color(0xFF999999)),
),
)
],
));
}
///
/// 相关优惠信息
///
Widget buildRelatedDiscountWidget(
{List<ProductItemEntity> productEntitys,
void Function(ProductItemEntity, int) onItemTap,
void Function() onMoreTap}) {
bool isShowMore = false;
if (productEntitys == null || productEntitys.length <= 0) {
return Container();
} else if (productEntitys.length > 6) {
isShowMore = true;
productEntitys = productEntitys.getRange(0, 6).toList();
}
return Container(
decoration: BoxDecoration(
color: Color(0xFFF5F5F5),
borderRadius: BorderRadius.vertical(top: Radius.circular(8.w))),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin:
EdgeInsets.only(left: 13.w, right: 13.w, top: 12.w, bottom: 4.w),
child: Text(
'相关优惠',
style: TextStyle(),
),
),
PriceListWidget(
shrinkWrap: true,
padding: EdgeInsets.only(left: 13.w, right: 13.w),
list: productEntitys,
physics: NeverScrollableScrollPhysics(),
onItemTap: onItemTap,
),
///查看更多
isShowMore
? InkWell(
child: Container(
padding: EdgeInsets.symmetric(vertical: 11.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('查看更多优惠',
style: TextStyle(
color: Color(0xFF999999), fontSize: 13.w)),
Icon(
Icons.keyboard_arrow_right,
size: 16.w,
color: Color(0xFF999999),
)
],
),
),
onTap: onMoreTap,
)
: Container(height: 8.w, color: Color(0xFFF5F5F5))
],
),
);
}
///
/// 分享弹窗
///
///
Future<T> showShareModalBottom<T>(BuildContext context,{@required List<Map<String, dynamic>> items,Map<String, dynamic> extras = const {}}) {
return showPopBottomSheet(context:context,pageName:RoutePath.PRICE_DETAIL_PAGE.path(),items: items,extras: extras);
}
///
/// 举报弹窗
/// [context] 上下文对象
/// [items] 菜单列表
///
Future<T> showReceiveModalBottom<T>(BuildContext context,
{@required List<ReportModel> items}) {
return _showModalBottomList(context, itemCount: items.length,
itemBuilder: (BuildContext context, int index) {
Widget widget;
ReportModel reportModel;
if (index == items.length) {
widget = Container(
color: Colors.white,
alignment: Alignment.center,
height: 49,
margin: EdgeInsets.only(top: 8),
child: Text(
'取消',
style: TextStyle(color: Color(0xFF666666), fontSize: 15),
),
);
} else {
reportModel = items[index];
widget = Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: index == 0
? BorderRadius.vertical(top: Radius.circular(20))
: null),
alignment: Alignment.center,
height: 49,
child: Text(
items[index].text,
style: TextStyle(fontSize: 15, color: Color(0xFF302600)),
),
margin: EdgeInsets.only(top: index == 0 ? 0 : 0.6),
);
}
return InkWell(
child:widget,
onTap: () {
Navigator.pop(context, reportModel);
},
);
});
}
///
/// 底部导航栏目
/// [like] 喜欢的个数
/// [favorites] 收藏的个数
/// [onLikeTap] 喜欢
/// [onFavoritesTap] 收藏
/// [onBuyTap] 购买
///
Widget buildBottomNavWidget(
{@required String like,
@required String favorites,
bool isLike = false,
bool isFavorites = false,
@required String text,
VoidCallback onLikeTap,
VoidCallback onFavoritesTap,
VoidCallback onBuyTap,
VoidCallback onShareTap,
String status,
Map<String, dynamic> extras,
@required List<String> favoritesImages,
@required ValueNotifier<int> favoritesValueNotifier,
@required List<String> likeImages,
@required ValueNotifier<int> likeValueNotifier}) {
if (isLike) {
likeValueNotifier.value = likeImages.length - 1;
} else {
likeValueNotifier.value = 0;
}
if (isFavorites) {
favoritesValueNotifier.value = favoritesImages.length - 1;
} else {
favoritesValueNotifier.value = 0;
}
return SafeArea(
child: Container(
height: 49.w,
padding: EdgeInsets.only(left: 13, right: 13.w),
child: Row(
children: [
Row(
children: [
_buildAnimationText(
images: likeImages,
valueNotifier: likeValueNotifier,
text: like,
onTap: onLikeTap,
margin: EdgeInsets.only(right: 10.w),
width: 24.w,
height: 24.w,
style:TextStyle(fontSize: 11.w, color: Color(0xFF999999)),
),
_buildAnimationText(
images: favoritesImages,
valueNotifier: favoritesValueNotifier,
text: favorites,
margin: EdgeInsets.only(right: 10.w, left: 10.w),
onTap: onFavoritesTap,
width: 24.w,
height: 24.w,
style:TextStyle(fontSize: 11.w, color: Color(0xFF999999)),
),
],
),
AuditModeUtils.getInstance().isAuditMode()?Container(width:1):InkWell(
child: Container(
margin: EdgeInsets.only(right: 24.w, left: 10.w),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('assets/images/ic_btn_share1.png',package: 'price_module',width: 24.w,height: 24.w,),
Text('分享', style:TextStyle(fontSize: 11.w, color: Color(0xFF999999)))
],
),
),
onTap: onShareTap,
),
Expanded(
child: Container(
height: 44.w,
child: InkWell(
child: Container(
decoration: BoxDecoration(color: Color((status == null || status == '2') ? 0xFFFFCA00 : 0xFFE9E9E9),borderRadius:BorderRadius.all(Radius.circular(22.w))),
margin: EdgeInsets.symmetric(vertical: 2),
alignment: Alignment.center,
child: Text(text,style: TextStyle(color: Color(0xFF302600),fontSize: 15.w,fontWeight: FontWeight.w600))
),
onTap: status == '2' ? onBuyTap : null,
)
),
flex: 1,
),
],
)),
);
}
Widget _buildAnimationText({
@required String text,
ValueNotifier<int> valueNotifier,
double width1,
EdgeInsetsGeometry margin,
String package = 'price_module',
List<String> images,
double width,
double height,
VoidCallback onTap,
TextStyle style,
}) {
return Container(
padding: margin,
width: width1,
alignment: Alignment.centerLeft,
child: InkWell(
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
ImageAnimationWidget(
valueNotifier: valueNotifier,
images: images,
package: package,
width: width,
height: height,
),
Text(text, style: style)
]),
onTap: onTap,
));
}
///
/// 图标文字按钮
/// [image] 图片
/// [text] 文字
/// [onTap] 点击事件
///
Widget _buildIconText(
{@required String image,
@required String text,
double width = 24,
double width1,
EdgeInsetsGeometry margin,
TextStyle style,
VoidCallback onTap}) {
return Container(
width: width1,
padding: margin,
alignment: Alignment.center,
child: InkWell(
child: Row(children: [
ImageWidget(
name: image,
width: width,
),
Container(
child: Text(text, style: style),
margin: EdgeInsets.only(left: 4.w),
)
]),
onTap: onTap,
));
}
///
/// 自定义底部的导航条
///
SwiperPlugin _buildCustomPagination() {
return SwiperCustomPagination(
builder: (BuildContext context, SwiperPluginConfig config) {
return Positioned(
bottom: 12.w,
right: 12.w,
child: Container(
width: 43.w,
height: 21.w,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Color.fromRGBO(48, 38, 0, 0.4),
borderRadius: BorderRadius.circular(10.w)),
child: Text(
'${config.activeIndex + 1}/${config.itemCount}',
style: TextStyle(
color: Colors.white,
fontSize: 12.w,
fontWeight: FontWeight.w500),
),
));
});
}
Future<T> _showModalBottomList<T>(BuildContext context,
{@required int itemCount, @required IndexedWidgetBuilder itemBuilder}) {
return showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(20))),
child: SafeArea(
child: Container(
decoration: BoxDecoration(
color: Color(0xFFF5F5F5),
borderRadius:
BorderRadius.vertical(top: Radius.circular(20))),
child: ListView.builder(
shrinkWrap: true,
primary: true,
itemCount: itemCount + 1,
physics: NeverScrollableScrollPhysics(),
itemBuilder: itemBuilder),
)));
});
}
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:common_module/utils/xapp_utils.dart';
Widget buildAppBar(){
return AppBar(
backgroundColor:Colors.white,
centerTitle: true,
title: Text('全部优惠',style:TextStyle(fontSize:17.w,fontWeight:FontWeight.w600,color:Color(0xFF333333)),),
elevation:0,
);
}
\ No newline at end of file
import 'dart:math';
import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:price_module/page/model/index.dart';
import 'package:price_module/widget/appbar/index.dart';
import 'package:price_module/widget/image/image_widget.dart';
import 'package:price_module/widget/pull/pull_widget.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'other/other_widget.dart';
import 'package:common_module/utils/xapp_utils.dart';
///
/// 好价页面AppBar
/// [onSearchTap] 搜索点击事件
/// [onLayoutTap] 布局变化点击事件
///
Widget buildAppBar({
@required Function onSearchTap,
@required Function(bool) onLayoutTap,
bool isGrid = false,
}) {
return buildAppSearchBar(
height: 44.w,
padding: EdgeInsets.symmetric(horizontal: 13.w),
left: Image.asset(
'assets/images/ic_haojia_logo.png',
package: 'price_module',
width: 46.w,
height: 19.w,
),
child: Container(
height: 34.w,
margin: EdgeInsets.symmetric(horizontal: 8.w),
padding: EdgeInsets.symmetric(horizontal: 13.w),
decoration: BoxDecoration(
color: Color(0xFFFFFFFF), borderRadius: BorderRadius.circular(17.w)),
child: InkWell(
onTap: onSearchTap,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.asset(
'assets/images/ic_home_search.png',
package: 'price_module',
width: 16.w,
height: 16.w,
),
Container(
child: Text(
'搜索关键词或粘贴商品标题/链接',
style: TextStyle(
fontSize: 13.w,
color: Color(0xFFAAAAAA),
fontWeight: FontWeight.w400),
),
margin: EdgeInsets.only(left: 8.w),
)
],
),
),
),
right: InkWell(
child: Image.asset(
'assets/images/price_layout_' +
((isGrid ?? false) ? 'list' : 'grid') +
'_icon.png',
package: 'price_module',
width: 28.w,
height: 28.w,
),
onTap: () => onLayoutTap(!(isGrid ?? false)),
),
);
}
///
/// 专区部分单个Widget
/// [image] 单个Widget的图片
///
Widget _buildZoneItemWidget({@required String image, void Function() onTap}) {
return Expanded(
child: InkWell(
child: Container(
height: 64.w,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(12.w))),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ImageWidget(
name: image,
width: 118.w,
)
],
),
),
onTap: onTap,
),
);
}
///
/// 专区Widget
/// [onValueTap] 专区点击事件
///
Widget buildZoneWidget({void Function(String) onValueTap}) {
return Container(
margin: EdgeInsets.symmetric(vertical: 8.w, horizontal: 13.w),
child: Row(
children: [
_buildZoneItemWidget(
image: 'assets/images/img_baicai_area.png',
onTap: () {
if (onValueTap != null) {
onValueTap('1');
}
},
),
SizedBox(
width: 9,
),
_buildZoneItemWidget(
image: 'assets/images/img_baokuan_area.png',
onTap: () {
if (onValueTap != null) {
onValueTap('2');
}
},
),
],
));
}
///
/// 背景渐变色布局
/// [valueListenable] 数据变化
///
Widget backgroundColorWidget(
{@required ValueListenable<double> valueListenable}) {
return ValueListenableBuilder<double>(
valueListenable: valueListenable,
builder: (BuildContext context, double value, Widget child) {
return Positioned(
child: Container(
height: 200,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.white, Color(0x00FFFFFF)],
begin: Alignment.topCenter,
end: Alignment.bottomCenter)),
),
top: value,
left: 0,
right: 0,
);
},
);
}
///
/// 回到顶部
/// [valueListenable] 当value为1时显示回到顶部按钮
/// [onPressed] 点击回到顶部按钮
///
Widget backTopWidget(
{@required ValueListenable<int> valueListenable,
@required void Function() onPressed}) {
return ValueListenableBuilder<int>(
valueListenable: valueListenable,
builder: (BuildContext context, dynamic value, Widget child) {
if (value == 1) {
return Positioned(
bottom: 10.w,
right: 6.w,
child: IconButton(
icon: Image.asset('assets/all_top_icon.png'),
iconSize: 44.w,
onPressed: onPressed));
}
return Container();
});
}
Widget buildCustomScroll(
{@required List<Widget> children,
void Function() onRefresh,
void Function() onLoad,
RefreshController refreshController,
@required bool Function(ScrollNotification) onNotification,
@required ScrollController controller}) {
return NotificationListener(
onNotification: onNotification,
child: PullWidget(
child: CustomScrollView(
controller: controller,
cacheExtent: 1000,
slivers: children,
),
onRefresh: onRefresh,
controller: refreshController,
onLoad: onLoad,
),
);
}
///
/// 显示分类的弹窗
/// [structure] 分类的数据结构
///
Widget _buildClassifyShowWidget(
{List<dynamic> structure,
@required void Function(Map<String, dynamic>) onResultTap,
@required void Function() onResetTap,
Map<String, dynamic> selected}) {
return ClassifyShowWidget(
selected: selected,
structure: structure,
onResetTap: onResetTap,
onResultTap: onResultTap,
);
}
///
/// 商城和最新Tab
///
Widget _newestTabWidget({List<dynamic> structure,void Function(int) onChange,Map<String, dynamic> selected}) {
return Container(
decoration: BoxDecoration(color: Colors.white,borderRadius: BorderRadius.vertical(bottom: Radius.circular(10))),
child: Column(
children: List.generate(structure.length, (index) {
dynamic map = structure[index];
bool enable = false;
if (selected != null && selected.isNotEmpty) {
enable = map['type'] == selected['type'];
}
return InkWell(
onTap: () {
if (onChange != null) {
onChange(index);
}
},
child: Container(
alignment: Alignment.centerLeft,
padding: EdgeInsets.only(left: 30.w),
height: 44.w,
child: Text('${map['text']}',style: TextStyle(fontSize: 13.w,color: Color(enable ? 0xFFFF8000 : 0xFF666666)))
)
);
}),
)
);
}
///
/// Tab
///
Widget buildTapWidget(BuildContext context,
{@required List<TabVo> tabList,
void Function(int, bool) onChange,
@required void Function(int, TabVo) onNodeChange}) {
Widget categoryWidget({Widget Function() onHeaderWidget}) { return Container(
height: 40.w,
color: Color(0xFFF5F5F5),
child: Row(
children: List<Widget>.generate(tabList.length, (index) {
TabVo tabVo = tabList[index];
String title = getHeaderTitle(tabVo);
return Expanded(
child:OtherWidget(
onHeaderWidget:onHeaderWidget,
child: Container(
alignment: Alignment.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
constraints: BoxConstraints(maxWidth: 65.w),
child: Text('$title',
style: TextStyle(
color: Color(
(tabVo.selected != null || tabVo.open)
? 0xFFFF8000
: 0xFF999999),
fontSize: 13.w),
maxLines: 1,
overflow: TextOverflow.ellipsis)),
Container(
margin: EdgeInsets.only(left: 4.w),
child: Image.asset(
'assets/images/${tabVo.open ? 'ic_arrow_up' : 'ic_arrow_down'}.png',
package: 'price_module',
width: 6.w,
height: 4.w,
),
)
],
),
),
tapUpCallback: (OverlayWidgetBuilder callback) {
mCallback = callback;
if (onChange != null) {
onChange(index, true);
}
if (tabVo.type == '3') {
List<String> tabs = [];
tabVo.structure?.forEach((ele) {
tabs.add(ele['text']);
});
callback(
_newestTabWidget(
structure: tabVo.structure,
onChange: (v) {
if (tabVo.structure != null &&
tabVo.structure.length > v) {
dynamic data = tabVo.structure[v];
tabVo.selected = data;
if (onNodeChange != null) {
onNodeChange(index, tabVo);
}
callback(null);
}
},
selected: tabVo.selected,
),
);
} else if (tabVo.type == '4') {
callback(_buildPriceShowWidget(
structure: tabVo.structure[0],
keyName: tabVo.keyName,
onResultTap: (Map<String, dynamic> data) {
tabVo.selected = data;
if (onNodeChange != null) {
onNodeChange(index, tabVo);
}
callback(null);
},
onResetTap: () {
tabVo.selected = null;
if (onNodeChange != null) {
onNodeChange(index, tabVo);
}
callback(null);
},
selected: tabVo.selected ?? {},
));
} else if (tabVo.type == '5') {
callback(_buildClassifyShowWidget(
selected: tabVo.selected,
structure: tabVo.structure,
onResetTap: () {
tabVo.selected = null;
if (onNodeChange != null) {
onNodeChange(index, tabVo);
}
callback(null);
},
onResultTap: (Map<String, dynamic> result) {
tabVo.selected = result;
if (onNodeChange != null) {
onNodeChange(index, tabVo);
}
callback(null);
}));
}
},
onCloseTap: () {
if (onChange != null && _jindex != -1) {
onChange(_jindex, false);
}
if (onChange != null) {
onChange(index, false);
}
_jindex = -1;
},
),
);
})));}
return SliverPersistentHeader(
pinned: true,
floating: true,
delegate:
_SliverTabBarDelegate(maxHeight: 40, minHeight: 40, child: categoryWidget(onHeaderWidget:(){
if(_jindex is int && _jindex != -1){
for (var i = 0; i < tabList.length; i++) {
if(_jindex == i){tabList[i].open = true;}else{tabList[i].open = false;}
}
}
return OverlayEntryHeader(tabList:tabList,onHeaderTitle:(TabVo tabVo){
return getHeaderTitle(tabVo);
},onTap:(int jindex,TabVo tabVo){
_jindex = jindex;
if(mCallback == null){return;}
if (onChange != null) {
onChange(jindex, true);
}
onTapUpCallback(callback: mCallback,onChange:onChange,tabVo:tabVo,onNodeChange:onNodeChange,index:jindex);
},);
})),
);
}
///
/// 价格Table
///
Widget _buildPriceShowWidget(
{Map<String, dynamic> structure,
String keyName,
@required void Function(Map<String, dynamic>) onResultTap,
@required void Function() onResetTap,
Map<String, dynamic> selected}) {
TextEditingController controller1 = TextEditingController();
TextEditingController controller2 = TextEditingController();
String hintText1 = '';
String hintText2 = '';
String keyName1 = '';
String keyName2 = '';
try {
if (selected != null && selected['price_section'] is Map) {
selected = Map<String, dynamic>.from(selected['price_section']);
}
if (structure['label'] != null && structure['label'] is List) {
List<dynamic> labels = List<dynamic>.from(structure['label']);
if (labels.length > 0) {
hintText1 = '${labels[0]['text']}';
keyName1 = '${labels[0]['key_name']}';
}
if (labels.length > 1) {
hintText2 = '${labels[1]['text']}';
keyName2 = '${labels[1]['key_name']}';
}
}
if (selected[keyName1] is String) {
controller1.text = '${selected[keyName1]}';
}
if (selected[keyName2] is String) {
controller2.text = '${selected[keyName2]}';
}
} catch (e) {
print(e);
}
return Container(
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(bottom: Radius.circular(10))),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题
Container(
child: Text(
'${structure['text']}',
style: TextStyle(
color: Color(0xFF333333),
fontSize: 13,
fontWeight: FontWeight.w500),
),
margin: EdgeInsets.only(top: 12, left: 13),
),
//价格输入
Container(
margin: EdgeInsets.only(left: 13, right: 13, top: 12),
child: Row(children: [
Expanded(
child: Container(
alignment: Alignment.center,
height: 44,
padding: EdgeInsets.only(left: 48.w),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 0.5.w, color: Color(0xFFEDEDED)))),
child: TextField(
controller: controller1,
keyboardType:
TextInputType.numberWithOptions(decimal: true),
textAlign: TextAlign.left,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[0-9.]'))
],
cursorColor: Color(0xFFFFCA00),
decoration: InputDecoration(
hintText: hintText1,
hintStyle: TextStyle(
fontSize: 13.w, color: Color(0xFF999999)),
border: InputBorder.none),
style:
TextStyle(fontSize: 13.w, color: Color(0xFF333333)),
)),
flex: 1,
),
Container(
width: 69.w,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
alignment: Alignment.center,
width: 10.w,
height: 2.w,
color: Color(0xFF333333),
)
],
),
),
Expanded(
child: Container(
alignment: Alignment.center,
height: 44.w,
padding: EdgeInsets.only(left: 48.w),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 0.5.w, color: Color(0xFFEDEDED)))),
child: TextField(
controller: controller2,
keyboardType:
TextInputType.numberWithOptions(decimal: true),
textAlign: TextAlign.left,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[0-9.]'))
],
decoration: InputDecoration(
border: InputBorder.none,
hintText: hintText2,
hintStyle:
TextStyle(fontSize: 13.w, color: Color(0xFF999999)),
),
cursorColor: Color(0xFFFFCA00),
style:
TextStyle(fontSize: 13.w, color: Color(0xFF333333))),
),
flex: 1,
),
])),
//底部按钮
Container(
margin: EdgeInsets.only(left: 13, right: 13, bottom: 8, top: 28),
child: Row(
children: [
Expanded(
child:InkWell(
child: Container(
alignment: Alignment.center,
height: 40,
child: Text('重置',
style: TextStyle(
fontSize: 15, fontWeight: FontWeight.w500)),
decoration: BoxDecoration(
color: Color(0xFFF5F5F5),
borderRadius: BorderRadius.circular(8)),
),
onTap: () {
if (onResetTap != null) {
onResetTap();
}
}),
flex: 1,
),
SizedBox(width: 12),
Expanded(
child:InkWell(
child: Container(
alignment: Alignment.center,
height: 40,
child: Text('确认',
style: TextStyle(
fontSize: 15, fontWeight: FontWeight.w500)),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Color(0xFFFFBB00)),
),
onTap: () {
if (onResultTap != null) {
Map<String, dynamic> resultMap = {};
String lowest = controller1.text.trim();
String highest = controller2.text.trim();
if (keyName1 != null &&
keyName1.isNotEmpty &&
lowest.isNotEmpty) {
resultMap[keyName1] = lowest;
}
if (keyName2 != null &&
keyName2.isNotEmpty &&
highest.isNotEmpty) {
resultMap[keyName2] = highest;
}
if (resultMap.isEmpty) {
onResetTap();
} else {
Map<String, dynamic> result = {
'keyName': keyName,
keyName: resultMap
};
onResultTap(result);
}
}
}),
flex: 3,
),
],
),
)
],
),
);
}
Widget _buildNewestWidget(
{@required List<dynamic> structure,
@required void Function(int) onChange,
Map<String, dynamic> selected}) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(bottom: Radius.circular(10))),
child: ListView.builder(
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: structure?.length ?? 0,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
dynamic map = structure[index];
bool enable = false;
if (selected != null && selected.isNotEmpty) {
enable = map['type'] == selected['type'];
}
return InkWell(
child: Container(
alignment: Alignment.centerLeft,
padding: EdgeInsets.only(left: 30.w),
height: 44.w,
child: Text("${map['text']}",
style: TextStyle(
fontSize: 13.w,
color: Color(enable ? 0xFFFF8000 : 0xFF666666))),
),
onTap: () {
if (onChange != null) {
onChange(index);
}
},
);
},
),
);
}
String getHeaderTitle(TabVo tabVo ){
String title = '';
if (tabVo.selected == null) {
title = tabVo.text;
} else if (tabVo.keyName == 'price_section') {
try {
if (tabVo.selected['price_section'] is Map) {
Map<String, dynamic>.from(tabVo.selected['price_section'])
.values
.forEach((ele) {
if (title.isNotEmpty) {
title = title + '-';
}
title = title + '$ele';
});
}
} catch (e) {
title = tabVo.text;
}
} else {
title = '${tabVo.selected['text'] ?? tabVo.selected['name']}';
}
return title;
}
void onTapUpCallback({OverlayWidgetBuilder callback,void Function(int, bool) onChange,TabVo tabVo,void Function(int, TabVo) onNodeChange,int index}){
if (tabVo.type == '3') {
callback(_buildNewestWidget(
structure: tabVo.structure ?? [],
onChange: (v) {
if (tabVo.structure != null && tabVo.structure.length > v) {
callback(null);
dynamic data = tabVo.structure[v];
tabVo.selected = data;
if (onNodeChange != null) {
onNodeChange(index, tabVo);
}
}
},
selected: tabVo.selected));
} else if (tabVo.type == '4') {
callback(_buildPriceShowWidget(
structure: tabVo.structure[0],
keyName: tabVo.keyName,
onResultTap: (Map<String, dynamic> data) {
tabVo.selected = data;
if (onNodeChange != null) {
onNodeChange(index, tabVo);
}
callback(null);
},
onResetTap: () {
tabVo.selected = null;
if (onNodeChange != null) {
onNodeChange(index, tabVo);
}
callback(null);
},
selected: tabVo.selected ?? {},
));
} else if (tabVo.type == '5') {
callback(_buildClassifyShowWidget(
selected: tabVo.selected,
structure: tabVo.structure,
onResetTap: () {
tabVo.selected = null;
if (onNodeChange != null) {
onNodeChange(index, tabVo);
}
callback(null);
},
onResultTap: (Map<String, dynamic> result) {
tabVo.selected = result;
if (onNodeChange != null) {
onNodeChange(index, tabVo);
}
callback(null);
}));
}
}
OverlayWidgetBuilder mCallback;
int _jindex = -1;
///
/// 分类
///
Widget buildCategoryTapWidget(List<TabVo> tabList, List<dynamic> sellingTabs,
{GlobalKey anchorKey,
Map<String, dynamic> sellTab,
void Function(Map<String, dynamic>) onChangeSort,
@required void Function(int, bool) onChange,
@required void Function(int, TabVo) onNodeChange}) {
Widget categoryWidget(int index,{Widget Function() onHeaderWidget}) {
TabVo tabVo = tabList[index];
String title = getHeaderTitle(tabVo);
return Expanded(
child: OtherWidget(
onHeaderWidget:onHeaderWidget,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
constraints: BoxConstraints(maxWidth: 65.w),
margin: EdgeInsets.only(right: 4.w),
child: Text('$title',
style: TextStyle(
color: Color((tabVo.selected != null || tabVo.open)
? 0xFFFF8000
: 0xFF302600),
fontSize: 13.w),
maxLines: 1,
overflow: TextOverflow.ellipsis)),
Image.asset(
'assets/images/${tabVo.open ? 'ic_arrow_up' : 'ic_arrow_down'}.png',
package: 'price_module',
width: 6.w,
height: 4.w,
)
],
),
onCloseTap: () {
if (onChange != null && _jindex != -1) {
onChange(_jindex, false);
}
if (onChange != null) {
onChange(index, false);
}
_jindex = -1;
},
tapUpCallback: (OverlayWidgetBuilder callback) {
mCallback = callback;
if (onChange != null) {
onChange(index, true);
}
onTapUpCallback(callback: mCallback,onChange:onChange,tabVo:tabVo,onNodeChange:onNodeChange,index:index);
},
),
flex: 1);
}
return SliverPersistentHeader(
pinned: true,
floating: true,
delegate: _SliverTabBarDelegate(
maxHeight: 64.w + 12.w,
minHeight: 64.w + 12.w,
child: Container(
key: anchorKey,
decoration: BoxDecoration(
color: Color(0xFFFFFFFF),
borderRadius:
BorderRadius.vertical(top: Radius.circular(12.0))),
child: Column(
children: [
Container(
height: 40.w,
child: Row(children:List.generate(tabList.length, (index)=>categoryWidget(
index,
onHeaderWidget:(){
if(_jindex is int && _jindex != -1){
for (var i = 0; i < tabList.length; i++) {
if(_jindex == i){
tabList[i].open = true;
}else{
tabList[i].open = false;
}
}
}
return OverlayEntryHeader(tabList:tabList,onHeaderTitle:(TabVo tabVo){
return getHeaderTitle(tabVo);
},onTap:(int jindex,TabVo tabVo){
_jindex = jindex;
if(mCallback == null){return;}
if (onChange != null) {
onChange(jindex, true);
}
onTapUpCallback(callback: mCallback,onChange:onChange,tabVo:tabVo,onNodeChange:onNodeChange,index:jindex);
},);
})
))
),
Container(
height: 24.w + 12.w,
child: ListView.builder(
itemCount: sellingTabs.length,
padding: EdgeInsets.only(left: 5.w, right: 13.w),
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
Map<String, dynamic> sellingMap = sellingTabs[index];
bool enable = false;
if (sellTab != null) {
enable = sellingMap['id'] == sellTab['id'];
}
return InkWell(
child: Container(
color: Colors.transparent,
child: Column(
children: [
Container(
height: 24.w,
margin: EdgeInsets.only(left: 8.w),
padding:
EdgeInsets.symmetric(horizontal: 10.w),
alignment: Alignment.center,
decoration: BoxDecoration(
color: Color(
enable ? 0x1AFF8000 : 0xFFF5F5F5),
borderRadius: BorderRadius.all(
Radius.circular(4.w))),
child: Text(
'${sellingMap['name']}',
style: TextStyle(
color: Color(
enable ? 0xFFFF8000 : 0xFF666666),
fontSize: 12.w),
))
],
)),
onTap: () {
if (onChangeSort != null) {
if (enable) {
onChangeSort({});
} else {
onChangeSort(sellingMap);
}
}
},
);
},
),
)
],
),
)));
}
///
/// 当搜索的商品数据为空
///
Widget buildEmptyProductList({bool refresh = false, void Function() onResetTap}) {
Widget widget = Container(
margin: EdgeInsets.only(top: 120.w),
child: Column(
children: [
ImageWidget(name: 'assets/images/img_ empty_data.png', width: 168),
Container(child: Text('啥也没有空空如也~',style: TextStyle(fontSize: 14, color: Color(0xFF999999))),margin: EdgeInsets.symmetric(vertical: 12)),
InkWell(
onTap: onResetTap,
child: Container(
width: 160,
height: 40,
alignment: Alignment.center,
child: Text(refresh ? '点击刷新' : '重置筛选',style: TextStyle(color: Color(0xFF3D3101),fontSize: 16,fontWeight: FontWeight.w500)),
decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(20)),color: Color(0xFFFFCA00)),
)
),
],
)
);
return widget;
}
class _SliverTabBarDelegate extends SliverPersistentHeaderDelegate {
_SliverTabBarDelegate({
@required this.minHeight,
@required this.maxHeight,
@required this.child,
});
final double minHeight;
final double maxHeight;
final Widget child;
@override
double get minExtent => minHeight;
@override
double get maxExtent => max(maxHeight, minHeight);
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return new SizedBox.expand(child: child);
}
@override
bool shouldRebuild(_SliverTabBarDelegate oldDelegate) {
return maxHeight != oldDelegate.maxHeight ||
minHeight != oldDelegate.minHeight ||
child != oldDelegate.child;
}
}
///
/// 分类Widget
///
class ClassifyShowWidget extends StatefulWidget {
final Map<String, dynamic> selected;
final void Function(Map<String, dynamic>) onResultTap;
final void Function() onResetTap;
final List<dynamic> structure;
const ClassifyShowWidget(
{Key key,
this.structure,
@required this.onResultTap,
@required this.onResetTap,
this.selected})
: super(key: key);
@override
_ClassifyShowWidgetState createState() => _ClassifyShowWidgetState();
}
class _ClassifyShowWidgetState extends State<ClassifyShowWidget> {
List<dynamic> structure, structureNode1;
int childNode1 = 0, childNode2 = 0;
@override
void initState() {
this.structure = widget.structure;
try {
if (widget.selected != null && widget.selected.isNotEmpty) {
String level = widget.selected['level'];
String parentId = widget.selected['parent_id'];
String id = widget.selected['id'];
if (level == '1') {
childNode1 = this.structure.indexWhere((ele) => (ele['id'] == id));
} else if (level == '2') {
childNode1 =
this.structure.indexWhere((ele) => (ele['id'] == parentId));
if (this.structure[childNode1] != null) {
childNode2 = List<dynamic>.from(Map<String, dynamic>.from(
this.structure[childNode1])['child'])
.indexWhere((ele) => (ele['id'] == id));
}
}
}
} catch (e) {}
super.initState();
}
@override
Widget build(BuildContext context) {
if (structure[childNode1]['child'] != null) {
structureNode1 = List<dynamic>.from(structure[childNode1]['child']);
}
return Container(
alignment: Alignment.topCenter,
constraints: BoxConstraints(maxHeight: 470.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(bottom: Radius.circular(10))),
child: Column(
children: [
Expanded(
child: Container(
child: Row(
children: [
Container(
width: 94.w,
padding: EdgeInsets.all(0),
child: ListView.builder(
itemCount: structure.length,
padding: EdgeInsets.all(0),
itemBuilder: (BuildContext context, int index) {
dynamic dMap = structure[index];
return InkWell(
child: Container(
padding: EdgeInsets.only(left: 12.w),
height: 44.w,
child: Text(
'${dMap['name']}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: childNode1 == index
? FontWeight.w500
: FontWeight.w400,
fontSize: 13.w,
color: Color(childNode1 == index
? 0xFFFF8000
: 0xFF666666)),
),
alignment: Alignment.centerLeft,
decoration: BoxDecoration(
color: childNode1 == index
? Colors.white
: Color(0xFFF5F5F5),
borderRadius: BorderRadius.only(
topRight: Radius.circular(
childNode1 + 1 == index ? 6.w : 0),
bottomRight: Radius.circular(
childNode1 - 1 == index ? 6.w : 0))),
),
onTap: () {
setState(() {
childNode1 = index;
childNode2 = 0;
});
},
);
}),
),
if (structureNode1 != null && structureNode1.length > 0)
Expanded(
child: Container(
color: Colors.white,
child: GridView.builder(
padding: EdgeInsets.all(0),
gridDelegate:SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2,crossAxisSpacing: 1.w,childAspectRatio: 3.3.w),
itemCount: structureNode1.length,
itemBuilder: (BuildContext context, int index) {
dynamic dMap = structureNode1[index];
return InkWell(
child: Container(
padding: EdgeInsets.only(left: 24.w),
height: 44.w,
child: Text('${dMap['name']}',maxLines: 1,overflow: TextOverflow.ellipsis,style: TextStyle(fontSize: 13.w,fontWeight: childNode2 == index ? FontWeight.w500 : FontWeight.w400,color: Color(childNode2 == index? 0xFFFF8000: 0xFF666666))),
alignment: Alignment.centerLeft,
),
onTap: () {
setState(() {childNode2 = index;});
}
);
}
),
),
flex: 1
)
],
)),
flex: 1,
),
//底部按钮
Container(
margin: EdgeInsets.only(left: 13, right: 13, bottom: 8, top: 8),
child: Row(
children: [
Expanded(
child:InkWell(
child: Container(
alignment: Alignment.center,
height: 40.w,
child: Text('重置',
style: TextStyle(
fontSize: 15.w,
fontWeight: FontWeight.w500)),
decoration: BoxDecoration(
color: Color(0xFFF5F5F5),
borderRadius: BorderRadius.circular(8.w)),
),
onTap: () {
if (widget.onResetTap != null) {
widget.onResetTap();
}
}),
flex: 1,
),
SizedBox(width: 12.w),
Expanded(
child:InkWell(
child: Container(
alignment: Alignment.center,
height: 40.w,
child: Text('确认',style: TextStyle(fontSize: 15.w,fontWeight: FontWeight.w500)),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8),color: Color(0xFFFFBB00)),
),
onTap: () {
if (structureNode1 != null && structureNode1.isNotEmpty) {
Map<String, dynamic> result = {};
if (childNode2 == 0) {
result = Map<String, dynamic>.from(structure[childNode1]);
} else {
result = Map<String, dynamic>.from(structureNode1[childNode2]);
}
result.remove('child');
if (widget.onResultTap != null) {
widget.onResultTap(result);
}
}
}),
flex: 3,
),
],
),
)
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:price_module/page/model/index.dart';
import 'package:price_module/utils/event_bus_utils.dart';
import 'package:common_module/utils/xapp_utils.dart';
typedef OverlayTapUpCallback = void Function(OverlayWidgetBuilder overlayBuilder);
typedef OverlayWidgetBuilder = void Function(Widget overlayWidget);
class OtherWidget extends StatefulWidget {
final Widget child;
final OverlayTapUpCallback tapUpCallback;
final GestureTapCallback onCloseTap;
final Widget Function() onHeaderWidget;
const OtherWidget({Key key,
@required this.child,
this.tapUpCallback,
this.onCloseTap,
@required this.onHeaderWidget,
}) : super(key: key);
@override
_OtherWidgetState createState() => _OtherWidgetState();
}
class _OtherWidgetState extends State<OtherWidget> {
GlobalKey globalKey = GlobalKey();
OverlayEntry overlayEntry;
void onCloseEntry({bool close}) {
if (overlayEntry != null) {
overlayEntry.remove();
overlayEntry = null;
}
if(widget.onCloseTap != null){
widget.onCloseTap();
}
}
void showOverlayEntry({@required double height,@required double headerHeight ,@required Widget overlayWidget}) {
if(overlayEntry != null){
overlayEntry.remove();
overlayEntry = null;
}
overlayEntry = new OverlayEntry(maintainState: true,builder: (BuildContext context) {
return Container(child:Material(child:Column(children: [
InkWell(child:Container(height:height ,color:Colors.transparent),onTap:onCloseEntry),
Container(
alignment: Alignment.center,
child:widget.onHeaderWidget(),height:headerHeight,
color: Colors.transparent,
),
Container(child:overlayWidget,color:Color(0xFF000000).withAlpha(102)),
Expanded(
child: InkWell(child: Container(color:Color(0xFF000000).withAlpha(102)),onTap: onCloseEntry),
flex: 1,
),
],),color:Colors.transparent,));
});
Overlay.of(context,rootOverlay: true).insert(overlayEntry);
}
@override
void initState() {
eventBus.on<String>().listen((event) {
if(event == 'close'){
onCloseEntry(close:true);
}
});
super.initState();
}
@override
Widget build(BuildContext context) {
return InkWell(
key: globalKey,
child: Container(child:widget.child,alignment:Alignment.center,),
onTap:(){
if (overlayEntry != null) {
overlayEntry.remove();
overlayEntry = null;
}
RenderBox renderBox = context.findRenderObject();
Offset offset = renderBox.localToGlobal(Offset.zero);
widget.tapUpCallback((Widget overlayWidget){
if(overlayWidget == null){
onCloseEntry(close:true);
}else{
showOverlayEntry(height: offset.dy,headerHeight:renderBox.size.height,overlayWidget:overlayWidget);
}
});
},
);
}
@override
void dispose() {
super.dispose();
}
}
class OverlayEntryHeader extends StatefulWidget {
final List<TabVo> tabList;
final String Function(TabVo) onHeaderTitle;
final void Function(int,TabVo) onTap;
const OverlayEntryHeader({ Key key,@required this.tabList,this.onHeaderTitle,this.onTap}) : super(key: key);
@override
_OverlayEntryHeaderState createState() => _OverlayEntryHeaderState();
}
class _OverlayEntryHeaderState extends State<OverlayEntryHeader> {
List<TabVo> tabList;
@override
void initState() {
tabList = widget.tabList;
super.initState();
}
@override
Widget build(BuildContext context) {
return Row(
children:List.generate(tabList.length,(index){
TabVo tabVo = tabList[index];
String title = widget.onHeaderTitle(tabVo);
return Expanded(
child:InkWell(child:Container(
alignment: Alignment.center,
child:Row(mainAxisAlignment:MainAxisAlignment.center,children: [
Container(child:Text(
'$title',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: Color((tabVo.selected != null || tabVo.open)? 0xFFFF8000: 0xFF302600),fontSize: 13.w)
),constraints: BoxConstraints(maxWidth: 65.w),margin: EdgeInsets.only(right: 4.w),),
Image.asset(
'assets/images/${tabVo.open ? 'ic_arrow_up' : 'ic_arrow_down'}.png',
package: 'price_module',
width: 6.w,
height: 4.w,
)
],)
),onTap:(){
widget.onTap(index,tabVo);
},),
flex:1,
);
})
);
}
}
\ No newline at end of file
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:price_module/framework/base/ds_provider.dart';
import 'package:price_module/framework/base/ds_provider_widget.dart';
import 'package:price_module/framework/model/ds_model.dart';
import 'package:price_module/page/action/index.dart';
import 'package:price_module/page/model/index.dart';
import 'package:price_module/page/widget/index.dart';
import 'package:common_module/utils/xapp_utils.dart';
import 'package:price_module/widget/price/price_item.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:xiaoxiong_repository/entity/product_item_entity.dart';
///
/// 代理类,类似接口
///
abstract class SearchWidgetDelegate {
///
/// 好价关键词搜索
/// keywords 待搜索的关键词
///
void onKeywordsSearch(String keywords);
///
/// 设置页面isGrid
///
void setPageGrid({bool isGrid});
}
///
/// 好价搜索页面
///
class SearchWidget extends StatefulWidget {
final bool isGrid;
final List<dynamic> priceCategory;
final Map<int, dynamic> platformIconMap;
final String keywords;
final void Function(Map<String, dynamic>) onGoodDetails;
final void Function(SearchWidgetDelegate) onWidgetDelegate;
const SearchWidget(
{Key key,
@required this.priceCategory,
@required this.keywords,
@required this.platformIconMap,
this.isGrid = true,
this.onGoodDetails,
this.onWidgetDelegate})
: super(key: key);
@override
_SearchWidgetState createState() => _SearchWidgetState();
}
class _SearchWidgetState extends State<SearchWidget>
with
AutomaticKeepAliveClientMixin,
SearchWidgetDelegate,
IPriceActionDelegate {
RefreshController _refreshController = RefreshController();
IPriceAction<PriceVo> _priceAction;
@override
bool get wantKeepAlive => true;
@override
void initState() {
if (widget.onWidgetDelegate != null) {
widget.onWidgetDelegate(this);
}
_priceAction = PriceAction(delegate: this);
_searchKeywords = widget.keywords;
_priceAction.setPriceCategory(
priceCategory: widget.priceCategory,
keywords: _searchKeywords,
platformIconMap: widget.platformIconMap);
super.initState();
}
@override
void setPageGrid({bool isGrid}) {
_priceAction.getProductListVo()?.isGrid = isGrid;
_priceAction.getProductListVo()?.notifyListener(true);
}
String _searchKeywords;
@override
void onKeywordsSearch(String keywords) {
if (_searchKeywords != keywords) {
_searchKeywords = keywords;
_priceAction.qryProductListEvent(keywords: keywords);
}
}
ScrollController controller = ScrollController();
@override
Widget build(BuildContext context) {
super.build(context);
return DSProviderWidget<PriceVo, IPriceAction<PriceVo>>(
dsAction: _priceAction,
builder: (BuildContext context, child) {
return Container(
color: Color(0xFFF5F5F5),
child: buildCustomScroll(
onRefresh: null,
onLoad: () {
_priceAction?.loadMore();
},
onNotification: (ScrollNotification data) {
return true;
},
controller: controller,
refreshController: _refreshController,
children: [
//Tab分类Widget
buildSortHeader(
action: _priceAction, keywords: widget.keywords),
//商品列表
DSProvider.value(
vo: _priceAction.getProductListVo(),
builderWidget: (BuildContext context,
ProductListVo productListVo, Widget widget1) {
if (productListVo.productList.isEmpty) {
if (productListVo.viewState == DSViewState.busy) {
return SliverToBoxAdapter(
child: Container(
margin: EdgeInsets.only(top: 120.w),
child: CupertinoActivityIndicator(
animating: true,
radius: 16,
),
));
}
return SliverToBoxAdapter(
child: Container(
margin: EdgeInsets.only(top: 100.w),
child: Column(
children: [
Image.asset(
'assets/zanwushuju.png',
width: 261.w,
height: 165.w,
fit: BoxFit.cover,
),
SizedBox(height: 16.w),
Text('暂无数据',
style: new TextStyle(
color: Color(0xFF999999),
fontSize: 14.w)),
],
)));
} else {
if (productListVo.isGrid ?? false) {
return SliverToBoxAdapter(
child: PriceGridWidget(
shrinkWrap: true,
padding: EdgeInsets.all(13.w),
physics: NeverScrollableScrollPhysics(),
list: productListVo.productList,
onItemTap: (int index) {
ProductItemEntity entity =
productListVo.productList[index];
Map<String, dynamic> goodDetails = {
'productId': entity.id,
'src': entity.itemPic,
'title': entity.itemTitle,
'remark': entity.special
};
if (widget.onGoodDetails != null) {
widget.onGoodDetails(goodDetails);
}
}));
} else {
return SliverToBoxAdapter(
child: PriceListWidget(
shrinkWrap: true,
padding: EdgeInsets.only(
left: 13.w, right: 13.w, bottom: 13.w),
physics: NeverScrollableScrollPhysics(),
list: productListVo.productList,
onItemTap:
(ProductItemEntity entity, int index) {
Map<String, dynamic> goodDetails = {
'productId': entity.id,
'src': entity.itemPic,
'title': entity.itemTitle,
'remark': entity.special
};
if (widget.onGoodDetails != null) {
widget.onGoodDetails(goodDetails);
}
},
));
}
}
}),
]));
});
}
//Tab分类Widget
Widget buildSortHeader(
{@required IPriceAction<PriceVo> action, String keywords}) {
return DSProvider.value(
vo: action.getTabListVo(),
builderWidget:
(BuildContext context, TabListVo tabListVo, Widget widget) {
return buildTapWidget(context, tabList: tabListVo.tabList ?? [],
onChange: (index, enable) {
tabListVo?.tabList[index].open = enable;
tabListVo.notifyListener(true);
}, onNodeChange: (int index, TabVo tabVo) {
tabVo.open = false;
if (tabVo.selected != null && tabVo.selected['is_default'] == '1') {
tabVo.selected = null;
}
tabListVo.notifyListener(true);
_priceAction.qryProductListEvent(keywords: keywords);
});
});
}
@override
void onLoadComplete() {
_refreshController.loadComplete();
_refreshController.refreshCompleted();
}
@override
void onloadFailed({String msg}) {
_refreshController.loadFailed();
_refreshController.refreshCompleted(resetFooterState: true);
}
}
import 'package:flutter/foundation.dart';
///
/// 路由路径枚举管理
///
enum RoutePath {
PRICE_PAGE, //好价主页面
PRICE_SEARCH_PAGE, //好价搜索页面
PRICE_DETAIL_PAGE, //好价详情页面
PRICE_DISCOUNT_PAGE, //好价更多优惠页面
}
///
/// 路由管理的扩展
///
extension RoutePathExt on RoutePath {
String get name => describeEnum(this);
String path() {
return name.replaceAll("_", "/");
}
}
import 'package:flutter/cupertino.dart';
import 'package:flutter_boost/flutter_boost.dart';
///
/// Boost导航路由管理器
///
class NavigateUtils {
///
/// 开启新页面统一API
/// [path] 待打开页面的路径
/// [isNative] 是否需伴随原生容器弹出,默认false
/// [needLogin] 是否需要登录
/// [arguments] 携带到下一页面的参数
///
///
static Future<Map<dynamic, dynamic>> push({
@required String path,
bool isNative = false,
bool needLogin = true,
Map<String, dynamic> arguments = const {},
}) async {
if (arguments == null) {
arguments = {};
}
// 记录上一个页面的名称
arguments['previousRouteName'] = _getPreviousRouteName();
dynamic result = await BoostNavigator.instance.push(
path,
withContainer: isNative,
arguments: arguments,
);
return result;
}
///
/// 关闭当前页面
/// [arguments] 关闭页面时携带的参数
///
static Future<bool> pop({Map<String, dynamic> arguments = const {}}) async {
dynamic result = BoostNavigator.instance.pop(arguments);
return result;
}
///
/// 回到主页
/// [rootPath] 根路由路径
///
static popRoot({
@required String rootPath,
}) async {
await BoostNavigator.instance.pop().then((value) async {
PageInfo pageInfo = BoostNavigator.instance.getTopPageInfo();
String pageName = pageInfo?.pageName ?? '';
if (pageName.isNotEmpty && pageName != rootPath) {
await popRoot(rootPath: rootPath);
}
}).whenComplete(() async {});
}
// 获取下一个新页面的上一个页面名称(页面路径)
static String _getPreviousRouteName() {
PageInfo pageInfo = BoostNavigator.instance.getTopPageInfo();
var boostPath = pageInfo?.pageName;
return boostPath;
}
}
export 'package:common_module/utils/event_bus_utils.dart';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
class TextSize {
///
/// 计算文字大小
/// [context] 上下文对象
/// [value] 文字
/// [fontSize] 字体大小
/// [fontWeight] 字重
/// [maxWidth] 最大宽度
/// [maxLines] 最大行
///
static Size calculateTextSize(
BuildContext context, {
String value,
double fontSize,
FontWeight fontWeight,
double maxWidth,
int maxLines,
}) {
TextPainter painter = TextPainter(
///AUTO:华为手机如果不指定locale的时候,该方法算出来的文字高度是比系统计算偏小的。
locale: Localizations.localeOf(context),
maxLines: maxLines,
textDirection: TextDirection.ltr,
text: TextSpan(
text: value,
style: TextStyle(
fontWeight: fontWeight,
fontSize: fontSize,
)));
painter.layout(maxWidth: maxWidth);
///文字的宽度:painter.width
return Size(painter.width, painter.height);
}
}
class TimeUtils {
static String formatTimeLeft2({int timestamp=0}){
try {
if(timestamp<60){
return '00分${'$timestamp'.padLeft(2,'0')}秒';
}else if(timestamp < 60 * 60){
String minute = '${(timestamp ~/ 60)}';
String seconds = '${timestamp % 60}';
return '${minute.padLeft(2, '0')}${seconds.padLeft(2, '0')}秒';
}
} catch (e) {
}
return '00分00秒';
}
static String formatTimeLeft1({int timestamp=0}){
try {
if(timestamp<60){
return '00:${'$timestamp'.padLeft(2,'0')}';
}else if(timestamp < 60 * 60){
String minute = '${(timestamp ~/ 60)}';
String seconds = '${timestamp % 60}';
return '${minute.padLeft(2, '0')}:${seconds.padLeft(2, '0')}';
}
} catch (e) {
}
return '00:00';
}
static String formatTimeLeft({String timestamp='0'}){
try {
int time = int.parse(timestamp);
if(time<60){
return '00分${timestamp.padLeft(2, '0')}秒';
}else if(time < 60 * 60 * 60){
String minute = '${(time ~/ 60)}';
String seconds = '${time % 60}';
return '${minute.padLeft(2, '0')}${seconds.padLeft(2, '0')}秒';
}
} catch (e) {
}
return '0';
}
static String formatTime({String timestamp}) {
int time = int.parse(timestamp);
DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(time * 1000);
DateTime endDate = new DateTime.now();
Duration duration = endDate.difference(dateTime);
String releaseMsg = '';
if (duration.inSeconds < 60) {
releaseMsg = '${duration.inSeconds}秒前';
} else if (duration.inMinutes < 60) {
releaseMsg = '${duration.inMinutes}分钟前';
} else if (duration.inHours <= 4) {
releaseMsg = '${duration.inHours}小时前';
} else {
String hour = '${dateTime.hour}'.padLeft(2, '0');
String minute = '${dateTime.minute}'.padLeft(2, '0');
if (endDate.day == dateTime.day) {
//当天
releaseMsg = '$hour:$minute';
} else if (endDate.day - dateTime.day == 1 ||
(duration.inDays <= 1 && duration.inHours < 48)) {
releaseMsg = '昨日 $hour:$minute';
} else if (endDate.day - dateTime.day == 2 ||
(duration.inDays <= 2 && duration.inHours < 72)) {
releaseMsg = '前天 $hour:$minute';
} else if (duration.inDays > 2 && endDate.year == dateTime.year) {
String month = '${dateTime.month}'.padLeft(2, '0');
String day = '${dateTime.day}'.padLeft(2, '0');
releaseMsg = '$month-$day $hour:$minute';
} else {
String year = '${dateTime.year}'.padLeft(4, '0');
String month = '${dateTime.month}'.padLeft(2, '0');
String day = '${dateTime.day}'.padLeft(2, '0');
releaseMsg = '$year-$month-$day $hour:$minute';
}
}
return releaseMsg;
}
}
import 'package:flutter/material.dart';
class ImageAnimationWidget extends StatelessWidget {
final ValueNotifier<int> valueNotifier;
final List<String> images;
final String package;
final double width;
final double height;
const ImageAnimationWidget({ Key key,@required this.valueNotifier,@required this.images,this.package,this.width,this.height}) : super(key: key);
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<int>(
valueListenable: valueNotifier,
builder: (BuildContext context, int value, Widget child) {
String image;
if(value>=0 && value < images.length){
image = images[value];
}else{
image = images[0];
}
return Image.asset(image,package:package,gaplessPlayback: true,width:width,height:height,);
}
);
}
}
\ No newline at end of file
import 'package:flutter/material.dart';
Widget buildAppSearchBar({
double height = 48,
Decoration decoration,
EdgeInsetsGeometry margin,
EdgeInsetsGeometry padding,
Widget left,
Widget child,
Widget right,
}) {
return PreferredSize(
child: SafeArea(
child: Container(
padding:padding,
height: height,
decoration:decoration,
margin: margin,
child: Row(
crossAxisAlignment:CrossAxisAlignment.center,
children: [
if (left != null) left,
Expanded(child: child == null ? Container() : child, flex: 1),
if (right != null) right,
],
),
),
),
preferredSize: Size.fromHeight(height),
);
}
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:price_module/eventbus/notify_event_type.dart';
import 'package:price_module/utils/event_bus_utils.dart';
import 'package:common_module/utils/xapp_utils.dart';
class LoadingPage extends StatelessWidget {
final String msg;
final int length;
final EdgeInsetsGeometry padding;
const LoadingPage({Key key, this.padding, this.msg = '1', this.length = 6})
: super(key: key);
@override
Widget build(BuildContext context) {
return ListView.builder(
padding: padding,
itemCount: length,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return _buildItem(index);
},
);
}
Widget _buildItem(int index) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(8.w))),
height: 120.w,
margin: index > 0 ? EdgeInsets.only(top: 8.w) : null,
child: Row(
children: [
Container(
decoration: BoxDecoration(
color: Color(0xFFE9E9E9),
borderRadius:
BorderRadius.horizontal(left: Radius.circular(8.w))),
height: 120.w,
width: 120.w,
),
Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius.horizontal(right: Radius.circular(8.w))),
padding: EdgeInsets.only(top: 8.w, left: 12.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
constraints: BoxConstraints(maxWidth: 205.w),
height: 14.w,
color: Color(0xFFF5F5F5),
),
SizedBox(
height: 4.w,
),
Container(
constraints: BoxConstraints(maxWidth: 124.w),
height: 14.w,
color: Color(0xFFF5F5F5),
),
SizedBox(
height: 13.w,
),
Container(
constraints: BoxConstraints(maxWidth: 40.w),
height: 14.w,
color: Color(0xFFF5F5F5),
),
SizedBox(
height: 8.w,
),
Container(
constraints: BoxConstraints(maxWidth: 72.w),
height: 14.w,
color: Color(0xFFF5F5F5),
),
SizedBox(
height: 8.w,
),
Container(
height: 14.w,
child: Row(
children: [
Container(
width: 43.w,
height: 14.w,
color: Color(0xFFF5F5F5),
),
Container(
constraints: BoxConstraints(
maxWidth: 119.w,
),
),
Container(
width: 43.w,
height: 14.w,
color: Color(0xFFF5F5F5),
),
],
),
)
],
),
),
],
),
);
}
}
///
/// 小熊加载失败界面
/// [margin] 边距
/// [errorMsg] 错误信息
/// [onContactTap] 联系我们按钮
/// [onReloadTap] 重新加载按钮
///
class FailedLoadPage extends StatelessWidget {
final EdgeInsetsGeometry margin;
final String errorMsg;
final void Function() onContactTap;
final void Function() onReloadTap;
const FailedLoadPage(
{Key key,
this.margin,
this.errorMsg = '加载失败,请刷新重试',
this.onContactTap,
this.onReloadTap})
: super(key: key);
@override
Widget build(BuildContext context) {
return Container(
margin: margin,
alignment: Alignment.topCenter,
child: Column(
children: [
Container(
width: 261.w,
height: 165.w,
child: Image.asset('assets/images/img_ empty_data.png',
package: 'price_module'),
),
SizedBox(
height: 16.w,
),
Text(
errorMsg,
style: TextStyle(fontSize: 14.w, color: Color(0xFF999999)),
),
SizedBox(
height: 24.w,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildButtonWidget(
text: '联系客服',
style: TextStyle(
fontSize: 16.w,
color: Color(0xFF666666),
fontWeight: FontWeight.w600),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(8.w)),
border:
Border.all(width: 0.5.w, color: Color(0xFF999999))),
onTap: onContactTap ??
() {
///首页中有监听,发送到监听客服
eventBus.fire(NotifyEventType(
eventType: NotifyEventEnum.MeiQiaEnum));
}),
SizedBox(
width: 12.w,
),
_buildButtonWidget(
text: '重新加载',
style: TextStyle(
fontSize: 16.w,
color: Color(0xFF302600),
fontWeight: FontWeight.w600),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(8.w)),
color: Color(0xFFFFCA00)),
onTap: onReloadTap),
],
)
],
),
);
}
Widget _buildButtonWidget(
{@required String text,
@required TextStyle style,
Decoration decoration,
void Function() onTap}) {
return InkWell(
child: Container(
width: 164.w,
height: 40.w,
child: Text(text, style: style),
alignment: Alignment.center,
decoration: decoration,
),
onTap: onTap,
);
}
}
import 'package:flutter/material.dart';
class ImageWidget extends StatelessWidget {
final String name;
final double width;
final double height;
final String package;
final BoxFit fit;
const ImageWidget({ Key key,@required this.name,this.width,this.height,this.package = 'price_module',this.fit}) : super(key: key);
@override
Widget build(BuildContext context) {
return Image.asset(name,package:package,width:width,fit:fit,height:height,);
}
}
import 'package:flutter/material.dart';
///
/// [image] 商品图片
/// [title] 商品标题
/// [price] 商品价格
/// [platform] 商品平台 如:京东:17:30
///
class ItemProduct extends StatelessWidget {
final Widget image;
final double height;
final Decoration decoration;
final EdgeInsetsGeometry margin;
final EdgeInsetsGeometry rightMargin;
final Widget title;
final Widget price;
final Widget platform;
final void Function() onTap;
const ItemProduct({
Key key,
@required this.image,
this.height,
this.decoration,
this.margin,
this.rightMargin,
@required this.title,
this.platform,
this.price,
this.onTap
}) : super(key: key);
@override
Widget build(BuildContext context) {
return InkWell(child:Container(
decoration:decoration,
height: height,
margin: margin,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
image,
Expanded(child:Container(margin:rightMargin,child:Column(crossAxisAlignment: CrossAxisAlignment.start,children: [
title,
Expanded(child:price,flex:1,),
platform
],)))
],
),
),onTap: onTap,);
}
}
import 'package:flutter/material.dart';
import 'package:price_module/widget/image/image_widget.dart';
import 'package:common_module/utils/xapp_utils.dart';
//
Future<T> showPopBottomSheet<T>({BuildContext context,String pageName,Map<String,dynamic> extras,@required List<Map<String, dynamic>> items}){
return showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(20))),
child: SafeArea(
child: ListView(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
children: [
Container(
height: 114,
decoration: BoxDecoration(
color: Colors.white,
borderRadius:
BorderRadius.vertical(top: Radius.circular(20))),
child: Row(
children: List.generate(
items.length,
(index) => _buildShareWidget(
context, '${items[index]['key']}',
name: '${items[index]['name']}',
title: '${items[index]['title']}',
extras: extras)),
)),
InkWell(
child: Container(
height: 49.w,
decoration: BoxDecoration(
color: Colors.white,
border: Border(
top: BorderSide(
color: Color(0xFFE9E9E9), width: 0.6))),
alignment: Alignment.center,
child: Text(
'取消',
style:
TextStyle(color: Color(0xFF666666), fontSize: 15),
),
),
onTap: () {
Navigator.maybePop(context);
},
)
])));
});
}
Widget _buildShareWidget(BuildContext context, String key,
{@required String name,
@required String title,
Map<String, dynamic> extras = const {}}) {
return Expanded(
child:InkWell(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ImageWidget(name: name,width: 48),
Container(margin: EdgeInsets.only(top: 8),child: Text(title,style: TextStyle(color: Color(0xFF333333), fontSize: 12),))
],
),
onTap: () {
Navigator.maybePop(context, key);
}
)
);
}
\ No newline at end of file
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:xiaoxiong_repository/entity/product_item_entity.dart';
import 'package:common_module/utils/xapp_utils.dart';
class PriceGridWidget extends StatelessWidget {
final EdgeInsetsGeometry padding;
final bool shrinkWrap;
final ScrollPhysics physics;
final void Function(int) onItemTap;
final List<ProductItemEntity> list;
final bool isHaoJia;
const PriceGridWidget(
{Key key,
this.padding = const EdgeInsets.all(0),
this.shrinkWrap = false,
this.physics,
this.onItemTap,
this.list,
this.isHaoJia = false})
: super(key: key);
@override
Widget build(BuildContext context) {
return StaggeredGridView.countBuilder(
shrinkWrap: shrinkWrap,
physics: physics,
padding: padding,
crossAxisCount: 4,
staggeredTileBuilder: (index) => StaggeredTile.fit(2),
mainAxisSpacing: 8.w,
crossAxisSpacing: 9.w,
itemCount: list.length,
itemBuilder: itemGridBuilder,
);
}
Widget itemGridBuilder(BuildContext context, int index) {
ProductItemEntity vo = list[index];
return InkWell(
onTap: () => onItemTap(index),
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 170.w,
height: 170.w,
decoration: BoxDecoration(
color: Colors.white,
borderRadius:
BorderRadius.vertical(top: Radius.circular(8.w))),
child: Stack(
alignment: Alignment.bottomLeft,
children: [
ClipRRect(
child: CachedNetworkImage(
imageUrl: vo.itemPic,
placeholder: (BuildContext c, String s) {
return Image.asset(
'assets/images/placeholder_image.png',
package: 'price_module',
);
},
),
borderRadius:
BorderRadius.vertical(top: Radius.circular(8.w)),
),
Container(
height: 16.w,
margin: EdgeInsets.only(left: 8.w, bottom: 6.w),
padding: EdgeInsets.symmetric(horizontal: 4.w),
decoration: BoxDecoration(
color: Color(0xCCFFFFFF),
border: Border.all(
width: 0.5.w, color: Color(0xFFE9E9E9)),
borderRadius: BorderRadius.all(Radius.circular(3.w))),
child: Text(
vo.platform,
style: TextStyle(
color: Color(0xFF666666),
fontSize: 10.w,
),
textAlign: TextAlign.center,
),
)
],
),
),
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius:
BorderRadius.vertical(bottom: Radius.circular(8.w))),
padding: EdgeInsets.all(8.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
vo.itemTitle,
maxLines: 2,
style: TextStyle(
fontWeight: FontWeight.w600, fontSize: 14.w),
overflow: TextOverflow.ellipsis,
),
vo.descriptionText.isEmpty
? Container()
: SizedBox(height: 4.w),
vo.descriptionText.isEmpty
? Container()
: Text(
vo.descriptionText,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 11.w, color: Color(0xFF999999)),
),
SizedBox(height: 4.w),
Row(
children: [
Container(
decoration: BoxDecoration(
border: Border.all(
width: 0.5.w, color: Color(0x80FF8000)),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(8.w),
topRight: Radius.circular(8.w),
bottomRight: Radius.circular(8.w))),
padding: EdgeInsets.symmetric(
vertical: 1.w, horizontal: 4.w),
child: Text(vo.rebate,
style: TextStyle(
color: Color(0xFFFF8000),
fontSize: 11.w,
height: 1.3)),
),
SizedBox(
width: 2.w,
),
Expanded(
child: Container(
alignment: Alignment.centerLeft,
child: ListView.builder(
itemCount:
vo.tags.length >= 1 ? 1 : vo.tags.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Container(
margin: EdgeInsets.only(left: 4.w),
decoration: BoxDecoration(
color: Color(0xFFF5F5F5),
borderRadius: BorderRadius.all(
Radius.circular(2.w))),
padding:
EdgeInsets.symmetric(horizontal: 2.w),
child: Text('${vo.tags[index]}',
style: TextStyle(
color: Color(0xFFB0B0B0),
fontSize: 11.w,
fontWeight: FontWeight.w400)),
);
},
),
height: 16.w,
),
),
],
),
SizedBox(height: 4.w),
Text(
vo.special,
style: TextStyle(
color: Color(0xFFFF0400),
fontSize: 13.w,
fontWeight: FontWeight.w400),
),
],
),
)
],
),
));
}
}
class PriceListWidget extends StatelessWidget {
final EdgeInsetsGeometry padding;
final bool shrinkWrap;
final ScrollPhysics physics;
final void Function(ProductItemEntity, int) onItemTap;
final bool scroll;
final List<ProductItemEntity> list;
final bool isHaoJia;
const PriceListWidget(
{Key key,
this.padding = const EdgeInsets.all(0),
this.scroll = false,
this.shrinkWrap = false,
this.isHaoJia = false,
this.physics,
this.onItemTap,
this.list})
: super(key: key);
@override
Widget build(BuildContext context) {
return ListView.builder(
shrinkWrap: shrinkWrap,
physics: physics,
padding: padding,
itemCount: list.length,
itemBuilder: (BuildContext context, int index) {
ProductItemEntity itemVo = list[index];
return _buildPriceItem(
index: index,
scroll: scroll,
itemEntity: itemVo,
onTap: () {
if (onItemTap != null) {
onItemTap(itemVo, index);
}
},
);
},
);
}
Widget _buildPriceItem(
{int index,
bool scroll,
ProductItemEntity itemEntity,
void Function() onTap}) {
return InkWell(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(8.w))),
height: 120.w,
margin: EdgeInsets.only(
top: (index > 0)
? 8.w
: isHaoJia
? 0
: 8.w),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildLeftImage(
imageUrl: itemEntity.itemPic,
source: itemEntity.platform ?? '未知',
scroll: scroll,
),
_buildItemDetails(vo: itemEntity),
],
),
),
onTap: onTap,
);
}
///
/// 好价商品条目的标题等信息
///
Widget _buildItemDetails({ProductItemEntity vo}) {
String descriptionText = vo.descriptionText;
if (descriptionText != null && descriptionText.isNotEmpty) {
descriptionText = descriptionText.replaceAll('\n', '');
descriptionText = descriptionText.replaceAll('\r', '');
}
return Expanded(
child: Container(
margin: EdgeInsets.only(top: 8.w, bottom: 10.w, right: 8.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${vo.itemTitle}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14.w,
color: Color(0xFF333333),
height: 1.5,
fontWeight: FontWeight.w600),
),
SizedBox(
height: 4,
),
Text(
'$descriptionText',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 11.w, color: Color(0xFF999999)),
),
Spacer(),
Text(
vo.special ?? '',
style: TextStyle(
fontSize: 13.w,
color: Color(0xFFFF0400),
fontWeight: FontWeight.w400),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
SizedBox(
height: 9.w,
),
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
decoration: BoxDecoration(
border:
Border.all(width: 0.5.w, color: Color(0x80FF8000)),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(8.w),
topRight: Radius.circular(8.w),
bottomRight: Radius.circular(8.w))),
padding: EdgeInsets.symmetric(vertical: 1.w, horizontal: 4.w),
child: Text('${vo.rebate}',
style: TextStyle(
color: Color(0xFFFF8000),
fontSize: 11.w,
fontWeight: FontWeight.w500,
height: 1.3,
)),
),
SizedBox(
width: 2.w,
),
Expanded(
child: Container(
alignment: Alignment.centerLeft,
child: ListView.builder(
itemCount: vo.tags.length >= 1 ? 1 : vo.tags.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Container(
margin: EdgeInsets.only(left: 4.w),
decoration: BoxDecoration(
color: Color(0xFFF5F5F5),
borderRadius:
BorderRadius.all(Radius.circular(2.w))),
padding: EdgeInsets.symmetric(horizontal: 4.w),
alignment: Alignment.center,
child: Text('${vo.tags[index]}',
style: TextStyle(
color: Color(0xFF666666),
fontSize: 11.w,
fontWeight: FontWeight.w400)),
);
},
),
height: 16.w,
),
),
Text(
'${vo.releaseTime}',
style: TextStyle(
color: Color(0xFFB0B0B0),
fontSize: 11.w,
fontWeight: FontWeight.w400),
),
],
),
)
],
),
));
}
///
/// 商品图片
/// [imageUrl] 商品图片
/// [source] 商品来源,如淘宝、京东、拼多多等
/// [scroll] 是否滚动
///
Widget _buildLeftImage(
{@required String imageUrl, @required String source, bool scroll}) {
return Container(
width: 104.w,
height: 104.w,
margin: EdgeInsets.all(8.w),
child: Stack(
alignment: Alignment.topRight,
children: [
scroll
? Image.asset('assets/images/placeholder_image.png',
package: 'price_module', width: 104.w, height: 104.w)
: ClipRRect(
child: CachedNetworkImage(
imageUrl: imageUrl,
placeholder: (BuildContext c, String s) {
return Image.asset(
'assets/images/placeholder_image.png',
package: 'price_module',
);
},
width: 104.w,
height: 104.w,
filterQuality: FilterQuality.low,
),
borderRadius: BorderRadius.all(Radius.circular(4.w))),
Container(
decoration: BoxDecoration(
color: Color(0xCCFFFFFF),
border: Border.all(width: 0.5.w, color: Color(0xFFE9E9E9)),
borderRadius: BorderRadius.all(Radius.circular(4.w))),
margin: EdgeInsets.only(top: 4.w, right: 4.w),
padding: EdgeInsets.symmetric(horizontal: 3.w),
child: Text(source,
style: TextStyle(color: Color(0xFF666666), fontSize: 10.w)),
)
],
),
);
}
}
import 'package:flutter/material.dart';
class ProgresPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return oldDelegate != this;
}
}
\ No newline at end of file
import 'package:flutter/cupertino.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
class PullWidget extends StatelessWidget {
final Widget child;
final VoidCallback onRefresh;
final VoidCallback onLoad;
final RefreshController controller;
final ScrollController scrollController;
PullWidget(
{this.child,
this.onRefresh,
this.onLoad,
this.controller,
this.scrollController}) {
assert(this.child != null);
}
@override
Widget build(BuildContext context) {
return SmartRefresher(
scrollController: scrollController,
controller: controller,
enablePullDown: onRefresh != null,
enablePullUp: onLoad != null,
onRefresh: onRefresh,
onLoading: onLoad,
child: child,
);
}
}
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:common_module/utils/xapp_utils.dart';
Widget buildTabbarButton({@required String title,@required String tabBarType,TabbarController controller}){
return Container(child:Column(mainAxisAlignment:MainAxisAlignment.center,children: [
Container(
width:24.w,
height:24.w,
child:TabbarWidget(
controller:(controller)=>controller,
tabBarType:tabBarType,
)
),
Text(title,style:TextStyle(color:Color(0xFF666666),fontSize:10.w),)
]));
}
class TabbarWidget extends StatefulWidget {
final Function(TabbarController) controller;
final String tabBarType;
const TabbarWidget({ Key key,@required this.controller,@required this.tabBarType}) : super(key: key);
@override
_TabbarWidgetState createState() => _TabbarWidgetState();
}
class _TabbarWidgetState extends State<TabbarWidget> with TabbarController,SingleTickerProviderStateMixin{
Animation<double> _animation;
AnimationController _controller;
int interval = 200;
List<Image> images=[];
ValueNotifier<int> index = ValueNotifier(0);
@override
void initState() {
images.clear();
for (var i = 0; i < 10; i++) {
images.add(Image.asset('assets/tabbar/${widget.tabBarType}/$i.png',gaplessPlayback: true));
}
widget.controller(this);
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 1000),
reverseDuration: Duration(milliseconds: 1000),
upperBound: images.length.toDouble() - 1,
lowerBound: 0,
);
_controller.addListener(() {
index.value = _controller.value.toInt();
});
_animation=Tween<double>(begin: 0,end: images.length.toDouble()).animate(_controller);
_animation.addStatusListener((status) {
//if (status==AnimationStatus.completed) {_controller.forward(from: 0);} //循环执行动画
});
super.initState();
}
@override
void testMethod(int index) {
if(_controller.value.toInt() == 9){
_controller.reverse(from:9);
}else{
_controller.forward(from:0);
}
print("========>index:$index ${images.length} value:${_controller.value.toInt()}");
}
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<int>(
valueListenable: index,
builder: (BuildContext context, int value, Widget child) {
return images[value];
}
);
}
}
abstract class TabbarController {
void testMethod(int index);
}
\ No newline at end of file
library price_module;
\ No newline at end of file
name: price_module
description: 小熊比价模块.
version: 1.0.0
homepage: /
publish_to: none
environment:
sdk: ">=2.8.0 <3.0.0"
flutter: ">=1.17.0 <2.0.0"
dependencies:
flutter:
sdk: flutter
flutter_datetime_picker: ^1.5.1
flutter_picker: ^2.0.2
flutter_html: ^2.1.5
common_module:
git:
url: 'git@git.xiaomanxiong.com:flutter-plugin/common_module.git'
ref: 'null-safety'
xiaoxiong_repository:
git:
url: 'git@git.xiaomanxiong.com:flutter-plugin/app-xiaoxiong-repository.git'
ref: 'null-safety'
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
assets:
- assets/images/
- assets/images/collect/
- assets/images/like/
\ No newline at end of file
import 'package:flutter_test/flutter_test.dart';
void main() {
test('adds one to input values', () {
});
}
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